View Javadoc

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