Home | History | Annotate | Download | only in conn
      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 (at) 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      */
    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
    139     protected void finalize() throws Throwable {
    140         shutdown();
    141         super.finalize();
    142     }
    143 
    144 
    145     // non-javadoc, see interface ClientConnectionManager
    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
    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      */
    175     protected final void assertStillUp()
    176         throws IllegalStateException {
    177 
    178         if (this.isShutDown)
    179             throw new IllegalStateException("Manager is shut down.");
    180     }
    181 
    182 
    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