You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ro...@apache.org on 2007/01/21 10:07:58 UTC
svn commit: r498258 - in
/jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn:
ClientConnectionManager.java ManagedClientConnection.java
impl/AbstractClientConnectionAdapter.java
impl/ThreadSafeClientConnManager.java
Author: rolandw
Date: Sun Jan 21 01:07:57 2007
New Revision: 498258
URL: http://svn.apache.org/viewvc?view=rev&rev=498258
Log:
new conn manager, part 1 - it compiles
Added:
jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java (with props)
jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java (with props)
jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java (with props)
jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java (with props)
Added: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java?view=auto&rev=498258
==============================================================================
--- jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java (added)
+++ jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java Sun Jan 21 01:07:57 2007
@@ -0,0 +1,118 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ * 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.conn;
+
+
+
+/**
+ * Management interface for {@link ManagedClientConnection client connections}.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision$ $Date$
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionManager {
+
+ /**
+ * Obtains a connection.
+ * This method will block until a connection becomes available
+ * or the connection manager is {@link #shutdown shut down}.
+ *
+ * @param route where the connection should point to
+ *
+ * @return a connection that can be used to communicate
+ * along the given route
+ */
+ ManagedClientConnection getConnection(HostConfiguration route)
+ ;
+
+
+ /**
+ * Obtains a connection within a given time.
+ * This method will block until a connection becomes available,
+ * the timeout expires, or the connection manager is
+ * {@link #shutdown shut down}.
+ *
+ * @param route where the connection should point to
+ * @param timeout the timeout in milliseconds
+ *
+ * @return a connection that can be used to communicate
+ * along the given route
+ *
+ * @throws ConnectionPoolTimeoutException
+ * in case of a timeout
+ */
+ ManagedClientConnection getConnection(HostConfiguration route,
+ long timeout)
+ throws ConnectionPoolTimeoutException
+ ;
+
+
+ /**
+ * Releases a connection for use by others.
+ *
+ * @param conn the connection to release
+ */
+ void releaseConnection(ManagedClientConnection conn)
+ ;
+
+
+ /**
+ * Closes idle connections in the pool.
+ * Open connections in the pool that have not been used for the
+ * timespan given by the timeout argument will be closed.
+ * Currently allocated connections are not subject to this method.
+ *
+ * @param idletime the idle time in milliseconds
+ *
+ * @since 3.0
+ */
+ void closeIdleConnections(long idletime)
+ ;
+
+
+ /**
+ * Shuts down this connection manager and releases allocated resources.
+ * This includes closing all connections, whether they are currently
+ * used or not.
+ */
+ void shutdown()
+ ;
+
+
+} // interface ClientConnectionManager
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ClientConnectionManager.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java?view=auto&rev=498258
==============================================================================
--- jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java (added)
+++ jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java Sun Jan 21 01:07:57 2007
@@ -0,0 +1,109 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ * 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.conn;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A client-side connection with advanced connection logic.
+ * Instances are typically obtained from a connection manager.
+ *
+ * @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision$ $Date$
+ *
+ * @since 4.0
+ */
+public interface ManagedClientConnection
+ extends HttpClientConnection, HttpInetConnection {
+
+
+ /**
+ * Indicates whether this connection is secure.
+ * The return value is well-defined only while the connection is open.
+ * It may change even while the connection is open.
+ *
+ * @return <code>true</code> if this connection is secure,
+ * <code>false</code> otherwise
+ */
+ boolean isSecure()
+ ;
+
+
+ /**
+ * Opens this connection according to the given route.
+ *
+ * @param route the route along which to open. It will be opened
+ * to the proxy if present, or directly to the target.
+ * @param context the context for opening this connection
+ * @param params the parameters for opening this connection
+ *
+ * @throws IOException in case of a problem
+ */
+ void open(HostConfiguration route, HttpContext context, HttpParams params)
+ throws IOException
+ ;
+
+
+ /* *
+ * Indicates that a tunnel has been created.
+ *
+ * @param route the route along which the tunnel has been created
+ * @param params the parameters for updating the connection
+ */
+ //@@@ tunnelCreated in the old implementation triggers layering
+ //@@@ of the TLS/SSL connection. This should be split in two.
+
+
+ /* *
+ * Releases this connection back to it's connection manager.
+ *
+ * @throws IllegalStateException
+ * if this connection is already released
+ * /
+ boolean release()
+ throws IllegalStateException
+ ;
+ */
+
+
+} // interface ManagedClientConnection
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/ManagedClientConnection.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java?view=auto&rev=498258
==============================================================================
--- jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java (added)
+++ jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java Sun Jan 21 01:07:57 2007
@@ -0,0 +1,188 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * 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.conn.impl;
+
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.params.HttpParams;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ManagedClientConnection;
+
+
+
+/**
+ * Abstract adapter from {@link OperatedClientConnection operated} to
+ * {@link ManagedClientConnection managed} client connections.
+ * Read and write methods are delegated to the wrapped connection.
+ * Operations affecting the connection state have to be implemented
+ * by derived classes. Operations for querying the connection state
+ * are delegated to the wrapped connection if there is one, or
+ * return a default value if there is none.
+ *
+ * @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision$ $Date$
+ *
+ * @since 4.0
+ */
+public abstract class AbstractClientConnectionAdapter
+ implements ManagedClientConnection {
+
+ /** The wrapped connection. */
+ protected OperatedClientConnection wrappedConnection;
+
+
+ /**
+ * Creates a new connection adapter.
+ *
+ * @param conn the connection to wrap, or <code>null</code>
+ */
+ protected AbstractClientConnectionAdapter(OperatedClientConnection conn) {
+
+ wrappedConnection = conn;
+
+ } // <constructor>
+
+
+ /**
+ * Asserts that there is a wrapped connection to delegate to.
+ *
+ * @throws IllegalStateException if there is no wrapped connection
+ */
+ protected final void assertWrappedConn() {
+ if (wrappedConnection == null) {
+ throw new IllegalStateException("No wrapped connection.");
+ }
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public boolean isOpen() {
+ if (wrappedConnection == null)
+ return false;
+
+ return wrappedConnection.isOpen();
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public boolean isStale() {
+ if (wrappedConnection == null)
+ return true;
+
+ return wrappedConnection.isStale();
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void flush()
+ throws IOException {
+
+ assertWrappedConn();
+ wrappedConnection.flush();
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public boolean isResponseAvailable(int timeout)
+ throws IOException {
+
+ assertWrappedConn();
+ return wrappedConnection.isResponseAvailable(timeout);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void receiveResponseEntity(HttpResponse response)
+ throws HttpException, IOException {
+
+ assertWrappedConn();
+ wrappedConnection.receiveResponseEntity(response);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public HttpResponse receiveResponseHeader(HttpParams params)
+ throws HttpException, IOException {
+
+ assertWrappedConn();
+ return wrappedConnection.receiveResponseHeader(params);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void sendRequestEntity(HttpEntityEnclosingRequest request)
+ throws HttpException, IOException {
+
+ assertWrappedConn();
+ wrappedConnection.sendRequestEntity(request);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void sendRequestHeader(HttpRequest request)
+ throws HttpException, IOException {
+
+ assertWrappedConn();
+ wrappedConnection.sendRequestHeader(request);
+ }
+
+
+ // non-javadoc, see interface HttpInetConnection
+ public InetAddress getLocalAddress() {
+ assertWrappedConn();
+ return wrappedConnection.getLocalAddress();
+ }
+
+
+ // non-javadoc, see interface HttpInetConnection
+ public InetAddress getRemoteAddress() {
+ assertWrappedConn();
+ return wrappedConnection.getRemoteAddress();
+ }
+
+
+ // non-javadoc, see interface ManagedClientConnection
+ public boolean isSecure() {
+ assertWrappedConn();
+ return wrappedConnection.isSecure();
+ }
+
+
+} // class AbstractClientConnectionAdapter
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/AbstractClientConnectionAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java?view=auto&rev=498258
==============================================================================
--- jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java (added)
+++ jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java Sun Jan 21 01:07:57 2007
@@ -0,0 +1,1368 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * 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.conn.impl;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.HostConfiguration;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.params.HttpConnectionManagerParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.params.HttpParams;
+import org.apache.http.impl.DefaultHttpParams;
+
+
+/**
+ * Manages a pool of {@link OperatedClientConnection client connections}.
+ * <p>
+ * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
+ * in HttpClient 3. See there for original authors.
+ * </p>
+ *
+ * @author <a href="mailto:rolandw@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$ $Date$
+ *
+ * @since 4.0
+ */
+public class ThreadSafeClientConnManager
+ implements ClientConnectionManager {
+
+
+ private static final Log LOG =
+ LogFactory.getLog(ThreadSafeClientConnManager.class);
+
+ /**
+ * A mapping from Reference to ConnectionSource.
+ * Used to reclaim resources when connections are lost
+ * to the garbage collector.
+ */
+ private static final Map REFERENCE_TO_CONNECTION_SOURCE = new HashMap();
+
+ /**
+ * The reference queue used to track when connections are lost to the
+ * garbage collector
+ */
+ private static final ReferenceQueue REFERENCE_QUEUE = new ReferenceQueue();
+
+ /**
+ * The thread responsible for handling lost connections.
+ */
+ private static ReferenceQueueThread REFERENCE_QUEUE_THREAD;
+
+
+ /**
+ * Holds references to all active instances of this class.
+ */
+ private static WeakHashMap ALL_CONNECTION_MANAGERS = new WeakHashMap();
+
+
+ /** The parameters of this connection manager. */
+ private HttpParams params = new DefaultHttpParams();
+
+
+ /** The pool of connections being managed. */
+ private ConnectionPool connectionPool;
+
+ /** The operator for opening and updating connections. */
+ private ClientConnectionOperator connectionOperator;
+
+ /** Indicates whether this connection manager is shut down. */
+ private boolean isShutDown;
+
+
+
+ /**
+ * Creates a new thread safe connection manager.
+ *
+ * @param params the parameters for this manager
+ */
+ public ThreadSafeClientConnManager(HttpParams params) {
+
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ this.params = params;
+ this.connectionPool = new ConnectionPool();
+ this.connectionOperator = createConnectionOperator();
+ this.isShutDown = false;
+
+ synchronized(ALL_CONNECTION_MANAGERS) {
+ ALL_CONNECTION_MANAGERS.put(this, null);
+ }
+ } // <constructor>
+
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public ManagedClientConnection getConnection(HostConfiguration route) {
+
+ while (true) {
+ try {
+ return getConnection(route, 0);
+ } catch (ConnectionPoolTimeoutException e) {
+ // we'll go ahead and log this, but it should never happen.
+ // Exceptions are only thrown when the timeout occurs and
+ // since we have no timeout, it doesn't happen.
+ LOG.debug(
+ "Unexpected exception while waiting for connection",
+ e
+ );
+ }
+ }
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public ManagedClientConnection getConnection(HostConfiguration route,
+ long timeout)
+ throws ConnectionPoolTimeoutException {
+
+ if (route == null) {
+ throw new IllegalArgumentException("hostConfiguration is null");
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ThreadSafeClientConnManager.getConnection: route = "
+ + route + ", timeout = " + timeout);
+ }
+
+ final TrackingPoolEntry entry = doGetConnection(route, timeout);
+
+ return new HttpConnectionAdapter(entry);
+ }
+
+
+ /**
+ * Obtains a connection within the given timeout.
+ *
+ * @param route the route for which to get the connection
+ * @param timeout the timeout, or 0 for no timeout
+ *
+ * @return the pool entry for the connection
+ *
+ * @throws ConnectionPoolTimeoutException if the timeout expired
+ */
+ private TrackingPoolEntry doGetConnection(HostConfiguration route,
+ long timeout)
+ throws ConnectionPoolTimeoutException {
+
+ TrackingPoolEntry entry = null;
+
+ int maxHostConnections = HttpConnectionManagerParams
+ .getMaxConnectionsPerHost(this.params, route);
+ int maxTotalConnections = HttpConnectionManagerParams
+ .getMaxTotalConnections(this.params);
+
+ synchronized (connectionPool) {
+
+ // we used to clone the hostconfig here, but it is now immutable:
+ //route = new HostConfiguration(route);
+ HostConnectionPool hostPool = connectionPool.getHostPool(route);
+ WaitingThread waitingThread = null;
+
+ boolean useTimeout = (timeout > 0);
+ long timeToWait = timeout;
+ long startWait = 0;
+ long endWait = 0;
+
+ while (entry == null) {
+
+ if (isShutDown) {
+ throw new IllegalStateException
+ ("Connection manager has been shut down.");
+ }
+
+ // happen to have a free connection with the right specs
+ //
+ if (hostPool.freeConnections.size() > 0) {
+ entry = connectionPool.getFreeConnection(route);
+
+ // have room to make more
+ //
+ } else if ((hostPool.numConnections < maxHostConnections)
+ && (connectionPool.numConnections < maxTotalConnections)) {
+
+ entry = createPoolEntry(route);
+
+ // have room to add host connection, and there is at least one
+ // free connection that can be liberated to make overall room
+ //
+ } else if ((hostPool.numConnections < maxHostConnections)
+ && (connectionPool.freeConnections.size() > 0)) {
+
+ connectionPool.deleteLeastUsedConnection();
+ entry = createPoolEntry(route);
+
+ // otherwise, we have to wait for one of the above conditions
+ // to become true
+ //
+ } else {
+ // TODO: keep track of which routes have waiting
+ // threads, so they avoid being sacrificed before necessary
+
+ try {
+
+ if (useTimeout && timeToWait <= 0) {
+ throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to get a connection, waiting..., hostConfig=" + route);
+ }
+
+ if (waitingThread == null) {
+ waitingThread = new WaitingThread();
+ waitingThread.hostConnectionPool = hostPool;
+ waitingThread.thread = Thread.currentThread();
+ }
+
+ if (useTimeout) {
+ startWait = System.currentTimeMillis();
+ }
+
+ hostPool.waitingThreads.addLast(waitingThread);
+ connectionPool.waitingThreads.addLast(waitingThread);
+ connectionPool.wait(timeToWait);
+
+ // we have not been interrupted so we need to remove ourselves from the
+ // wait queue
+ hostPool.waitingThreads.remove(waitingThread);
+ connectionPool.waitingThreads.remove(waitingThread);
+ } catch (InterruptedException e) {
+ // do nothing
+ } finally {
+ if (useTimeout) {
+ endWait = System.currentTimeMillis();
+ timeToWait -= (endWait - startWait);
+ }
+ }
+ }
+ }
+ }
+ return entry;
+
+ } // doGetConnection
+
+
+ /**
+ * Creates a connection to be managed, along with a pool entry.
+ *
+ * @param route the route for which to create the connection
+ *
+ * @return the pool entry for the new connection
+ */
+ private TrackingPoolEntry createPoolEntry(HostConfiguration route) {
+
+ OperatedClientConnection occ = createOperatedConnection();
+ return connectionPool.createEntry(route, occ);
+ }
+
+
+ /**
+ * Hook for creating an operated connection to be managed.
+ * Derived classes can override this method to change the
+ * instantiation of operated connections.
+ * The default implementation here instantiates
+ * {@link DefaultClientConnection DefaultClientConnection}.
+ *
+ * @return a new connection to be managed
+ */
+ protected OperatedClientConnection createOperatedConnection() {
+ return new DefaultClientConnection();
+ }
+
+
+ /**
+ * 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}.
+ *
+ * @return the connection operator to use
+ */
+ protected ClientConnectionOperator createConnectionOperator() {
+ return new DefaultClientConnectionOperator();
+ }
+
+
+ /**
+ * Releases an allocated connection.
+ * If another thread is blocked in getConnection() that could use this
+ * connection, it will be woken up.
+ *
+ * @param conn the connection to make available.
+ */
+ public void releaseConnection(ManagedClientConnection conn) {
+
+ if (!(conn instanceof HttpConnectionAdapter)) {
+ throw new IllegalArgumentException
+ ("Connection class mismatch, " +
+ "connection not obtained from this manager.");
+ }
+ HttpConnectionAdapter hca = (HttpConnectionAdapter) conn;
+ if ((hca.poolEntry != null) && (hca.poolEntry.manager != this)) {
+ throw new IllegalArgumentException
+ ("Connection not obtained from this manager.");
+ }
+
+ TrackingPoolEntry entry = hca.poolEntry;
+ hca.detach();
+ releasePoolEntry(entry);
+ }
+
+
+ /**
+ * Releases an allocated connection by the pool entry.
+ *
+ * @param entry the pool entry for the connection to release,
+ * or <code>null</code>
+ */
+ private void releasePoolEntry(TrackingPoolEntry entry) {
+
+ if (entry == null)
+ return;
+
+ // make sure that the response has been read completely
+ System.out.println("@@@ should consume response and free connection");
+ //@@@ SimpleHttpConnectionManager.finishLastResponse(conn);
+ connectionPool.freeConnection(entry);
+ }
+
+
+
+ // ######################################################################
+ // ######################################################################
+ // ########## old code below ##########
+ // ######################################################################
+ // ######################################################################
+
+
+ /**
+ * Shuts down and cleans up resources used by all instances of
+ * ThreadSafeClientConnManager. All static resources are released, all threads are
+ * stopped, and {@link #shutdown()} is called on all live instances of
+ * ThreadSafeClientConnManager.
+ *
+ * @see #shutdown()
+ */
+ public static void shutdownAll() {
+
+ synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
+ // shutdown all connection managers
+ synchronized (ALL_CONNECTION_MANAGERS) {
+ Iterator connIter = ALL_CONNECTION_MANAGERS.keySet().iterator();
+ while (connIter.hasNext()) {
+ ThreadSafeClientConnManager connManager =
+ (ThreadSafeClientConnManager) connIter.next();
+ connIter.remove();
+ connManager.shutdown();
+ }
+ }
+
+ // shutdown static resources
+ if (REFERENCE_QUEUE_THREAD != null) {
+ REFERENCE_QUEUE_THREAD.shutdown();
+ REFERENCE_QUEUE_THREAD = null;
+ }
+ REFERENCE_TO_CONNECTION_SOURCE.clear();
+ }
+ }
+
+
+ /**
+ * Stores a weak reference to the given pool entry.
+ * Along with the reference, the route and connection pool are stored.
+ * These values will be used to reclaim resources if the connection
+ * is lost to the garbage collector. This method should be called
+ * before a connection is handed out by the connection manager.
+ * <br/>
+ * A static reference to the connection manager will also be stored.
+ * To ensure that the connection manager can be GCed,
+ * {@link #removeReferenceToConnection removeReferenceToConnection}
+ * should be called for all pool entry to which the manager
+ * keeps a strong reference.
+ *
+ * @param connection the pool entry to store a reference for
+ * @param hostConfiguration the connection's route
+ * @param connectionPool the connection pool that created the entry
+ *
+ * @see #removeReferenceToConnection
+ */
+ private static void storeReferenceToConnection(
+ TrackingPoolEntry connection,
+ HostConfiguration hostConfiguration,
+ ConnectionPool connectionPool
+ ) {
+
+ ConnectionSource source = new ConnectionSource();
+ source.connectionPool = connectionPool;
+ source.hostConfiguration = hostConfiguration;
+
+ synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
+
+ // start the reference queue thread if needed
+ if (REFERENCE_QUEUE_THREAD == null) {
+ REFERENCE_QUEUE_THREAD = new ReferenceQueueThread();
+ REFERENCE_QUEUE_THREAD.start();
+ }
+
+ REFERENCE_TO_CONNECTION_SOURCE.put(
+ connection.reference,
+ source
+ );
+ }
+ }
+
+ /**
+ * Removes the reference being stored for the given connection.
+ * This method should be called when the manager again has a
+ * direct reference to the pool entry.
+ *
+ * @param entry the pool entry for which to remove the reference
+ *
+ * @see #storeReferenceToConnection
+ */
+ private static void removeReferenceToConnection(TrackingPoolEntry entry) {
+
+ synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
+ REFERENCE_TO_CONNECTION_SOURCE.remove(entry.reference);
+ }
+ }
+
+
+ /**
+ * Closes and releases all connections currently checked out of the
+ * given connection pool.
+ * @param connectionPool the pool for which to shutdown the connections
+ */
+ private static
+ void shutdownCheckedOutConnections(ConnectionPool connectionPool) {
+
+ // keep a list of the connections to be closed
+ ArrayList connectionsToClose = new ArrayList();
+
+ synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
+
+ Iterator referenceIter = REFERENCE_TO_CONNECTION_SOURCE.keySet().iterator();
+ while (referenceIter.hasNext()) {
+ Reference ref = (Reference) referenceIter.next();
+ ConnectionSource source =
+ (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.get(ref);
+ if (source.connectionPool == connectionPool) {
+ referenceIter.remove();
+ Object entry = ref.get(); // class TrackingPoolEntry
+ if (entry != null) {
+ connectionsToClose.add(entry);
+ }
+ }
+ }
+ }
+
+ // close and release the connections outside of the synchronized block
+ // to avoid holding the lock for too long
+ for (Iterator i = connectionsToClose.iterator(); i.hasNext();) {
+ TrackingPoolEntry entry = (TrackingPoolEntry) i.next();
+ closeConnection(entry.connection);
+ // remove the reference to the connection manager. this ensures
+ // that the we don't accidentally end up here again
+ //@@@ connection.setHttpConnectionManager(null);
+
+ entry.manager.releasePoolEntry(entry);
+ }
+ }
+
+
+
+
+
+
+ // ------------------------------------------------------- Instance Methods
+
+ /**
+ * Shuts down the connection manager and releases all resources. All connections associated
+ * with this class will be closed and released.
+ *
+ * <p>The connection manager can no longer be used once shutdown.
+ *
+ * <p>Calling this method more than once will have no effect.
+ */
+ public synchronized void shutdown() {
+ synchronized (connectionPool) {
+ if (!isShutDown) {
+ isShutDown = true;
+ connectionPool.shutdown();
+ }
+ }
+ }
+
+ /**
+ * Gets the total number of pooled connections for the given host configuration. This
+ * is the total number of connections that have been created and are still in use
+ * by this connection manager for the host configuration. This value will
+ * not exceed the maximum number of connections per host.
+ *
+ * @param hostConfiguration The host configuration
+ * @return The total number of pooled connections
+ */
+ public int getConnectionsInPool(HostConfiguration hostConfiguration) {
+ synchronized (connectionPool) {
+ HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration);
+ return hostPool.numConnections;
+ }
+ }
+
+ /**
+ * Gets the total number of pooled connections. This is the total number of
+ * connections that have been created and are still in use by this connection
+ * manager. This value will not exceed the maximum number of connections
+ * in total.
+ *
+ * @return the total number of pooled connections
+ */
+ public int getConnectionsInPool() {
+ synchronized (connectionPool) {
+ return connectionPool.numConnections;
+ }
+ }
+
+
+ /**
+ * Deletes all free connections that are closed.
+ * Only connections currently owned by the connection
+ * manager are processed.
+ */
+ private void deleteClosedConnections() {
+ connectionPool.deleteClosedConnections();
+ }
+
+
+ /**
+ * Deletes all free connections that are idle or closed.
+ */
+ public void closeIdleConnections(long idleTimeout) {
+ connectionPool.closeIdleConnections(idleTimeout);
+ deleteClosedConnections();
+ }
+
+
+ /**
+ * Returns {@link HttpParams parameters} associated
+ * with this connection manager.
+ */
+ public HttpParams getParams() {
+ return this.params;
+ }
+
+ /**
+ * Assigns {@link HttpParams parameters} for this
+ * connection manager.
+ *
+ * @since 3.0
+ *
+ * @see HttpConnectionManagerParams
+ */
+ public void setParams(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ this.params = params;
+ }
+
+ /**
+ * A structured pool of connections.
+ * This class keeps track of all connections, using overall lists
+ * as well as per-route lists.
+ */
+ private class ConnectionPool {
+
+ /** The list of free connections */
+ private LinkedList freeConnections = new LinkedList();
+
+ /** The list of WaitingThreads waiting for a connection */
+ private LinkedList waitingThreads = new LinkedList();
+
+ /**
+ * Map where keys are {@link HostConfiguration}s and values are
+ * {@link HostConnectionPool}s
+ */
+ private final Map mapHosts = new HashMap();
+
+ private IdleConnectionHandler idleConnectionHandler = new IdleConnectionHandler();
+
+ /** The number of created connections */
+ private int numConnections = 0;
+
+ /**
+ * Cleans up all connection pool resources.
+ */
+ public synchronized void shutdown() {
+
+ // close all free connections
+ Iterator iter = freeConnections.iterator();
+ while (iter.hasNext()) {
+ TrackingPoolEntry entry = (TrackingPoolEntry) iter.next();
+ iter.remove();
+ closeConnection(entry.connection);
+ }
+
+ // close all connections that have been checked out
+ shutdownCheckedOutConnections(this);
+
+ // interrupt all waiting threads
+ iter = waitingThreads.iterator();
+ while (iter.hasNext()) {
+ WaitingThread waiter = (WaitingThread) iter.next();
+ iter.remove();
+ waiter.thread.interrupt();
+ }
+
+ // clear out map hosts
+ mapHosts.clear();
+
+ // remove all references to connections
+ idleConnectionHandler.removeAll();
+ }
+
+ /**
+ * Creates a new pool entry for an operated connection.
+ *
+ * @param route the route associated with the new entry
+ * @param conn the underlying connection for the new entry
+ *
+ * @return the new pool entry
+ */
+ protected synchronized
+ TrackingPoolEntry createEntry(HostConfiguration route,
+ OperatedClientConnection conn) {
+
+ HostConnectionPool hostPool = getHostPool(route);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Allocating new connection, hostConfiguration=" + route);
+ }
+ TrackingPoolEntry entry = new TrackingPoolEntry(conn);
+ numConnections++;
+ hostPool.numConnections++;
+
+ // store a reference to this entry so that it can be cleaned up
+ // in the event it is not correctly released
+ storeReferenceToConnection(entry, route, this);
+ return entry;
+ }
+
+
+ /**
+ * Handles cleaning up for a lost connection with the given config. Decrements any
+ * connection counts and notifies waiting threads, if appropriate.
+ *
+ * @param config the host configuration of the connection that was lost
+ */
+ public synchronized void handleLostConnection(HostConfiguration config) {
+ HostConnectionPool hostPool = getHostPool(config);
+ hostPool.numConnections--;
+ if (hostPool.numConnections < 1)
+ mapHosts.remove(config);
+
+ numConnections--;
+ notifyWaitingThread(config);
+ }
+
+ /**
+ * Get the pool (list) of connections available for the given hostConfig.
+ *
+ * @param hostConfiguration the configuraton for the connection pool
+ * @return a pool (list) of connections available for the given config
+ */
+ public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration) {
+
+ // Look for a list of connections for the given config
+ HostConnectionPool listConnections = (HostConnectionPool)
+ mapHosts.get(hostConfiguration);
+ if (listConnections == null) {
+ // First time for this config
+ listConnections = new HostConnectionPool();
+ listConnections.hostConfiguration = hostConfiguration;
+ mapHosts.put(hostConfiguration, listConnections);
+ }
+
+ return listConnections;
+ }
+
+ /**
+ * If available, get a free connection for this host
+ *
+ * @param hostConfiguration the configuraton for the connection pool
+ * @return an available connection for the given config
+ */
+ public synchronized TrackingPoolEntry getFreeConnection(HostConfiguration hostConfiguration) {
+
+ TrackingPoolEntry entry = null;
+
+ HostConnectionPool hostPool = getHostPool(hostConfiguration);
+
+ if (hostPool.freeConnections.size() > 0) {
+ entry = (TrackingPoolEntry) hostPool.freeConnections.removeLast();
+ freeConnections.remove(entry);
+ // store a reference to this entry so that it can be cleaned up
+ // in the event it is not correctly released
+ storeReferenceToConnection(entry, hostConfiguration, this);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
+ }
+
+ // remove the connection from the timeout handler
+ idleConnectionHandler.remove(entry.connection);
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug("There were no free connections to get, hostConfig="
+ + hostConfiguration);
+ }
+ return entry;
+ }
+
+ /**
+ * Deletes all closed connections.
+ */
+ public synchronized void deleteClosedConnections() {
+
+ Iterator iter = freeConnections.iterator();
+
+ while (iter.hasNext()) {
+ TrackingPoolEntry entry =
+ (TrackingPoolEntry) iter.next();
+ if (!entry.connection.isOpen()) {
+ iter.remove();
+ deleteConnection(entry);
+ }
+ }
+ }
+
+ /**
+ * Closes idle connections.
+ * @param idleTimeout
+ */
+ public synchronized void closeIdleConnections(long idleTimeout) {
+ idleConnectionHandler.closeIdleConnections(idleTimeout);
+ }
+
+ /**
+ * Deletes the given connection.
+ * This will remove all reference to the connection
+ * so that it can be GCed.
+ *
+ * <p><b>Note:</b> Does not remove the connection from the
+ * freeConnections list. It
+ * is assumed that the caller has already handled this step.</p>
+ *
+ * @param entry the pool entry for the connection to delete
+ */
+ private synchronized void deleteConnection(TrackingPoolEntry entry) {
+
+ HostConfiguration route = entry.route;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Reclaiming connection, hostConfig=" + route);
+ }
+
+ closeConnection(entry.connection);
+
+ HostConnectionPool hostPool = getHostPool(route);
+
+ hostPool.freeConnections.remove(entry);
+ hostPool.numConnections--;
+ numConnections--;
+ if (hostPool.numConnections < 1)
+ mapHosts.remove(route);
+
+ // remove the connection from the timeout handler
+ idleConnectionHandler.remove(entry.connection);
+ }
+
+ /**
+ * Close and delete an old, unused connection to make room for a new one.
+ */
+ public synchronized void deleteLeastUsedConnection() {
+
+ TrackingPoolEntry entry =
+ (TrackingPoolEntry) freeConnections.removeFirst();
+
+ if (entry != null) {
+ deleteConnection(entry);
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempted to reclaim an unused connection but there were none.");
+ }
+ }
+
+ /**
+ * Notifies a waiting thread that a connection for the given configuration is
+ * available.
+ * @param configuration the host config to use for notifying
+ * @see #notifyWaitingThread(HostConnectionPool)
+ */
+ public synchronized void notifyWaitingThread(HostConfiguration configuration) {
+ notifyWaitingThread(getHostPool(configuration));
+ }
+
+ /**
+ * Notifies a waiting thread that a connection for the given configuration is
+ * available. This will wake a thread waiting in this host pool or if there is not
+ * one a thread in the connection pool will be notified.
+ *
+ * @param hostPool the host pool to use for notifying
+ */
+ public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
+
+ // find the thread we are going to notify, we want to ensure that each
+ // waiting thread is only interrupted once so we will remove it from
+ // all wait queues before interrupting it
+ WaitingThread waitingThread = null;
+
+ if (hostPool.waitingThreads.size() > 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Notifying thread waiting on host pool, hostConfig="
+ + hostPool.hostConfiguration);
+ }
+ waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
+ waitingThreads.remove(waitingThread);
+ } else if (waitingThreads.size() > 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
+ }
+ waitingThread = (WaitingThread) waitingThreads.removeFirst();
+ waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug("Notifying no-one, there are no waiting threads");
+ }
+
+ if (waitingThread != null) {
+ waitingThread.thread.interrupt();
+ }
+ }
+
+ /**
+ * Marks the given connection as free.
+ *
+ * @param entry the pool entry for the connection
+ */
+ private void freeConnection(TrackingPoolEntry entry) {
+
+ HostConfiguration route = entry.route;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Freeing connection, hostConfig=" + route);
+ }
+
+ synchronized (this) {
+
+ if (isShutDown) {
+ // the connection manager has been shutdown, release the
+ // connection's resources and get out of here
+ closeConnection(entry.connection);
+ return;
+ }
+
+ HostConnectionPool hostPool = getHostPool(route);
+
+ // Put the connect back in the available list and notify a waiter
+ hostPool.freeConnections.add(entry);
+ if (hostPool.numConnections == 0) {
+ // for some reason this connection pool didn't already exist
+ LOG.error("Host connection pool not found, hostConfig="
+ + route);
+ hostPool.numConnections = 1;
+ }
+
+ freeConnections.add(entry);
+ // we can remove the reference to this connection as we have control over
+ // it again. this also ensures that the connection manager can be GCed
+ removeReferenceToConnection(entry);
+ if (numConnections == 0) {
+ // for some reason this connection pool didn't already exist
+ LOG.error("Host connection pool not found, hostConfig="
+ + route);
+ numConnections = 1;
+ }
+
+ // register the connection with the timeout handler
+ idleConnectionHandler.add(entry.connection);
+
+ notifyWaitingThread(hostPool);
+ }
+ }
+ }
+
+ //@@@ change argument to OperatedClientConnection
+ //@@@ private static void closeConnection(final org.apache.http.HttpConnection conn) {
+ private static void closeConnection(final OperatedClientConnection conn) {
+ try {
+ conn.close();
+ } catch (IOException ex) {
+ LOG.debug("I/O error closing connection", ex);
+ }
+ }
+
+ /**
+ * A simple struct-like class to combine the objects needed to release a connection's
+ * resources when claimed by the garbage collector.
+ */
+ private static class ConnectionSource {
+
+ /** The connection pool that created the connection */
+ public ConnectionPool connectionPool;
+
+ /** The connection's host configuration */
+ public HostConfiguration hostConfiguration;
+ }
+
+ /**
+ * A simple struct-like class to combine the connection list and the count
+ * of created connections.
+ */
+ private static class HostConnectionPool {
+ /** The hostConfig this pool is for */
+ public HostConfiguration hostConfiguration;
+
+ /** The list of free connections */
+ public LinkedList freeConnections = new LinkedList();
+
+ /** The list of WaitingThreads for this host */
+ public LinkedList waitingThreads = new LinkedList();
+
+ /** The number of created connections */
+ public int numConnections = 0;
+ }
+
+ /**
+ * A simple struct-like class to combine the waiting thread and the connection
+ * pool it is waiting on.
+ */
+ private static class WaitingThread {
+ /** The thread that is waiting for a connection */
+ public Thread thread;
+
+ /** The connection pool the thread is waiting for */
+ public HostConnectionPool hostConnectionPool;
+ }
+
+ /**
+ * A thread for listening for HttpConnections reclaimed by the garbage
+ * collector.
+ */
+ private static class ReferenceQueueThread extends Thread {
+
+ private boolean isShutDown = false;
+
+ /**
+ * Create an instance and make this a daemon thread.
+ */
+ public ReferenceQueueThread() {
+ setDaemon(true);
+ setName("ThreadSafeClientConnManager cleanup");
+ }
+
+ public void shutdown() {
+ this.isShutDown = true;
+ }
+
+ /**
+ * Handles cleaning up for the given connection reference.
+ *
+ * @param ref the reference to clean up
+ */
+ private void handleReference(Reference ref) {
+
+ ConnectionSource source = null;
+
+ synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
+ source = (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.remove(ref);
+ }
+ // only clean up for this reference if it is still associated with
+ // a ConnectionSource
+ if (source != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Connection reclaimed by garbage collector, hostConfig="
+ + source.hostConfiguration);
+ }
+
+ source.connectionPool.handleLostConnection(source.hostConfiguration);
+ }
+ }
+
+ /**
+ * Start execution.
+ */
+ public void run() {
+ while (!isShutDown) {
+ try {
+ // remove the next reference and process it, a timeout
+ // is used so that the thread does not block indefinitely
+ // and therefore keep the thread from shutting down
+ Reference ref = REFERENCE_QUEUE.remove(1000);
+ if (ref != null) {
+ handleReference(ref);
+ }
+ } catch (InterruptedException e) {
+ LOG.debug("ReferenceQueueThread interrupted", e);
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * A pool entry representing a connection that tracks it's route.
+ * For historical reasons, these entries are sometimes referred to
+ * as <i>connections</i> throughout the code.
+ */
+ private class TrackingPoolEntry {
+
+ /** The underlying connection being pooled or used. */
+ private OperatedClientConnection connection;
+
+ /** The host configuration part of the route. */
+ private HostConfiguration route;
+
+ /** The tunnel created flag part of the route. */
+ private boolean tunnelled;
+
+ /** The connection manager. */
+ private ThreadSafeClientConnManager manager;
+
+
+ /**
+ * A weak reference used to detect GCed pool entries.
+ * Of course, pool entries can only be GCed when they are allocated
+ * and therefore not referenced with a hard link in the manager.
+ */
+ private WeakReference reference;
+
+
+ /**
+ * Creates a new pool entry.
+ *
+ * @param occ the underlying connection for this entry
+ */
+ private TrackingPoolEntry(OperatedClientConnection occ) {
+ this.connection = occ;
+ this.route = null;
+ this.tunnelled = false;
+ this.manager = ThreadSafeClientConnManager.this;
+ this.reference = new WeakReference(this, REFERENCE_QUEUE);
+ }
+
+
+ /**
+ * Opens the underlying connection.
+ *
+ * @param route the route along which to open the connection
+ * @param context the context for opening the connection
+ * @param params the parameters for opening the connection
+ *
+ * @throws IOException in case of a problem
+ */
+ private void open(HostConfiguration route,
+ HttpContext context, HttpParams params)
+ throws IOException {
+
+ if (route == null) {
+ throw new IllegalArgumentException
+ ("HostConfiguration must not be null.");
+ }
+ //@@@ is context allowed to be null? depends on operator?
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ // - collect the arguments
+ // - call the operator
+ // - update the tracking data
+ // In this order, we can be sure that only a successful
+ // opening of the connection will be tracked.
+
+ final HttpHost target = route.getHost();
+ if (target == null) {
+ throw new IllegalStateException
+ ("Target host must not be null.");
+ }
+ final HttpHost proxy = route.getProxyHost();
+
+ if (LOG.isDebugEnabled()) {
+ if (proxy == null) {
+ LOG.debug("Open connection to " + target);
+ } else {
+ LOG.debug("Open connection to " + target +
+ " via proxy " + proxy);
+ }
+ }
+
+ ThreadSafeClientConnManager.this.connectionOperator.openConnection
+ (this.connection,
+ (proxy != null) ? proxy : target,
+ context, params);
+
+ this.route = route;
+
+ } // open
+
+
+ /**
+ * Tracks close or shutdown of the connection.
+ * There is no distinction between the two, the route is dropped
+ * in both cases. This method should be called regardless of
+ * whether the close or shutdown succeeds or triggers an error.
+ */
+ private void closing() {
+ route = null;
+ tunnelled = false;
+ }
+
+ } // class TrackingPoolEntry
+
+
+ /**
+ * A connection wrapper and callback handler.
+ * All connections given out by the manager are wrappers which
+ * can be {@link #detach detach}ed to prevent further use on release.
+ */
+ private class HttpConnectionAdapter
+ extends AbstractClientConnectionAdapter {
+
+ /** The wrapped pool entry. */
+ private TrackingPoolEntry poolEntry;
+
+
+ /**
+ * Creates a new adapter.
+ *
+ * @param entry the pool entry for the connection being wrapped
+ */
+ protected HttpConnectionAdapter(TrackingPoolEntry entry) {
+ super(entry.connection);
+ poolEntry = entry;
+ }
+
+
+ /**
+ * Asserts that this adapter is still attached.
+ *
+ * @throws IllegalStateException
+ * if it is {@link #detach detach}ed
+ */
+ protected final void assertAttached() {
+ if (poolEntry == null) {
+ throw new IllegalStateException("Adapter is detached.");
+ }
+ }
+
+ /**
+ * Tests if the wrapped connection is still available.
+ * @return boolean
+ */
+ protected boolean hasConnection() {
+ return wrappedConnection != null;
+ }
+
+ /**
+ * Obtains the wrapped connection.
+ *
+ * @return the wrapped connection
+ */
+ OperatedClientConnection getXWrappedConnection() {
+ return wrappedConnection;
+ }
+
+ /**
+ * Detaches this adapter from the wrapped connection.
+ * This adapter becomes useless.
+ */
+ private void detach() {
+ this.wrappedConnection = null;
+ this.poolEntry = null;
+ }
+
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public void open(HostConfiguration route,
+ HttpContext context, HttpParams params)
+ throws IOException {
+
+ assertAttached();
+ poolEntry.open(route, context, params);
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public void close() throws IOException {
+ if (poolEntry != null)
+ poolEntry.closing();
+
+ if (hasConnection()) {
+ wrappedConnection.close();
+ } else {
+ // do nothing
+ }
+ }
+
+ // non-javadoc, see interface HttpConnection
+ public void shutdown() throws IOException {
+ if (poolEntry != null)
+ poolEntry.closing();
+
+ if (hasConnection()) {
+ wrappedConnection.shutdown();
+ } else {
+ // do nothing
+ }
+ }
+
+
+
+
+
+/*
+ //===========================
+ // HttpHostConnection methods
+ //===========================
+ public void setHttpConnectionManager(final HttpConnectionManager httpConnectionManager) {
+ if (hasConnection()) {
+ wrappedConnection.setHttpConnectionManager(httpConnectionManager);
+ } else {
+ // do nothing
+ }
+ }
+
+ public boolean isLocked() {
+ if (hasConnection()) {
+ return wrappedConnection.isLocked();
+ } else {
+ return false;
+ }
+ }
+
+ public void setLocked(boolean locked) {
+ if (hasConnection()) {
+ wrappedConnection.setLocked(locked);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void releaseConnection() {
+ HttpHostConnection conn = this.wrappedConnection;
+ if (conn != null && !conn.isLocked()) {
+ this.wrappedConnection = null;
+ conn.releaseConnection();
+ } else {
+ // do nothing
+ }
+ }
+
+ public HostConfiguration getHostConfiguration() {
+ if (hasConnection()) {
+ return wrappedConnection.getHostConfiguration();
+ } else {
+ return null;
+ }
+ }
+
+ public void open(final HttpParams params) throws IOException {
+ if (hasConnection()) {
+ wrappedConnection.open(params);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void tunnelCreated(final HttpParams params) throws IOException {
+ if (hasConnection()) {
+ wrappedConnection.tunnelCreated(params);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setSocketTimeout(int timeout) throws SocketException {
+ if (hasConnection()) {
+ wrappedConnection.setSocketTimeout(timeout);
+ } else {
+ // do nothing
+ }
+ }
+
+ public HttpResponse getLastResponse() {
+ if (hasConnection()) {
+ return wrappedConnection.getLastResponse();
+ } else {
+ return null;
+ }
+ }
+
+ public void setLastResponse(final HttpResponse response) {
+ if (hasConnection()) {
+ wrappedConnection.setLastResponse(response);
+ } else {
+ // do nothing
+ }
+ }
+*/
+ } // class HttpConnectionAdapter
+
+}
+
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/httpcomponents/httpclient/trunk/src/java/org/apache/http/conn/impl/ThreadSafeClientConnManager.java
------------------------------------------------------------------------------
svn:mime-type = text/plain