mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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  */
    28 package ch.boye.httpclientandroidlib.impl.conn;
    30 import java.io.IOException;
    31 import java.util.concurrent.TimeUnit;
    33 import ch.boye.httpclientandroidlib.annotation.GuardedBy;
    34 import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
    36 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
    37 /* LogFactory removed by HttpClient for Android script. */
    38 import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
    39 import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator;
    40 import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest;
    41 import ch.boye.httpclientandroidlib.conn.ManagedClientConnection;
    42 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
    43 import ch.boye.httpclientandroidlib.conn.routing.RouteTracker;
    44 import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
    45 import ch.boye.httpclientandroidlib.params.HttpParams;
    47 /**
    48  * A connection manager for a single connection. This connection manager
    49  * maintains only one active connection at a time. Even though this class
    50  * is thread-safe it ought to be used by one execution thread only.
    51  * <p>
    52  * SingleClientConnManager will make an effort to reuse the connection
    53  * for subsequent requests with the same {@link HttpRoute route}.
    54  * It will, however, close the existing connection and open it
    55  * for the given route, if the route of the persistent connection does
    56  * not match that of the connection request. If the connection has been
    57  * already been allocated {@link IllegalStateException} is thrown.
    58  *
    59  * @since 4.0
    60  */
    61 @ThreadSafe
    62 public class SingleClientConnManager implements ClientConnectionManager {
    64     public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
    66     /** The message to be logged on multiple allocation. */
    67     public final static String MISUSE_MESSAGE =
    68     "Invalid use of SingleClientConnManager: connection still allocated.\n" +
    69     "Make sure to release the connection before allocating another one.";
    71     /** The schemes supported by this connection manager. */
    72     protected final SchemeRegistry schemeRegistry;
    74     /** The operator for opening and updating connections. */
    75     protected final ClientConnectionOperator connOperator;
    77     /** Whether the connection should be shut down  on release. */
    78     protected final boolean alwaysShutDown;
    80     /** The one and only entry in this pool. */
    81     @GuardedBy("this")
    82     protected PoolEntry uniquePoolEntry;
    84     /** The currently issued managed connection, if any. */
    85     @GuardedBy("this")
    86     protected ConnAdapter managedConn;
    88     /** The time of the last connection release, or -1. */
    89     @GuardedBy("this")
    90     protected long lastReleaseTime;
    92     /** The time the last released connection expires and shouldn't be reused. */
    93     @GuardedBy("this")
    94     protected long connectionExpiresTime;
    96     /** Indicates whether this connection manager is shut down. */
    97     protected volatile boolean isShutDown;
    99     /**
   100      * Creates a new simple connection manager.
   101      *
   102      * @param params    the parameters for this manager
   103      * @param schreg    the scheme registry
   104      *
   105      * @deprecated use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)}
   106      */
   107     @Deprecated
   108     public SingleClientConnManager(HttpParams params,
   109                                    SchemeRegistry schreg) {
   110         this(schreg);
   111     }
   112     /**
   113      * Creates a new simple connection manager.
   114      *
   115      * @param schreg    the scheme registry
   116      */
   117     public SingleClientConnManager(final SchemeRegistry schreg) {
   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     }
   131     /**
   132      * @since 4.1
   133      */
   134     public SingleClientConnManager() {
   135         this(SchemeRegistryFactory.createDefault());
   136     }
   138     @Override
   139     protected void finalize() throws Throwable {
   140         try {
   141             shutdown();
   142         } finally { // Make sure we call overridden method even if shutdown barfs
   143             super.finalize();
   144         }
   145     }
   147     public SchemeRegistry getSchemeRegistry() {
   148         return this.schemeRegistry;
   149     }
   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         return new DefaultClientConnectionOperator(schreg);
   166     }
   168     /**
   169      * Asserts that this manager is not shut down.
   170      *
   171      * @throws IllegalStateException    if this manager is shut down
   172      */
   173     protected final void assertStillUp() throws IllegalStateException {
   174         if (this.isShutDown)
   175             throw new IllegalStateException("Manager is shut down.");
   176     }
   178     public final ClientConnectionRequest requestConnection(
   179             final HttpRoute route,
   180             final Object state) {
   182         return new ClientConnectionRequest() {
   184             public void abortRequest() {
   185                 // Nothing to abort, since requests are immediate.
   186             }
   188             public ManagedClientConnection getConnection(
   189                     long timeout, TimeUnit tunit) {
   190                 return SingleClientConnManager.this.getConnection(
   191                         route, state);
   192             }
   194         };
   195     }
   197     /**
   198      * Obtains a connection.
   199      *
   200      * @param route     where the connection should point to
   201      *
   202      * @return  a connection that can be used to communicate
   203      *          along the given route
   204      */
   205     public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) {
   206         if (route == null) {
   207             throw new IllegalArgumentException("Route may not be null.");
   208         }
   209         assertStillUp();
   211         if (log.isDebugEnabled()) {
   212             log.debug("Get connection for route " + route);
   213         }
   215         if (managedConn != null)
   216             throw new IllegalStateException(MISUSE_MESSAGE);
   218         // check re-usability of the connection
   219         boolean recreate = false;
   220         boolean shutdown = false;
   222         // Kill the connection if it expired.
   223         closeExpiredConnections();
   225         if (uniquePoolEntry.connection.isOpen()) {
   226             RouteTracker tracker = uniquePoolEntry.tracker;
   227             shutdown = (tracker == null || // can happen if method is aborted
   228                         !tracker.toRoute().equals(route));
   229         } else {
   230             // If the connection is not open, create a new PoolEntry,
   231             // as the connection may have been marked not reusable,
   232             // due to aborts -- and the PoolEntry should not be reused
   233             // either.  There's no harm in recreating an entry if
   234             // the connection is closed.
   235             recreate = true;
   236         }
   238         if (shutdown) {
   239             recreate = true;
   240             try {
   241                 uniquePoolEntry.shutdown();
   242             } catch (IOException iox) {
   243                 log.debug("Problem shutting down connection.", iox);
   244             }
   245         }
   247         if (recreate)
   248             uniquePoolEntry = new PoolEntry();
   250         managedConn = new ConnAdapter(uniquePoolEntry, route);
   252         return managedConn;
   253     }
   255     public synchronized void releaseConnection(
   256             ManagedClientConnection conn,
   257             long validDuration, TimeUnit timeUnit) {
   258         assertStillUp();
   260         if (!(conn instanceof ConnAdapter)) {
   261             throw new IllegalArgumentException
   262                 ("Connection class mismatch, " +
   263                  "connection not obtained from this manager.");
   264         }
   266         if (log.isDebugEnabled()) {
   267             log.debug("Releasing connection " + conn);
   268         }
   270         ConnAdapter sca = (ConnAdapter) conn;
   271         if (sca.poolEntry == null)
   272             return; // already released
   273         ClientConnectionManager manager = sca.getManager();
   274         if (manager != null && manager != this) {
   275             throw new IllegalArgumentException
   276                 ("Connection not obtained from this manager.");
   277         }
   279         try {
   280             // make sure that the response has been read completely
   281             if (sca.isOpen() && (this.alwaysShutDown ||
   282                                  !sca.isMarkedReusable())
   283                 ) {
   284                 if (log.isDebugEnabled()) {
   285                     log.debug
   286                         ("Released connection open but not reusable.");
   287                 }
   289                 // make sure this connection will not be re-used
   290                 // we might have gotten here because of a shutdown trigger
   291                 // shutdown of the adapter also clears the tracked route
   292                 sca.shutdown();
   293             }
   294         } catch (IOException iox) {
   295             if (log.isDebugEnabled())
   296                 log.debug("Exception shutting down released connection.",
   297                           iox);
   298         } finally {
   299             sca.detach();
   300             managedConn = null;
   301             lastReleaseTime = System.currentTimeMillis();
   302             if(validDuration > 0)
   303                 connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
   304             else
   305                 connectionExpiresTime = Long.MAX_VALUE;
   306         }
   307     }
   309     public synchronized void closeExpiredConnections() {
   310         if(System.currentTimeMillis() >= connectionExpiresTime) {
   311             closeIdleConnections(0, TimeUnit.MILLISECONDS);
   312         }
   313     }
   315     public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) {
   316         assertStillUp();
   318         // idletime can be 0 or negative, no problem there
   319         if (tunit == null) {
   320             throw new IllegalArgumentException("Time unit must not be null.");
   321         }
   323         if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
   324             final long cutoff =
   325                 System.currentTimeMillis() - tunit.toMillis(idletime);
   326             if (lastReleaseTime <= cutoff) {
   327                 try {
   328                     uniquePoolEntry.close();
   329                 } catch (IOException iox) {
   330                     // ignore
   331                     log.debug("Problem closing idle connection.", iox);
   332                 }
   333             }
   334         }
   335     }
   337     public synchronized void shutdown() {
   339         this.isShutDown = true;
   341         if (managedConn != null)
   342             managedConn.detach();
   344         try {
   345             if (uniquePoolEntry != null) // and connection open?
   346                 uniquePoolEntry.shutdown();
   347         } catch (IOException iox) {
   348             // ignore
   349             log.debug("Problem while shutting down manager.", iox);
   350         } finally {
   351             uniquePoolEntry = null;
   352         }
   353     }
   355     /**
   356      * @deprecated no longer used
   357      */
   358     @Deprecated
   359     protected synchronized void revokeConnection() {
   360         if (managedConn == null)
   361             return;
   362         managedConn.detach();
   363         try {
   364             uniquePoolEntry.shutdown();
   365         } catch (IOException iox) {
   366             // ignore
   367             log.debug("Problem while shutting down connection.", iox);
   368         }
   369     }
   371     /**
   372      * The pool entry for this connection manager.
   373      */
   374     protected class PoolEntry extends AbstractPoolEntry {
   376         /**
   377          * Creates a new pool entry.
   378          *
   379          */
   380         protected PoolEntry() {
   381             super(SingleClientConnManager.this.connOperator, null);
   382         }
   384         /**
   385          * Closes the connection in this pool entry.
   386          */
   387         protected void close() throws IOException {
   388             shutdownEntry();
   389             if (connection.isOpen())
   390                 connection.close();
   391         }
   393         /**
   394          * Shuts down the connection in this pool entry.
   395          */
   396         protected void shutdown() throws IOException {
   397             shutdownEntry();
   398             if (connection.isOpen())
   399                 connection.shutdown();
   400         }
   402     }
   404     /**
   405      * The connection adapter used by this manager.
   406      */
   407     protected class ConnAdapter extends AbstractPooledConnAdapter {
   409         /**
   410          * Creates a new connection adapter.
   411          *
   412          * @param entry   the pool entry for the connection being wrapped
   413          * @param route   the planned route for this connection
   414          */
   415         protected ConnAdapter(PoolEntry entry, HttpRoute route) {
   416             super(SingleClientConnManager.this, entry);
   417             markReusable();
   418             entry.route = route;
   419         }
   421     }
   423 }

mercurial