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