summaryrefslogtreecommitdiffstats
path: root/src/org/apache/http/impl/conn/SingleClientConnManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/apache/http/impl/conn/SingleClientConnManager.java')
-rw-r--r--src/org/apache/http/impl/conn/SingleClientConnManager.java444
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