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;
28  
29  import java.io.IOException;
30  import java.util.concurrent.ExecutionException;
31  import java.util.concurrent.Future;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.TimeoutException;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.http.annotation.ThreadSafe;
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.DnsResolver;
43  import org.apache.http.conn.ManagedClientConnection;
44  import org.apache.http.conn.routing.HttpRoute;
45  import org.apache.http.conn.scheme.SchemeRegistry;
46  import org.apache.http.pool.ConnPoolControl;
47  import org.apache.http.pool.PoolStats;
48  import org.apache.http.util.Args;
49  import org.apache.http.util.Asserts;
50  
51  /**
52   * Manages a pool of {@link org.apache.http.conn.OperatedClientConnection}
53   * and is able to service connection requests from multiple execution threads.
54   * Connections are pooled on a per route basis. A request for a route which
55   * already the manager has persistent connections for available in the pool
56   * will be services by leasing a connection from the pool rather than
57   * creating a brand new connection.
58   * <p>
59   * PoolingConnectionManager maintains a maximum limit of connection on
60   * a per route basis and in total. Per default this implementation will
61   * create no more than than 2 concurrent connections per given route
62   * and no more 20 connections in total. For many real-world applications
63   * these limits may prove too constraining, especially if they use HTTP
64   * as a transport protocol for their services. Connection limits, however,
65   * can be adjusted using HTTP parameters.
66   *
67   * @since 4.2
68   *
69   * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}.
70   */
71  @Deprecated
72  @ThreadSafe
73  public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl<HttpRoute> {
74  
75      private final Log log = LogFactory.getLog(getClass());
76  
77      private final SchemeRegistry schemeRegistry;
78  
79      private final HttpConnPool pool;
80  
81      private final ClientConnectionOperator operator;
82  
83      /** the custom-configured DNS lookup mechanism. */
84      private final DnsResolver dnsResolver;
85  
86      public PoolingClientConnectionManager(final SchemeRegistry schreg) {
87          this(schreg, -1, TimeUnit.MILLISECONDS);
88      }
89  
90      public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
91          this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
92      }
93  
94      public PoolingClientConnectionManager() {
95          this(SchemeRegistryFactory.createDefault());
96      }
97  
98      public PoolingClientConnectionManager(
99              final SchemeRegistry schemeRegistry,
100             final long timeToLive, final TimeUnit tunit) {
101         this(schemeRegistry, timeToLive, tunit, new SystemDefaultDnsResolver());
102     }
103 
104     public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
105                 final long timeToLive, final TimeUnit tunit,
106                 final DnsResolver dnsResolver) {
107         super();
108         Args.notNull(schemeRegistry, "Scheme registry");
109         Args.notNull(dnsResolver, "DNS resolver");
110         this.schemeRegistry = schemeRegistry;
111         this.dnsResolver  = dnsResolver;
112         this.operator = createConnectionOperator(schemeRegistry);
113         this.pool = new HttpConnPool(this.log, this.operator, 2, 20, timeToLive, tunit);
114     }
115 
116     @Override
117     protected void finalize() throws Throwable {
118         try {
119             shutdown();
120         } finally {
121             super.finalize();
122         }
123     }
124 
125     /**
126      * Hook for creating the connection operator.
127      * It is called by the constructor.
128      * Derived classes can override this method to change the
129      * instantiation of the operator.
130      * The default implementation here instantiates
131      * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
132      *
133      * @param schreg    the scheme registry.
134      *
135      * @return  the connection operator to use
136      */
137     protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
138             return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
139     }
140 
141     public SchemeRegistry getSchemeRegistry() {
142         return this.schemeRegistry;
143     }
144 
145     private String format(final HttpRoute route, final Object state) {
146         final StringBuilder buf = new StringBuilder();
147         buf.append("[route: ").append(route).append("]");
148         if (state != null) {
149             buf.append("[state: ").append(state).append("]");
150         }
151         return buf.toString();
152     }
153 
154     private String formatStats(final HttpRoute route) {
155         final StringBuilder buf = new StringBuilder();
156         final PoolStats totals = this.pool.getTotalStats();
157         final PoolStats stats = this.pool.getStats(route);
158         buf.append("[total kept alive: ").append(totals.getAvailable()).append("; ");
159         buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable());
160         buf.append(" of ").append(stats.getMax()).append("; ");
161         buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable());
162         buf.append(" of ").append(totals.getMax()).append("]");
163         return buf.toString();
164     }
165 
166     private String format(final HttpPoolEntry entry) {
167         final StringBuilder buf = new StringBuilder();
168         buf.append("[id: ").append(entry.getId()).append("]");
169         buf.append("[route: ").append(entry.getRoute()).append("]");
170         final Object state = entry.getState();
171         if (state != null) {
172             buf.append("[state: ").append(state).append("]");
173         }
174         return buf.toString();
175     }
176 
177     public ClientConnectionRequest requestConnection(
178             final HttpRoute route,
179             final Object state) {
180         Args.notNull(route, "HTTP route");
181         if (this.log.isDebugEnabled()) {
182             this.log.debug("Connection request: " + format(route, state) + formatStats(route));
183         }
184         final Future<HttpPoolEntry> future = this.pool.lease(route, state);
185 
186         return new ClientConnectionRequest() {
187 
188             public void abortRequest() {
189                 future.cancel(true);
190             }
191 
192             public ManagedClientConnection getConnection(
193                     final long timeout,
194                     final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
195                 return leaseConnection(future, timeout, tunit);
196             }
197 
198         };
199 
200     }
201 
202     ManagedClientConnection leaseConnection(
203             final Future<HttpPoolEntry> future,
204             final long timeout,
205             final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
206         final HttpPoolEntry entry;
207         try {
208             entry = future.get(timeout, tunit);
209             if (entry == null || future.isCancelled()) {
210                 throw new InterruptedException();
211             }
212             Asserts.check(entry.getConnection() != null, "Pool entry with no connection");
213             if (this.log.isDebugEnabled()) {
214                 this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
215             }
216             return new ManagedClientConnectionImpl(this, this.operator, entry);
217         } catch (final ExecutionException ex) {
218             Throwable cause = ex.getCause();
219             if (cause == null) {
220                 cause = ex;
221             }
222             this.log.error("Unexpected exception leasing connection from pool", cause);
223             // Should never happen
224             throw new InterruptedException();
225         } catch (final TimeoutException ex) {
226             throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool");
227         }
228     }
229 
230     public void releaseConnection(
231             final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) {
232 
233         Args.check(conn instanceof ManagedClientConnectionImpl, "Connection class mismatch, " +
234             "connection not obtained from this manager");
235         final ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
236         Asserts.check(managedConn.getManager() == this, "Connection not obtained from this manager");
237         synchronized (managedConn) {
238             final HttpPoolEntry entry = managedConn.detach();
239             if (entry == null) {
240                 return;
241             }
242             try {
243                 if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
244                     try {
245                         managedConn.shutdown();
246                     } catch (final IOException iox) {
247                         if (this.log.isDebugEnabled()) {
248                             this.log.debug("I/O exception shutting down released connection", iox);
249                         }
250                     }
251                 }
252                 // Only reusable connections can be kept alive
253                 if (managedConn.isMarkedReusable()) {
254                     entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
255                     if (this.log.isDebugEnabled()) {
256                         final String s;
257                         if (keepalive > 0) {
258                             s = "for " + keepalive + " " + tunit;
259                         } else {
260                             s = "indefinitely";
261                         }
262                         this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
263                     }
264                 }
265             } finally {
266                 this.pool.release(entry, managedConn.isMarkedReusable());
267             }
268             if (this.log.isDebugEnabled()) {
269                 this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
270             }
271         }
272     }
273 
274     public void shutdown() {
275         this.log.debug("Connection manager is shutting down");
276         try {
277             this.pool.shutdown();
278         } catch (final IOException ex) {
279             this.log.debug("I/O exception shutting down connection manager", ex);
280         }
281         this.log.debug("Connection manager shut down");
282     }
283 
284     public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) {
285         if (this.log.isDebugEnabled()) {
286             this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
287         }
288         this.pool.closeIdle(idleTimeout, tunit);
289     }
290 
291     public void closeExpiredConnections() {
292         this.log.debug("Closing expired connections");
293         this.pool.closeExpired();
294     }
295 
296     public int getMaxTotal() {
297         return this.pool.getMaxTotal();
298     }
299 
300     public void setMaxTotal(final int max) {
301         this.pool.setMaxTotal(max);
302     }
303 
304     public int getDefaultMaxPerRoute() {
305         return this.pool.getDefaultMaxPerRoute();
306     }
307 
308     public void setDefaultMaxPerRoute(final int max) {
309         this.pool.setDefaultMaxPerRoute(max);
310     }
311 
312     public int getMaxPerRoute(final HttpRoute route) {
313         return this.pool.getMaxPerRoute(route);
314     }
315 
316     public void setMaxPerRoute(final HttpRoute route, final int max) {
317         this.pool.setMaxPerRoute(route, max);
318     }
319 
320     public PoolStats getTotalStats() {
321         return this.pool.getTotalStats();
322     }
323 
324     public PoolStats getStats(final HttpRoute route) {
325         return this.pool.getStats(route);
326     }
327 
328 }
329