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