• 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 java.io.IOException;
35 import java.net.Socket;
36 import java.util.concurrent.TimeUnit;
37 
38 import android.net.TrafficStats;
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  * @deprecated Please use {@link java.net.URL#openConnection} instead.
73  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
74  *     for further details.
75  */
76 @Deprecated
77 public class SingleClientConnManager implements ClientConnectionManager {
78 
79     private final Log log = LogFactory.getLog(getClass());
80 
81     /** The message to be logged on multiple allocation. */
82     public final static String MISUSE_MESSAGE =
83     "Invalid use of SingleClientConnManager: connection still allocated.\n" +
84     "Make sure to release the connection before allocating another one.";
85 
86 
87     /** The schemes supported by this connection manager. */
88     protected SchemeRegistry schemeRegistry;
89 
90     /** The operator for opening and updating connections. */
91     protected ClientConnectionOperator connOperator;
92 
93     /** The one and only entry in this pool. */
94     protected PoolEntry uniquePoolEntry;
95 
96     /** The currently issued managed connection, if any. */
97     protected ConnAdapter managedConn;
98 
99     /** The time of the last connection release, or -1. */
100     protected long lastReleaseTime;
101 
102     /** The time the last released connection expires and shouldn't be reused. */
103     protected long connectionExpiresTime;
104 
105     /** Whether the connection should be shut down  on release. */
106     protected boolean alwaysShutDown;
107 
108     /** Indicates whether this connection manager is shut down. */
109     protected volatile boolean isShutDown;
110 
111 
112 
113 
114     /**
115      * Creates a new simple connection manager.
116      *
117      * @param params    the parameters for this manager
118      * @param schreg    the scheme registry
119      */
SingleClientConnManager(HttpParams params, SchemeRegistry schreg)120     public SingleClientConnManager(HttpParams params,
121                                    SchemeRegistry schreg) {
122 
123         if (schreg == null) {
124             throw new IllegalArgumentException
125                 ("Scheme registry must not be null.");
126         }
127         this.schemeRegistry  = schreg;
128         this.connOperator    = createConnectionOperator(schreg);
129         this.uniquePoolEntry = new PoolEntry();
130         this.managedConn     = null;
131         this.lastReleaseTime = -1L;
132         this.alwaysShutDown  = false; //@@@ from params? as argument?
133         this.isShutDown      = false;
134 
135     } // <constructor>
136 
137 
138     @Override
finalize()139     protected void finalize() throws Throwable {
140         shutdown();
141         super.finalize();
142     }
143 
144 
145     // non-javadoc, see interface ClientConnectionManager
getSchemeRegistry()146     public SchemeRegistry getSchemeRegistry() {
147         return this.schemeRegistry;
148     }
149 
150 
151     /**
152      * Hook for creating the connection operator.
153      * It is called by the constructor.
154      * Derived classes can override this method to change the
155      * instantiation of the operator.
156      * The default implementation here instantiates
157      * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
158      *
159      * @param schreg    the scheme registry to use, or <code>null</code>
160      *
161      * @return  the connection operator to use
162      */
163     protected ClientConnectionOperator
createConnectionOperator(SchemeRegistry schreg)164         createConnectionOperator(SchemeRegistry schreg) {
165 
166         return new DefaultClientConnectionOperator(schreg);
167     }
168 
169 
170     /**
171      * Asserts that this manager is not shut down.
172      *
173      * @throws IllegalStateException    if this manager is shut down
174      */
assertStillUp()175     protected final void assertStillUp()
176         throws IllegalStateException {
177 
178         if (this.isShutDown)
179             throw new IllegalStateException("Manager is shut down.");
180     }
181 
182 
requestConnection( final HttpRoute route, final Object state)183     public final ClientConnectionRequest requestConnection(
184             final HttpRoute route,
185             final Object state) {
186 
187         return new ClientConnectionRequest() {
188 
189             public void abortRequest() {
190                 // Nothing to abort, since requests are immediate.
191             }
192 
193             public ManagedClientConnection getConnection(
194                     long timeout, TimeUnit tunit) {
195                 return SingleClientConnManager.this.getConnection(
196                         route, state);
197             }
198 
199         };
200     }
201 
202 
203     /**
204      * Obtains a connection.
205      * This method does not block.
206      *
207      * @param route     where the connection should point to
208      *
209      * @return  a connection that can be used to communicate
210      *          along the given route
211      */
212     public ManagedClientConnection getConnection(HttpRoute route, Object state) {
213 
214         if (route == null) {
215             throw new IllegalArgumentException("Route may not be null.");
216         }
217         assertStillUp();
218 
219         if (log.isDebugEnabled()) {
220             log.debug("Get connection for route " + route);
221         }
222 
223         if (managedConn != null)
224             revokeConnection();
225 
226         // check re-usability of the connection
227         boolean recreate = false;
228         boolean shutdown = false;
229 
230         // Kill the connection if it expired.
231         closeExpiredConnections();
232 
233         if (uniquePoolEntry.connection.isOpen()) {
234             RouteTracker tracker = uniquePoolEntry.tracker;
235             shutdown = (tracker == null || // can happen if method is aborted
236                         !tracker.toRoute().equals(route));
237         } else {
238             // If the connection is not open, create a new PoolEntry,
239             // as the connection may have been marked not reusable,
240             // due to aborts -- and the PoolEntry should not be reused
241             // either.  There's no harm in recreating an entry if
242             // the connection is closed.
243             recreate = true;
244         }
245 
246         if (shutdown) {
247             recreate = true;
248             try {
249                 uniquePoolEntry.shutdown();
250             } catch (IOException iox) {
251                 log.debug("Problem shutting down connection.", iox);
252             }
253         }
254 
255         if (recreate)
256             uniquePoolEntry = new PoolEntry();
257 
258         // BEGIN android-changed
259         // When using a recycled Socket, we need to re-tag it with any
260         // updated statistics options.
261         try {
262             final Socket socket = uniquePoolEntry.connection.getSocket();
263             if (socket != null) {
264                 TrafficStats.tagSocket(socket);
265             }
266         } catch (IOException iox) {
267             log.debug("Problem tagging socket.", iox);
268         }
269         // END android-changed
270 
271         managedConn = new ConnAdapter(uniquePoolEntry, route);
272 
273         return managedConn;
274     }
275 
276 
277     // non-javadoc, see interface ClientConnectionManager
278     public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
279         assertStillUp();
280 
281         if (!(conn instanceof ConnAdapter)) {
282             throw new IllegalArgumentException
283                 ("Connection class mismatch, " +
284                  "connection not obtained from this manager.");
285         }
286 
287         if (log.isDebugEnabled()) {
288             log.debug("Releasing connection " + conn);
289         }
290 
291         ConnAdapter sca = (ConnAdapter) conn;
292         if (sca.poolEntry == null)
293             return; // already released
294         ClientConnectionManager manager = sca.getManager();
295         if (manager != null && manager != this) {
296             throw new IllegalArgumentException
297                 ("Connection not obtained from this manager.");
298         }
299 
300         try {
301             // BEGIN android-changed
302             // When recycling a Socket, we un-tag it to avoid collecting
303             // statistics from future users.
304             final Socket socket = uniquePoolEntry.connection.getSocket();
305             if (socket != null) {
306                 TrafficStats.untagSocket(socket);
307             }
308             // END android-changed
309 
310             // make sure that the response has been read completely
311             if (sca.isOpen() && (this.alwaysShutDown ||
312                                  !sca.isMarkedReusable())
313                 ) {
314                 if (log.isDebugEnabled()) {
315                     log.debug
316                         ("Released connection open but not reusable.");
317                 }
318 
319                 // make sure this connection will not be re-used
320                 // we might have gotten here because of a shutdown trigger
321                 // shutdown of the adapter also clears the tracked route
322                 sca.shutdown();
323             }
324         } catch (IOException iox) {
325             //@@@ log as warning? let pass?
326             if (log.isDebugEnabled())
327                 log.debug("Exception shutting down released connection.",
328                           iox);
329         } finally {
330             sca.detach();
331             managedConn = null;
332             lastReleaseTime = System.currentTimeMillis();
333             if(validDuration > 0)
334                 connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
335             else
336                 connectionExpiresTime = Long.MAX_VALUE;
337         }
338     } // releaseConnection
339 
340     public void closeExpiredConnections() {
341         if(System.currentTimeMillis() >= connectionExpiresTime) {
342             closeIdleConnections(0, TimeUnit.MILLISECONDS);
343         }
344     }
345 
346 
347     // non-javadoc, see interface ClientConnectionManager
348     public void closeIdleConnections(long idletime, TimeUnit tunit) {
349         assertStillUp();
350 
351         // idletime can be 0 or negative, no problem there
352         if (tunit == null) {
353             throw new IllegalArgumentException("Time unit must not be null.");
354         }
355 
356         if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
357             final long cutoff =
358                 System.currentTimeMillis() - tunit.toMillis(idletime);
359             if (lastReleaseTime <= cutoff) {
360                 try {
361                     uniquePoolEntry.close();
362                 } catch (IOException iox) {
363                     // ignore
364                     log.debug("Problem closing idle connection.", iox);
365                 }
366             }
367         }
368     }
369 
370 
371     // non-javadoc, see interface ClientConnectionManager
372     public void shutdown() {
373 
374         this.isShutDown = true;
375 
376         if (managedConn != null)
377             managedConn.detach();
378 
379         try {
380             if (uniquePoolEntry != null) // and connection open?
381                 uniquePoolEntry.shutdown();
382         } catch (IOException iox) {
383             // ignore
384             log.debug("Problem while shutting down manager.", iox);
385         } finally {
386             uniquePoolEntry = null;
387         }
388     }
389 
390 
391     /**
392      * Revokes the currently issued connection.
393      * The adapter gets disconnected, the connection will be shut down.
394      */
395     protected void revokeConnection() {
396         if (managedConn == null)
397             return;
398 
399         log.warn(MISUSE_MESSAGE);
400 
401         managedConn.detach();
402 
403         try {
404             uniquePoolEntry.shutdown();
405         } catch (IOException iox) {
406             // ignore
407             log.debug("Problem while shutting down connection.", iox);
408         }
409     }
410 
411 
412     /**
413      * The pool entry for this connection manager.
414      */
415     protected class PoolEntry extends AbstractPoolEntry {
416 
417         /**
418          * Creates a new pool entry.
419          *
420          */
421         protected PoolEntry() {
422             super(SingleClientConnManager.this.connOperator, null);
423         }
424 
425         /**
426          * Closes the connection in this pool entry.
427          */
428         protected void close()
429             throws IOException {
430 
431             shutdownEntry();
432             if (connection.isOpen())
433                 connection.close();
434         }
435 
436 
437         /**
438          * Shuts down the connection in this pool entry.
439          */
440         protected void shutdown()
441             throws IOException {
442 
443             shutdownEntry();
444             if (connection.isOpen())
445                 connection.shutdown();
446         }
447 
448     } // class PoolEntry
449 
450 
451 
452     /**
453      * The connection adapter used by this manager.
454      */
455     protected class ConnAdapter extends AbstractPooledConnAdapter {
456 
457         /**
458          * Creates a new connection adapter.
459          *
460          * @param entry   the pool entry for the connection being wrapped
461          * @param route   the planned route for this connection
462          */
463         protected ConnAdapter(PoolEntry entry, HttpRoute route) {
464             super(SingleClientConnManager.this, entry);
465             markReusable();
466             entry.route = route;
467         }
468 
469     }
470 
471 
472 } // class SingleClientConnManager
473