|
1 /* |
|
2 * ==================================================================== |
|
3 * |
|
4 * Licensed to the Apache Software Foundation (ASF) under one or more |
|
5 * contributor license agreements. See the NOTICE file distributed with |
|
6 * this work for additional information regarding copyright ownership. |
|
7 * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
8 * (the "License"); you may not use this file except in compliance with |
|
9 * the License. You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, software |
|
14 * distributed under the License is distributed on an "AS IS" BASIS, |
|
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
16 * See the License for the specific language governing permissions and |
|
17 * limitations under the License. |
|
18 * ==================================================================== |
|
19 * |
|
20 * This software consists of voluntary contributions made by many |
|
21 * individuals on behalf of the Apache Software Foundation. For more |
|
22 * information on the Apache Software Foundation, please see |
|
23 * <http://www.apache.org/>. |
|
24 * |
|
25 */ |
|
26 |
|
27 package ch.boye.httpclientandroidlib.impl.conn.tsccm; |
|
28 |
|
29 import java.io.IOException; |
|
30 import java.util.ListIterator; |
|
31 import java.util.Queue; |
|
32 import java.util.LinkedList; |
|
33 |
|
34 import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; |
|
35 |
|
36 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; |
|
37 /* LogFactory removed by HttpClient for Android script. */ |
|
38 import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; |
|
39 import ch.boye.httpclientandroidlib.conn.params.ConnPerRoute; |
|
40 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; |
|
41 import ch.boye.httpclientandroidlib.util.LangUtils; |
|
42 |
|
43 |
|
44 /** |
|
45 * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}. |
|
46 * The methods in this class are unsynchronized. It is expected that the |
|
47 * containing pool takes care of synchronization. |
|
48 * |
|
49 * @since 4.0 |
|
50 */ |
|
51 @NotThreadSafe // e.g. numEntries, freeEntries, |
|
52 public class RouteSpecificPool { |
|
53 |
|
54 public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); |
|
55 |
|
56 /** The route this pool is for. */ |
|
57 protected final HttpRoute route; //Immutable |
|
58 |
|
59 @Deprecated |
|
60 protected final int maxEntries; |
|
61 |
|
62 /** Connections per route */ |
|
63 protected final ConnPerRoute connPerRoute; |
|
64 |
|
65 /** |
|
66 * The list of free entries. |
|
67 * This list is managed LIFO, to increase idle times and |
|
68 * allow for closing connections that are not really needed. |
|
69 */ |
|
70 protected final LinkedList<BasicPoolEntry> freeEntries; |
|
71 |
|
72 /** The list of threads waiting for this pool. */ |
|
73 protected final Queue<WaitingThread> waitingThreads; |
|
74 |
|
75 /** The number of created entries. */ |
|
76 protected int numEntries; |
|
77 |
|
78 |
|
79 /** |
|
80 * @deprecated use {@link RouteSpecificPool#RouteSpecificPool(HttpRoute, ConnPerRoute)} |
|
81 */ |
|
82 @Deprecated |
|
83 public RouteSpecificPool(HttpRoute route, int maxEntries) { |
|
84 this.route = route; |
|
85 this.maxEntries = maxEntries; |
|
86 this.connPerRoute = new ConnPerRoute() { |
|
87 public int getMaxForRoute(HttpRoute route) { |
|
88 return RouteSpecificPool.this.maxEntries; |
|
89 } |
|
90 }; |
|
91 this.freeEntries = new LinkedList<BasicPoolEntry>(); |
|
92 this.waitingThreads = new LinkedList<WaitingThread>(); |
|
93 this.numEntries = 0; |
|
94 } |
|
95 |
|
96 |
|
97 /** |
|
98 * Creates a new route-specific pool. |
|
99 * |
|
100 * @param route the route for which to pool |
|
101 * @param connPerRoute the connections per route configuration |
|
102 */ |
|
103 public RouteSpecificPool(HttpRoute route, ConnPerRoute connPerRoute) { |
|
104 this.route = route; |
|
105 this.connPerRoute = connPerRoute; |
|
106 this.maxEntries = connPerRoute.getMaxForRoute(route); |
|
107 this.freeEntries = new LinkedList<BasicPoolEntry>(); |
|
108 this.waitingThreads = new LinkedList<WaitingThread>(); |
|
109 this.numEntries = 0; |
|
110 } |
|
111 |
|
112 |
|
113 /** |
|
114 * Obtains the route for which this pool is specific. |
|
115 * |
|
116 * @return the route |
|
117 */ |
|
118 public final HttpRoute getRoute() { |
|
119 return route; |
|
120 } |
|
121 |
|
122 |
|
123 /** |
|
124 * Obtains the maximum number of entries allowed for this pool. |
|
125 * |
|
126 * @return the max entry number |
|
127 */ |
|
128 public final int getMaxEntries() { |
|
129 return maxEntries; |
|
130 } |
|
131 |
|
132 |
|
133 /** |
|
134 * Indicates whether this pool is unused. |
|
135 * A pool is unused if there is neither an entry nor a waiting thread. |
|
136 * All entries count, not only the free but also the allocated ones. |
|
137 * |
|
138 * @return <code>true</code> if this pool is unused, |
|
139 * <code>false</code> otherwise |
|
140 */ |
|
141 public boolean isUnused() { |
|
142 return (numEntries < 1) && waitingThreads.isEmpty(); |
|
143 } |
|
144 |
|
145 |
|
146 /** |
|
147 * Return remaining capacity of this pool |
|
148 * |
|
149 * @return capacity |
|
150 */ |
|
151 public int getCapacity() { |
|
152 return connPerRoute.getMaxForRoute(route) - numEntries; |
|
153 } |
|
154 |
|
155 |
|
156 /** |
|
157 * Obtains the number of entries. |
|
158 * This includes not only the free entries, but also those that |
|
159 * have been created and are currently issued to an application. |
|
160 * |
|
161 * @return the number of entries for the route of this pool |
|
162 */ |
|
163 public final int getEntryCount() { |
|
164 return numEntries; |
|
165 } |
|
166 |
|
167 |
|
168 /** |
|
169 * Obtains a free entry from this pool, if one is available. |
|
170 * |
|
171 * @return an available pool entry, or <code>null</code> if there is none |
|
172 */ |
|
173 public BasicPoolEntry allocEntry(final Object state) { |
|
174 if (!freeEntries.isEmpty()) { |
|
175 ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size()); |
|
176 while (it.hasPrevious()) { |
|
177 BasicPoolEntry entry = it.previous(); |
|
178 if (entry.getState() == null || LangUtils.equals(state, entry.getState())) { |
|
179 it.remove(); |
|
180 return entry; |
|
181 } |
|
182 } |
|
183 } |
|
184 if (getCapacity() == 0 && !freeEntries.isEmpty()) { |
|
185 BasicPoolEntry entry = freeEntries.remove(); |
|
186 entry.shutdownEntry(); |
|
187 OperatedClientConnection conn = entry.getConnection(); |
|
188 try { |
|
189 conn.close(); |
|
190 } catch (IOException ex) { |
|
191 log.debug("I/O error closing connection", ex); |
|
192 } |
|
193 return entry; |
|
194 } |
|
195 return null; |
|
196 } |
|
197 |
|
198 |
|
199 /** |
|
200 * Returns an allocated entry to this pool. |
|
201 * |
|
202 * @param entry the entry obtained from {@link #allocEntry allocEntry} |
|
203 * or presented to {@link #createdEntry createdEntry} |
|
204 */ |
|
205 public void freeEntry(BasicPoolEntry entry) { |
|
206 |
|
207 if (numEntries < 1) { |
|
208 throw new IllegalStateException |
|
209 ("No entry created for this pool. " + route); |
|
210 } |
|
211 if (numEntries <= freeEntries.size()) { |
|
212 throw new IllegalStateException |
|
213 ("No entry allocated from this pool. " + route); |
|
214 } |
|
215 freeEntries.add(entry); |
|
216 } |
|
217 |
|
218 |
|
219 /** |
|
220 * Indicates creation of an entry for this pool. |
|
221 * The entry will <i>not</i> be added to the list of free entries, |
|
222 * it is only recognized as belonging to this pool now. It can then |
|
223 * be passed to {@link #freeEntry freeEntry}. |
|
224 * |
|
225 * @param entry the entry that was created for this pool |
|
226 */ |
|
227 public void createdEntry(BasicPoolEntry entry) { |
|
228 |
|
229 if (!route.equals(entry.getPlannedRoute())) { |
|
230 throw new IllegalArgumentException |
|
231 ("Entry not planned for this pool." + |
|
232 "\npool: " + route + |
|
233 "\nplan: " + entry.getPlannedRoute()); |
|
234 } |
|
235 |
|
236 numEntries++; |
|
237 } |
|
238 |
|
239 |
|
240 /** |
|
241 * Deletes an entry from this pool. |
|
242 * Only entries that are currently free in this pool can be deleted. |
|
243 * Allocated entries can not be deleted. |
|
244 * |
|
245 * @param entry the entry to delete from this pool |
|
246 * |
|
247 * @return <code>true</code> if the entry was found and deleted, or |
|
248 * <code>false</code> if the entry was not found |
|
249 */ |
|
250 public boolean deleteEntry(BasicPoolEntry entry) { |
|
251 |
|
252 final boolean found = freeEntries.remove(entry); |
|
253 if (found) |
|
254 numEntries--; |
|
255 return found; |
|
256 } |
|
257 |
|
258 |
|
259 /** |
|
260 * Forgets about an entry from this pool. |
|
261 * This method is used to indicate that an entry |
|
262 * {@link #allocEntry allocated} |
|
263 * from this pool has been lost and will not be returned. |
|
264 */ |
|
265 public void dropEntry() { |
|
266 if (numEntries < 1) { |
|
267 throw new IllegalStateException |
|
268 ("There is no entry that could be dropped."); |
|
269 } |
|
270 numEntries--; |
|
271 } |
|
272 |
|
273 |
|
274 /** |
|
275 * Adds a waiting thread. |
|
276 * This pool makes no attempt to match waiting threads with pool entries. |
|
277 * It is the caller's responsibility to check that there is no entry |
|
278 * before adding a waiting thread. |
|
279 * |
|
280 * @param wt the waiting thread |
|
281 */ |
|
282 public void queueThread(WaitingThread wt) { |
|
283 if (wt == null) { |
|
284 throw new IllegalArgumentException |
|
285 ("Waiting thread must not be null."); |
|
286 } |
|
287 this.waitingThreads.add(wt); |
|
288 } |
|
289 |
|
290 |
|
291 /** |
|
292 * Checks whether there is a waiting thread in this pool. |
|
293 * |
|
294 * @return <code>true</code> if there is a waiting thread, |
|
295 * <code>false</code> otherwise |
|
296 */ |
|
297 public boolean hasThread() { |
|
298 return !this.waitingThreads.isEmpty(); |
|
299 } |
|
300 |
|
301 |
|
302 /** |
|
303 * Returns the next thread in the queue. |
|
304 * |
|
305 * @return a waiting thread, or <code>null</code> if there is none |
|
306 */ |
|
307 public WaitingThread nextThread() { |
|
308 return this.waitingThreads.peek(); |
|
309 } |
|
310 |
|
311 |
|
312 /** |
|
313 * Removes a waiting thread, if it is queued. |
|
314 * |
|
315 * @param wt the waiting thread |
|
316 */ |
|
317 public void removeThread(WaitingThread wt) { |
|
318 if (wt == null) |
|
319 return; |
|
320 |
|
321 this.waitingThreads.remove(wt); |
|
322 } |
|
323 |
|
324 |
|
325 } // class RouteSpecificPool |