• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java $
3  * $Revision: 673450 $
4  * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
5  *
6  * ====================================================================
7  * Licensed to the Apache Software Foundation (ASF) under one
8  * or more contributor license agreements.  See the NOTICE file
9  * distributed with this work for additional information
10  * regarding copyright ownership.  The ASF licenses this file
11  * to you under the Apache License, Version 2.0 (the
12  * "License"); you may not use this file except in compliance
13  * with the License.  You may obtain a copy of the License at
14  *
15  *   http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing,
18  * software distributed under the License is distributed on an
19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20  * KIND, either express or implied.  See the License for the
21  * specific language governing permissions and limitations
22  * under the License.
23  * ====================================================================
24  *
25  * This software consists of voluntary contributions made by many
26  * individuals on behalf of the Apache Software Foundation.  For more
27  * information on the Apache Software Foundation, please see
28  * <http://www.apache.org/>.
29  *
30  */
31 
32 package org.apache.http.impl.conn;
33 
34 import dalvik.system.SocketTagger;
35 import java.io.IOException;
36 import java.net.Socket;
37 import java.util.concurrent.TimeUnit;
38 
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.http.conn.ClientConnectionManager;
42 import org.apache.http.conn.ClientConnectionOperator;
43 import org.apache.http.conn.ClientConnectionRequest;
44 import org.apache.http.conn.ManagedClientConnection;
45 import org.apache.http.conn.routing.HttpRoute;
46 import org.apache.http.conn.routing.RouteTracker;
47 import org.apache.http.conn.scheme.SchemeRegistry;
48 import org.apache.http.params.HttpParams;
49 
50 
51 /**
52  * A connection "manager" for a single connection.
53  * This manager is good only for single-threaded use.
54  * Allocation <i>always</i> returns the connection immediately,
55  * even if it has not been released after the previous allocation.
56  * In that case, a {@link #MISUSE_MESSAGE warning} is logged
57  * and the previously issued connection is revoked.
58  * <p>
59  * This class is derived from <code>SimpleHttpConnectionManager</code>
60  * in HttpClient 3. See there for original authors.
61  * </p>
62  *
63  * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
64  * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
65  *
66  *
67  * <!-- empty lines to avoid svn diff problems -->
68  * @version   $Revision: 673450 $
69  *
70  * @since 4.0
71  */
72 public class SingleClientConnManager implements ClientConnectionManager {
73 
74     private final Log log = LogFactory.getLog(getClass());
75 
76     /** The message to be logged on multiple allocation. */
77     public final static String MISUSE_MESSAGE =
78     "Invalid use of SingleClientConnManager: connection still allocated.\n" +
79     "Make sure to release the connection before allocating another one.";
80 
81 
82     /** The schemes supported by this connection manager. */
83     protected SchemeRegistry schemeRegistry;
84 
85     /** The operator for opening and updating connections. */
86     protected ClientConnectionOperator connOperator;
87 
88     /** The one and only entry in this pool. */
89     protected PoolEntry uniquePoolEntry;
90 
91     /** The currently issued managed connection, if any. */
92     protected ConnAdapter managedConn;
93 
94     /** The time of the last connection release, or -1. */
95     protected long lastReleaseTime;
96 
97     /** The time the last released connection expires and shouldn't be reused. */
98     protected long connectionExpiresTime;
99 
100     /** Whether the connection should be shut down  on release. */
101     protected boolean alwaysShutDown;
102 
103     /** Indicates whether this connection manager is shut down. */
104     protected volatile boolean isShutDown;
105 
106 
107 
108 
109     /**
110      * Creates a new simple connection manager.
111      *
112      * @param params    the parameters for this manager
113      * @param schreg    the scheme registry
114      */
SingleClientConnManager(HttpParams params, SchemeRegistry schreg)115     public SingleClientConnManager(HttpParams params,
116                                    SchemeRegistry schreg) {
117 
118         if (schreg == null) {
119             throw new IllegalArgumentException
120                 ("Scheme registry must not be null.");
121         }
122         this.schemeRegistry  = schreg;
123         this.connOperator    = createConnectionOperator(schreg);
124         this.uniquePoolEntry = new PoolEntry();
125         this.managedConn     = null;
126         this.lastReleaseTime = -1L;
127         this.alwaysShutDown  = false; //@@@ from params? as argument?
128         this.isShutDown      = false;
129 
130     } // <constructor>
131 
132 
133     @Override
finalize()134     protected void finalize() throws Throwable {
135         shutdown();
136         super.finalize();
137     }
138 
139 
140     // non-javadoc, see interface ClientConnectionManager
getSchemeRegistry()141     public SchemeRegistry getSchemeRegistry() {
142         return this.schemeRegistry;
143     }
144 
145 
146     /**
147      * Hook for creating the connection operator.
148      * It is called by the constructor.
149      * Derived classes can override this method to change the
150      * instantiation of the operator.
151      * The default implementation here instantiates
152      * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
153      *
154      * @param schreg    the scheme registry to use, or <code>null</code>
155      *
156      * @return  the connection operator to use
157      */
158     protected ClientConnectionOperator
createConnectionOperator(SchemeRegistry schreg)159         createConnectionOperator(SchemeRegistry schreg) {
160 
161         return new DefaultClientConnectionOperator(schreg);
162     }
163 
164 
165     /**
166      * Asserts that this manager is not shut down.
167      *
168      * @throws IllegalStateException    if this manager is shut down
169      */
assertStillUp()170     protected final void assertStillUp()
171         throws IllegalStateException {
172 
173         if (this.isShutDown)
174             throw new IllegalStateException("Manager is shut down.");
175     }
176 
177 
requestConnection( final HttpRoute route, final Object state)178     public final ClientConnectionRequest requestConnection(
179             final HttpRoute route,
180             final Object state) {
181 
182         return new ClientConnectionRequest() {
183 
184             public void abortRequest() {
185                 // Nothing to abort, since requests are immediate.
186             }
187 
188             public ManagedClientConnection getConnection(
189                     long timeout, TimeUnit tunit) {
190                 return SingleClientConnManager.this.getConnection(
191                         route, state);
192             }
193 
194         };
195     }
196 
197 
198     /**
199      * Obtains a connection.
200      * This method does not block.
201      *
202      * @param route     where the connection should point to
203      *
204      * @return  a connection that can be used to communicate
205      *          along the given route
206      */
207     public ManagedClientConnection getConnection(HttpRoute route, Object state) {
208 
209         if (route == null) {
210             throw new IllegalArgumentException("Route may not be null.");
211         }
212         assertStillUp();
213 
214         if (log.isDebugEnabled()) {
215             log.debug("Get connection for route " + route);
216         }
217 
218         if (managedConn != null)
219             revokeConnection();
220 
221         // check re-usability of the connection
222         boolean recreate = false;
223         boolean shutdown = false;
224 
225         // Kill the connection if it expired.
226         closeExpiredConnections();
227 
228         if (uniquePoolEntry.connection.isOpen()) {
229             RouteTracker tracker = uniquePoolEntry.tracker;
230             shutdown = (tracker == null || // can happen if method is aborted
231                         !tracker.toRoute().equals(route));
232         } else {
233             // If the connection is not open, create a new PoolEntry,
234             // as the connection may have been marked not reusable,
235             // due to aborts -- and the PoolEntry should not be reused
236             // either.  There's no harm in recreating an entry if
237             // the connection is closed.
238             recreate = true;
239         }
240 
241         if (shutdown) {
242             recreate = true;
243             try {
244                 uniquePoolEntry.shutdown();
245             } catch (IOException iox) {
246                 log.debug("Problem shutting down connection.", iox);
247             }
248         }
249 
250         if (recreate)
251             uniquePoolEntry = new PoolEntry();
252 
253         // BEGIN android-changed
254         // When using a recycled Socket, we need to re-tag it with any
255         // updated statistics options.
256         try {
257             final Socket socket = uniquePoolEntry.connection.getSocket();
258             if (socket != null) {
259                 SocketTagger.get().tag(socket);
260             }
261         } catch (IOException iox) {
262             log.debug("Problem tagging socket.", iox);
263         }
264         // END android-changed
265 
266         managedConn = new ConnAdapter(uniquePoolEntry, route);
267 
268         return managedConn;
269     }
270 
271 
272     // non-javadoc, see interface ClientConnectionManager
273     public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
274         assertStillUp();
275 
276         if (!(conn instanceof ConnAdapter)) {
277             throw new IllegalArgumentException
278                 ("Connection class mismatch, " +
279                  "connection not obtained from this manager.");
280         }
281 
282         if (log.isDebugEnabled()) {
283             log.debug("Releasing connection " + conn);
284         }
285 
286         ConnAdapter sca = (ConnAdapter) conn;
287         if (sca.poolEntry == null)
288             return; // already released
289         ClientConnectionManager manager = sca.getManager();
290         if (manager != null && manager != this) {
291             throw new IllegalArgumentException
292                 ("Connection not obtained from this manager.");
293         }
294 
295         try {
296             // BEGIN android-changed
297             // When recycling a Socket, we un-tag it to avoid collecting
298             // statistics from future users.
299             final Socket socket = uniquePoolEntry.connection.getSocket();
300             if (socket != null) {
301                 SocketTagger.get().untag(socket);
302             }
303             // END android-changed
304 
305             // make sure that the response has been read completely
306             if (sca.isOpen() && (this.alwaysShutDown ||
307                                  !sca.isMarkedReusable())
308                 ) {
309                 if (log.isDebugEnabled()) {
310                     log.debug
311                         ("Released connection open but not reusable.");
312                 }
313 
314                 // make sure this connection will not be re-used
315                 // we might have gotten here because of a shutdown trigger
316                 // shutdown of the adapter also clears the tracked route
317                 sca.shutdown();
318             }
319         } catch (IOException iox) {
320             //@@@ log as warning? let pass?
321             if (log.isDebugEnabled())
322                 log.debug("Exception shutting down released connection.",
323                           iox);
324         } finally {
325             sca.detach();
326             managedConn = null;
327             lastReleaseTime = System.currentTimeMillis();
328             if(validDuration > 0)
329                 connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
330             else
331                 connectionExpiresTime = Long.MAX_VALUE;
332         }
333     } // releaseConnection
334 
335     public void closeExpiredConnections() {
336         if(System.currentTimeMillis() >= connectionExpiresTime) {
337             closeIdleConnections(0, TimeUnit.MILLISECONDS);
338         }
339     }
340 
341 
342     // non-javadoc, see interface ClientConnectionManager
343     public void closeIdleConnections(long idletime, TimeUnit tunit) {
344         assertStillUp();
345 
346         // idletime can be 0 or negative, no problem there
347         if (tunit == null) {
348             throw new IllegalArgumentException("Time unit must not be null.");
349         }
350 
351         if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
352             final long cutoff =
353                 System.currentTimeMillis() - tunit.toMillis(idletime);
354             if (lastReleaseTime <= cutoff) {
355                 try {
356                     uniquePoolEntry.close();
357                 } catch (IOException iox) {
358                     // ignore
359                     log.debug("Problem closing idle connection.", iox);
360                 }
361             }
362         }
363     }
364 
365 
366     // non-javadoc, see interface ClientConnectionManager
367     public void shutdown() {
368 
369         this.isShutDown = true;
370 
371         if (managedConn != null)
372             managedConn.detach();
373 
374         try {
375             if (uniquePoolEntry != null) // and connection open?
376                 uniquePoolEntry.shutdown();
377         } catch (IOException iox) {
378             // ignore
379             log.debug("Problem while shutting down manager.", iox);
380         } finally {
381             uniquePoolEntry = null;
382         }
383     }
384 
385 
386     /**
387      * Revokes the currently issued connection.
388      * The adapter gets disconnected, the connection will be shut down.
389      */
390     protected void revokeConnection() {
391         if (managedConn == null)
392             return;
393 
394         log.warn(MISUSE_MESSAGE);
395 
396         managedConn.detach();
397 
398         try {
399             uniquePoolEntry.shutdown();
400         } catch (IOException iox) {
401             // ignore
402             log.debug("Problem while shutting down connection.", iox);
403         }
404     }
405 
406 
407     /**
408      * The pool entry for this connection manager.
409      */
410     protected class PoolEntry extends AbstractPoolEntry {
411 
412         /**
413          * Creates a new pool entry.
414          *
415          */
416         protected PoolEntry() {
417             super(SingleClientConnManager.this.connOperator, null);
418         }
419 
420         /**
421          * Closes the connection in this pool entry.
422          */
423         protected void close()
424             throws IOException {
425 
426             shutdownEntry();
427             if (connection.isOpen())
428                 connection.close();
429         }
430 
431 
432         /**
433          * Shuts down the connection in this pool entry.
434          */
435         protected void shutdown()
436             throws IOException {
437 
438             shutdownEntry();
439             if (connection.isOpen())
440                 connection.shutdown();
441         }
442 
443     } // class PoolEntry
444 
445 
446 
447     /**
448      * The connection adapter used by this manager.
449      */
450     protected class ConnAdapter extends AbstractPooledConnAdapter {
451 
452         /**
453          * Creates a new connection adapter.
454          *
455          * @param entry   the pool entry for the connection being wrapped
456          * @param route   the planned route for this connection
457          */
458         protected ConnAdapter(PoolEntry entry, HttpRoute route) {
459             super(SingleClientConnManager.this, entry);
460             markReusable();
461             entry.route = route;
462         }
463 
464     }
465 
466 
467 } // class SingleClientConnManager
468