|
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.concurrent.TimeUnit; |
|
31 |
|
32 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; |
|
33 /* LogFactory removed by HttpClient for Android script. */ |
|
34 import ch.boye.httpclientandroidlib.annotation.ThreadSafe; |
|
35 import ch.boye.httpclientandroidlib.conn.params.ConnPerRouteBean; |
|
36 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; |
|
37 import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; |
|
38 import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; |
|
39 import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; |
|
40 import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; |
|
41 import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; |
|
42 import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; |
|
43 import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; |
|
44 import ch.boye.httpclientandroidlib.params.HttpParams; |
|
45 import ch.boye.httpclientandroidlib.impl.conn.DefaultClientConnectionOperator; |
|
46 import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory; |
|
47 |
|
48 /** |
|
49 * Manages a pool of {@link OperatedClientConnection client connections} and |
|
50 * is able to service connection requests from multiple execution threads. |
|
51 * Connections are pooled on a per route basis. A request for a route which |
|
52 * already the manager has persistent connections for available in the pool |
|
53 * will be services by leasing a connection from the pool rather than |
|
54 * creating a brand new connection. |
|
55 * <p> |
|
56 * ThreadSafeClientConnManager maintains a maximum limit of connection on |
|
57 * a per route basis and in total. Per default this implementation will |
|
58 * create no more than than 2 concurrent connections per given route |
|
59 * and no more 20 connections in total. For many real-world applications |
|
60 * these limits may prove too constraining, especially if they use HTTP |
|
61 * as a transport protocol for their services. Connection limits, however, |
|
62 * can be adjusted using HTTP parameters. |
|
63 * |
|
64 * @since 4.0 |
|
65 */ |
|
66 @ThreadSafe |
|
67 public class ThreadSafeClientConnManager implements ClientConnectionManager { |
|
68 |
|
69 public HttpClientAndroidLog log; |
|
70 |
|
71 /** The schemes supported by this connection manager. */ |
|
72 protected final SchemeRegistry schemeRegistry; // @ThreadSafe |
|
73 |
|
74 @Deprecated |
|
75 protected final AbstractConnPool connectionPool; |
|
76 |
|
77 /** The pool of connections being managed. */ |
|
78 protected final ConnPoolByRoute pool; |
|
79 |
|
80 /** The operator for opening and updating connections. */ |
|
81 protected final ClientConnectionOperator connOperator; // DefaultClientConnectionOperator is @ThreadSafe |
|
82 |
|
83 protected final ConnPerRouteBean connPerRoute; |
|
84 |
|
85 /** |
|
86 * Creates a new thread safe connection manager. |
|
87 * |
|
88 * @param schreg the scheme registry. |
|
89 */ |
|
90 public ThreadSafeClientConnManager(final SchemeRegistry schreg) { |
|
91 this(schreg, -1, TimeUnit.MILLISECONDS); |
|
92 } |
|
93 |
|
94 /** |
|
95 * @since 4.1 |
|
96 */ |
|
97 public ThreadSafeClientConnManager() { |
|
98 this(SchemeRegistryFactory.createDefault()); |
|
99 } |
|
100 |
|
101 /** |
|
102 * Creates a new thread safe connection manager. |
|
103 * |
|
104 * @param schreg the scheme registry. |
|
105 * @param connTTL max connection lifetime, <=0 implies "infinity" |
|
106 * @param connTTLTimeUnit TimeUnit of connTTL |
|
107 * |
|
108 * @since 4.1 |
|
109 */ |
|
110 public ThreadSafeClientConnManager(final SchemeRegistry schreg, |
|
111 long connTTL, TimeUnit connTTLTimeUnit) { |
|
112 super(); |
|
113 if (schreg == null) { |
|
114 throw new IllegalArgumentException("Scheme registry may not be null"); |
|
115 } |
|
116 this.log = new HttpClientAndroidLog(getClass()); |
|
117 this.schemeRegistry = schreg; |
|
118 this.connPerRoute = new ConnPerRouteBean(); |
|
119 this.connOperator = createConnectionOperator(schreg); |
|
120 this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ; |
|
121 this.connectionPool = this.pool; |
|
122 } |
|
123 |
|
124 /** |
|
125 * Creates a new thread safe connection manager. |
|
126 * |
|
127 * @param params the parameters for this manager. |
|
128 * @param schreg the scheme registry. |
|
129 * |
|
130 * @deprecated use {@link ThreadSafeClientConnManager#ThreadSafeClientConnManager(SchemeRegistry)} |
|
131 */ |
|
132 @Deprecated |
|
133 public ThreadSafeClientConnManager(HttpParams params, |
|
134 SchemeRegistry schreg) { |
|
135 if (schreg == null) { |
|
136 throw new IllegalArgumentException("Scheme registry may not be null"); |
|
137 } |
|
138 this.log = new HttpClientAndroidLog(getClass()); |
|
139 this.schemeRegistry = schreg; |
|
140 this.connPerRoute = new ConnPerRouteBean(); |
|
141 this.connOperator = createConnectionOperator(schreg); |
|
142 this.pool = (ConnPoolByRoute) createConnectionPool(params) ; |
|
143 this.connectionPool = this.pool; |
|
144 } |
|
145 |
|
146 @Override |
|
147 protected void finalize() throws Throwable { |
|
148 try { |
|
149 shutdown(); |
|
150 } finally { |
|
151 super.finalize(); |
|
152 } |
|
153 } |
|
154 |
|
155 /** |
|
156 * Hook for creating the connection pool. |
|
157 * |
|
158 * @return the connection pool to use |
|
159 * |
|
160 * @deprecated use #createConnectionPool(long, TimeUnit)) |
|
161 */ |
|
162 @Deprecated |
|
163 protected AbstractConnPool createConnectionPool(final HttpParams params) { |
|
164 return new ConnPoolByRoute(connOperator, params); |
|
165 } |
|
166 |
|
167 /** |
|
168 * Hook for creating the connection pool. |
|
169 * |
|
170 * @return the connection pool to use |
|
171 * |
|
172 * @since 4.1 |
|
173 */ |
|
174 protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLTimeUnit) { |
|
175 return new ConnPoolByRoute(connOperator, connPerRoute, 20, connTTL, connTTLTimeUnit); |
|
176 } |
|
177 |
|
178 /** |
|
179 * Hook for creating the connection operator. |
|
180 * It is called by the constructor. |
|
181 * Derived classes can override this method to change the |
|
182 * instantiation of the operator. |
|
183 * The default implementation here instantiates |
|
184 * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. |
|
185 * |
|
186 * @param schreg the scheme registry. |
|
187 * |
|
188 * @return the connection operator to use |
|
189 */ |
|
190 protected ClientConnectionOperator |
|
191 createConnectionOperator(SchemeRegistry schreg) { |
|
192 |
|
193 return new DefaultClientConnectionOperator(schreg);// @ThreadSafe |
|
194 } |
|
195 |
|
196 public SchemeRegistry getSchemeRegistry() { |
|
197 return this.schemeRegistry; |
|
198 } |
|
199 |
|
200 public ClientConnectionRequest requestConnection( |
|
201 final HttpRoute route, |
|
202 final Object state) { |
|
203 |
|
204 final PoolEntryRequest poolRequest = pool.requestPoolEntry( |
|
205 route, state); |
|
206 |
|
207 return new ClientConnectionRequest() { |
|
208 |
|
209 public void abortRequest() { |
|
210 poolRequest.abortRequest(); |
|
211 } |
|
212 |
|
213 public ManagedClientConnection getConnection( |
|
214 long timeout, TimeUnit tunit) throws InterruptedException, |
|
215 ConnectionPoolTimeoutException { |
|
216 if (route == null) { |
|
217 throw new IllegalArgumentException("Route may not be null."); |
|
218 } |
|
219 |
|
220 if (log.isDebugEnabled()) { |
|
221 log.debug("Get connection: " + route + ", timeout = " + timeout); |
|
222 } |
|
223 |
|
224 BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit); |
|
225 return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry); |
|
226 } |
|
227 |
|
228 }; |
|
229 |
|
230 } |
|
231 |
|
232 public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) { |
|
233 |
|
234 if (!(conn instanceof BasicPooledConnAdapter)) { |
|
235 throw new IllegalArgumentException |
|
236 ("Connection class mismatch, " + |
|
237 "connection not obtained from this manager."); |
|
238 } |
|
239 BasicPooledConnAdapter hca = (BasicPooledConnAdapter) conn; |
|
240 if ((hca.getPoolEntry() != null) && (hca.getManager() != this)) { |
|
241 throw new IllegalArgumentException |
|
242 ("Connection not obtained from this manager."); |
|
243 } |
|
244 synchronized (hca) { |
|
245 BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry(); |
|
246 if (entry == null) { |
|
247 return; |
|
248 } |
|
249 try { |
|
250 // make sure that the response has been read completely |
|
251 if (hca.isOpen() && !hca.isMarkedReusable()) { |
|
252 // In MTHCM, there would be a call to |
|
253 // SimpleHttpConnectionManager.finishLastResponse(conn); |
|
254 // Consuming the response is handled outside in 4.0. |
|
255 |
|
256 // make sure this connection will not be re-used |
|
257 // Shut down rather than close, we might have gotten here |
|
258 // because of a shutdown trigger. |
|
259 // Shutdown of the adapter also clears the tracked route. |
|
260 hca.shutdown(); |
|
261 } |
|
262 } catch (IOException iox) { |
|
263 if (log.isDebugEnabled()) |
|
264 log.debug("Exception shutting down released connection.", |
|
265 iox); |
|
266 } finally { |
|
267 boolean reusable = hca.isMarkedReusable(); |
|
268 if (log.isDebugEnabled()) { |
|
269 if (reusable) { |
|
270 log.debug("Released connection is reusable."); |
|
271 } else { |
|
272 log.debug("Released connection is not reusable."); |
|
273 } |
|
274 } |
|
275 hca.detach(); |
|
276 pool.freeEntry(entry, reusable, validDuration, timeUnit); |
|
277 } |
|
278 } |
|
279 } |
|
280 |
|
281 public void shutdown() { |
|
282 log.debug("Shutting down"); |
|
283 pool.shutdown(); |
|
284 } |
|
285 |
|
286 /** |
|
287 * Gets the total number of pooled connections for the given route. |
|
288 * This is the total number of connections that have been created and |
|
289 * are still in use by this connection manager for the route. |
|
290 * This value will not exceed the maximum number of connections per host. |
|
291 * |
|
292 * @param route the route in question |
|
293 * |
|
294 * @return the total number of pooled connections for that route |
|
295 */ |
|
296 public int getConnectionsInPool(final HttpRoute route) { |
|
297 return pool.getConnectionsInPool(route); |
|
298 } |
|
299 |
|
300 /** |
|
301 * Gets the total number of pooled connections. This is the total number of |
|
302 * connections that have been created and are still in use by this connection |
|
303 * manager. This value will not exceed the maximum number of connections |
|
304 * in total. |
|
305 * |
|
306 * @return the total number of pooled connections |
|
307 */ |
|
308 public int getConnectionsInPool() { |
|
309 return pool.getConnectionsInPool(); |
|
310 } |
|
311 |
|
312 public void closeIdleConnections(long idleTimeout, TimeUnit tunit) { |
|
313 if (log.isDebugEnabled()) { |
|
314 log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); |
|
315 } |
|
316 pool.closeIdleConnections(idleTimeout, tunit); |
|
317 } |
|
318 |
|
319 public void closeExpiredConnections() { |
|
320 log.debug("Closing expired connections"); |
|
321 pool.closeExpiredConnections(); |
|
322 } |
|
323 |
|
324 /** |
|
325 * since 4.1 |
|
326 */ |
|
327 public int getMaxTotal() { |
|
328 return pool.getMaxTotalConnections(); |
|
329 } |
|
330 |
|
331 /** |
|
332 * since 4.1 |
|
333 */ |
|
334 public void setMaxTotal(int max) { |
|
335 pool.setMaxTotalConnections(max); |
|
336 } |
|
337 |
|
338 /** |
|
339 * @since 4.1 |
|
340 */ |
|
341 public int getDefaultMaxPerRoute() { |
|
342 return connPerRoute.getDefaultMaxPerRoute(); |
|
343 } |
|
344 |
|
345 /** |
|
346 * @since 4.1 |
|
347 */ |
|
348 public void setDefaultMaxPerRoute(int max) { |
|
349 connPerRoute.setDefaultMaxPerRoute(max); |
|
350 } |
|
351 |
|
352 /** |
|
353 * @since 4.1 |
|
354 */ |
|
355 public int getMaxForRoute(final HttpRoute route) { |
|
356 return connPerRoute.getMaxForRoute(route); |
|
357 } |
|
358 |
|
359 /** |
|
360 * @since 4.1 |
|
361 */ |
|
362 public void setMaxForRoute(final HttpRoute route, int max) { |
|
363 connPerRoute.setMaxForRoute(route, max); |
|
364 } |
|
365 |
|
366 } |
|
367 |