diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 417f3b92ba4549b2f22340e3107d869d2b9c5bb8 (patch) | |
tree | 2e08a2a91d6d14995df54490e3667f7943fbc6d6 /src/org/apache/http/impl/conn/SingleClientConnManager.java | |
download | external_apache-http-417f3b92ba4549b2f22340e3107d869d2b9c5bb8.tar.gz external_apache-http-417f3b92ba4549b2f22340e3107d869d2b9c5bb8.tar.bz2 external_apache-http-417f3b92ba4549b2f22340e3107d869d2b9c5bb8.zip |
Initial Contribution
Diffstat (limited to 'src/org/apache/http/impl/conn/SingleClientConnManager.java')
-rw-r--r-- | src/org/apache/http/impl/conn/SingleClientConnManager.java | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/org/apache/http/impl/conn/SingleClientConnManager.java b/src/org/apache/http/impl/conn/SingleClientConnManager.java new file mode 100644 index 0000000..7999f3e --- /dev/null +++ b/src/org/apache/http/impl/conn/SingleClientConnManager.java @@ -0,0 +1,444 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java $ + * $Revision: 673450 $ + * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.ClientConnectionOperator; +import org.apache.http.conn.ClientConnectionRequest; +import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.routing.RouteTracker; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.params.HttpParams; + + +/** + * A connection "manager" for a single connection. + * This manager is good only for single-threaded use. + * Allocation <i>always</i> returns the connection immediately, + * even if it has not been released after the previous allocation. + * In that case, a {@link #MISUSE_MESSAGE warning} is logged + * and the previously issued connection is revoked. + * <p> + * This class is derived from <code>SimpleHttpConnectionManager</code> + * in HttpClient 3. See there for original authors. + * </p> + * + * @author <a href="mailto:rolandw at apache.org">Roland Weber</a> + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> + * + * + * <!-- empty lines to avoid svn diff problems --> + * @version $Revision: 673450 $ + * + * @since 4.0 + */ +public class SingleClientConnManager implements ClientConnectionManager { + + private final Log log = LogFactory.getLog(getClass()); + + /** The message to be logged on multiple allocation. */ + public final static String MISUSE_MESSAGE = + "Invalid use of SingleClientConnManager: connection still allocated.\n" + + "Make sure to release the connection before allocating another one."; + + + /** The schemes supported by this connection manager. */ + protected SchemeRegistry schemeRegistry; + + /** The operator for opening and updating connections. */ + protected ClientConnectionOperator connOperator; + + /** The one and only entry in this pool. */ + protected PoolEntry uniquePoolEntry; + + /** The currently issued managed connection, if any. */ + protected ConnAdapter managedConn; + + /** The time of the last connection release, or -1. */ + protected long lastReleaseTime; + + /** The time the last released connection expires and shouldn't be reused. */ + protected long connectionExpiresTime; + + /** Whether the connection should be shut down on release. */ + protected boolean alwaysShutDown; + + /** Indicates whether this connection manager is shut down. */ + protected volatile boolean isShutDown; + + + + + /** + * Creates a new simple connection manager. + * + * @param params the parameters for this manager + * @param schreg the scheme registry, or + * <code>null</code> for the default registry + */ + public SingleClientConnManager(HttpParams params, + SchemeRegistry schreg) { + + if (schreg == null) { + throw new IllegalArgumentException + ("Scheme registry must not be null."); + } + this.schemeRegistry = schreg; + this.connOperator = createConnectionOperator(schreg); + this.uniquePoolEntry = new PoolEntry(); + this.managedConn = null; + this.lastReleaseTime = -1L; + this.alwaysShutDown = false; //@@@ from params? as argument? + this.isShutDown = false; + + } // <constructor> + + + @Override + protected void finalize() throws Throwable { + shutdown(); + super.finalize(); + } + + + // non-javadoc, see interface ClientConnectionManager + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry to use, or <code>null</code> + * + * @return the connection operator to use + */ + protected ClientConnectionOperator + createConnectionOperator(SchemeRegistry schreg) { + + return new DefaultClientConnectionOperator(schreg); + } + + + /** + * Asserts that this manager is not shut down. + * + * @throws IllegalStateException if this manager is shut down + */ + protected final void assertStillUp() + throws IllegalStateException { + + if (this.isShutDown) + throw new IllegalStateException("Manager is shut down."); + } + + + public final ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + return new ClientConnectionRequest() { + + public void abortRequest() { + // Nothing to abort, since requests are immediate. + } + + public ManagedClientConnection getConnection( + long timeout, TimeUnit tunit) { + return SingleClientConnManager.this.getConnection( + route, state); + } + + }; + } + + + /** + * Obtains a connection. + * This method does not block. + * + * @param route where the connection should point to + * + * @return a connection that can be used to communicate + * along the given route + */ + public ManagedClientConnection getConnection(HttpRoute route, Object state) { + + if (route == null) { + throw new IllegalArgumentException("Route may not be null."); + } + assertStillUp(); + + if (log.isDebugEnabled()) { + log.debug("Get connection for route " + route); + } + + if (managedConn != null) + revokeConnection(); + + // check re-usability of the connection + boolean recreate = false; + boolean shutdown = false; + + // Kill the connection if it expired. + closeExpiredConnections(); + + if (uniquePoolEntry.connection.isOpen()) { + RouteTracker tracker = uniquePoolEntry.tracker; + shutdown = (tracker == null || // can happen if method is aborted + !tracker.toRoute().equals(route)); + } else { + // If the connection is not open, create a new PoolEntry, + // as the connection may have been marked not reusable, + // due to aborts -- and the PoolEntry should not be reused + // either. There's no harm in recreating an entry if + // the connection is closed. + recreate = true; + } + + if (shutdown) { + recreate = true; + try { + uniquePoolEntry.shutdown(); + } catch (IOException iox) { + log.debug("Problem shutting down connection.", iox); + } + } + + if (recreate) + uniquePoolEntry = new PoolEntry(); + + managedConn = new ConnAdapter(uniquePoolEntry, route); + + return managedConn; + } + + + // non-javadoc, see interface ClientConnectionManager + public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) { + assertStillUp(); + + if (!(conn instanceof ConnAdapter)) { + throw new IllegalArgumentException + ("Connection class mismatch, " + + "connection not obtained from this manager."); + } + + if (log.isDebugEnabled()) { + log.debug("Releasing connection " + conn); + } + + ConnAdapter sca = (ConnAdapter) conn; + if (sca.poolEntry == null) + return; // already released + ClientConnectionManager manager = sca.getManager(); + if (manager != null && manager != this) { + throw new IllegalArgumentException + ("Connection not obtained from this manager."); + } + + try { + // make sure that the response has been read completely + if (sca.isOpen() && (this.alwaysShutDown || + !sca.isMarkedReusable()) + ) { + if (log.isDebugEnabled()) { + log.debug + ("Released connection open but not reusable."); + } + + // make sure this connection will not be re-used + // we might have gotten here because of a shutdown trigger + // shutdown of the adapter also clears the tracked route + sca.shutdown(); + } + } catch (IOException iox) { + //@@@ log as warning? let pass? + if (log.isDebugEnabled()) + log.debug("Exception shutting down released connection.", + iox); + } finally { + sca.detach(); + managedConn = null; + lastReleaseTime = System.currentTimeMillis(); + if(validDuration > 0) + connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime; + else + connectionExpiresTime = Long.MAX_VALUE; + } + } // releaseConnection + + public void closeExpiredConnections() { + if(System.currentTimeMillis() >= connectionExpiresTime) { + closeIdleConnections(0, TimeUnit.MILLISECONDS); + } + } + + + // non-javadoc, see interface ClientConnectionManager + public void closeIdleConnections(long idletime, TimeUnit tunit) { + assertStillUp(); + + // idletime can be 0 or negative, no problem there + if (tunit == null) { + throw new IllegalArgumentException("Time unit must not be null."); + } + + if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) { + final long cutoff = + System.currentTimeMillis() - tunit.toMillis(idletime); + if (lastReleaseTime <= cutoff) { + try { + uniquePoolEntry.close(); + } catch (IOException iox) { + // ignore + log.debug("Problem closing idle connection.", iox); + } + } + } + } + + + // non-javadoc, see interface ClientConnectionManager + public void shutdown() { + + this.isShutDown = true; + + if (managedConn != null) + managedConn.detach(); + + try { + if (uniquePoolEntry != null) // and connection open? + uniquePoolEntry.shutdown(); + } catch (IOException iox) { + // ignore + log.debug("Problem while shutting down manager.", iox); + } finally { + uniquePoolEntry = null; + } + } + + + /** + * Revokes the currently issued connection. + * The adapter gets disconnected, the connection will be shut down. + */ + protected void revokeConnection() { + if (managedConn == null) + return; + + log.warn(MISUSE_MESSAGE); + + managedConn.detach(); + + try { + uniquePoolEntry.shutdown(); + } catch (IOException iox) { + // ignore + log.debug("Problem while shutting down connection.", iox); + } + } + + + /** + * The pool entry for this connection manager. + */ + protected class PoolEntry extends AbstractPoolEntry { + + /** + * Creates a new pool entry. + * + */ + protected PoolEntry() { + super(SingleClientConnManager.this.connOperator, null); + } + + /** + * Closes the connection in this pool entry. + */ + protected void close() + throws IOException { + + shutdownEntry(); + if (connection.isOpen()) + connection.close(); + } + + + /** + * Shuts down the connection in this pool entry. + */ + protected void shutdown() + throws IOException { + + shutdownEntry(); + if (connection.isOpen()) + connection.shutdown(); + } + + } // class PoolEntry + + + + /** + * The connection adapter used by this manager. + */ + protected class ConnAdapter extends AbstractPooledConnAdapter { + + /** + * Creates a new connection adapter. + * + * @param entry the pool entry for the connection being wrapped + * @param route the planned route for this connection + */ + protected ConnAdapter(PoolEntry entry, HttpRoute route) { + super(SingleClientConnManager.this, entry); + markReusable(); + entry.route = route; + } + + } + + +} // class SingleClientConnManager |