Index: java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java =================================================================== retrieving revision 1.33 diff -u -r1.33 MultiThreadedHttpConnectionManager.java --- java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java 22 Feb 2004 18:08:46 -0000 1.33 +++ java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java 8 Mar 2004 04:19:17 -0000 @@ -47,6 +47,7 @@ import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.util.IdleConnectionHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -289,6 +290,11 @@ final HttpConnection conn = doGetConnection(hostConfiguration, timeout); + // register the connection with the timeout handler + IdleConnectionHandler idleConnectionHandler = (IdleConnectionHandler) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_HANDLER); + idleConnectionHandler.remove(conn); + // wrap the connection in an adapter so we can ensure it is used // only once return new HttpConnectionAdapter(conn); @@ -468,6 +474,13 @@ SimpleHttpConnectionManager.finishLastResponse(conn); connectionPool.freeConnection(conn); + + // register the connection with the timeout handler + IdleConnectionHandler idleConnectionHandler = (IdleConnectionHandler) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_HANDLER); + Integer idleTimeout = (Integer) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_TIMEOUT); + idleConnectionHandler.add(conn, idleTimeout.intValue()); } /** Index: java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java =================================================================== retrieving revision 1.17 diff -u -r1.17 SimpleHttpConnectionManager.java --- java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java 22 Feb 2004 18:08:46 -0000 1.17 +++ java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java 8 Mar 2004 04:19:18 -0000 @@ -35,6 +35,7 @@ import java.io.InputStream; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.commons.httpclient.util.IdleConnectionHandler; /** * A connection manager that provides access to a single HttpConnection. This @@ -129,6 +130,11 @@ } } + // remove the connection from the timeout handler + IdleConnectionHandler idleConnectionHandler = (IdleConnectionHandler) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_HANDLER); + idleConnectionHandler.remove(httpConnection); + return httpConnection; } @@ -151,6 +157,13 @@ } finishLastResponse(httpConnection); + + // register the connection with the timeout handler + IdleConnectionHandler idleConnectionHandler = (IdleConnectionHandler) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_HANDLER); + Integer idleTimeout = (Integer) + getParams().getParameter(HttpConnectionManagerParams.IDLE_CONNECTION_TIMEOUT); + idleConnectionHandler.add(httpConnection, idleTimeout.intValue()); } /** Index: java/org/apache/commons/httpclient/params/DefaultHttpParamsFactory.java =================================================================== retrieving revision 1.7 diff -u -r1.7 DefaultHttpParamsFactory.java --- java/org/apache/commons/httpclient/params/DefaultHttpParamsFactory.java 22 Feb 2004 18:08:48 -0000 1.7 +++ java/org/apache/commons/httpclient/params/DefaultHttpParamsFactory.java 8 Mar 2004 04:19:18 -0000 @@ -38,6 +38,7 @@ import org.apache.commons.httpclient.SimpleHttpConnectionManager; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.util.DateParser; +import org.apache.commons.httpclient.util.IdleConnectionHandler; @@ -73,6 +74,13 @@ params.setHttpElementCharset("US-ASCII"); params.setContentCharset("ISO-8859-1"); + IdleConnectionHandler idleConnectionHandler = new IdleConnectionHandler(); + idleConnectionHandler.setTimeoutGranularity(500); + params.setParameter( + HttpConnectionManagerParams.IDLE_CONNECTION_HANDLER, + idleConnectionHandler); + params.setParameter(HttpConnectionManagerParams.IDLE_CONNECTION_TIMEOUT, new Integer(5000)); + ArrayList datePatterns = new ArrayList(); datePatterns.addAll( Arrays.asList( Index: java/org/apache/commons/httpclient/params/HttpConnectionManagerParams.java =================================================================== retrieving revision 1.3 diff -u -r1.3 HttpConnectionManagerParams.java --- java/org/apache/commons/httpclient/params/HttpConnectionManagerParams.java 22 Feb 2004 18:08:48 -0000 1.3 +++ java/org/apache/commons/httpclient/params/HttpConnectionManagerParams.java 8 Mar 2004 04:19:19 -0000 @@ -50,7 +50,7 @@ * This parameter expects a value of type {@link Integer}. *

*/ - public static String MAX_HOST_CONNECTIONS = "http.connection-manager.max-per-host"; + public static final String MAX_HOST_CONNECTIONS = "http.connection-manager.max-per-host"; /** * Defines the maximum number of connections allowed overall @@ -58,6 +58,22 @@ * This parameter expects a value of type {@link Integer}. *

*/ - public static String MAX_TOTAL_CONNECTIONS = "http.connection-manager.max-total"; + public static final String MAX_TOTAL_CONNECTIONS = "http.connection-manager.max-total"; + + /** + * Defines the {@link org.apache.commons.httpclient.util.IdleConnectionHandler} to be used + * by the connection manager. + *

This parameter expects a value of type + * {@link org.apache.commons.httpclient.util.IdleConnectionHandler}.

+ */ + public static final String IDLE_CONNECTION_HANDLER = + "http.connection-manager.idle-connection-handler"; + /** + * Defines the default idle connection timeout. + *

This parameter expects a value of type {@link Integer}.

+ */ + public static final String IDLE_CONNECTION_TIMEOUT = + "http.connection-manager.idle-connection-timeout"; + } Index: src/java/org/apache/commons/httpclient/util/IdleConnectionHandler.java =================================================================== RCS file: src/java/org/apache/commons/httpclient/util/IdleConnectionHandler.java diff -N src/java/org/apache/commons/httpclient/util/IdleConnectionHandler.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/java/org/apache/commons/httpclient/util/IdleConnectionHandler.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,175 @@ +/* + * $Header: $ + * $Revision: $ + * $Date: $ + * + * ==================================================================== + * + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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 + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ +package org.apache.commons.httpclient.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.TreeMap; + +import org.apache.commons.httpclient.HttpConnection; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Handles closing idle connections. + */ +public class IdleConnectionHandler { + + private static final Log LOG = LogFactory.getLog(IdleConnectionHandler.class); + + // holds connections to be tracked + private HashSet connections = new HashSet(); + + // holds connections ordered by absolute timeout + private TreeMap timeoutToConnection = new TreeMap(); + + private int timeoutGranularity = 500; + + private Thread timeoutThread; + + /** + * + */ + public IdleConnectionHandler() { + super(); + } + + /** + * Registers the given connection with this handles. The connection will be closed unless + * it is removed within the given timeout. + * + * @param connection + * @param idleTimeout + * + * @see #remove(HttpConnection) + */ + public void add(HttpConnection connection, int idleTimeout) { + + Long timeout = new Long(System.currentTimeMillis() + idleTimeout); + + LOG.debug("Adding connection with timeout: " + timeout); + + synchronized (timeoutToConnection) { + if (timeoutThread == null) { + timeoutThread = new Thread() { + public void run() { + closeConnectionLoop(); + } + }; + timeoutThread.setDaemon(true); + timeoutThread.start(); + } + List tempList = (List) timeoutToConnection.get(timeout); + if (tempList == null) { + tempList = new ArrayList(); + timeoutToConnection.put(timeout, tempList); + } + tempList.add(connection); + connections.add(connection); + } + } + + /** + * Removes the given connection from the list of connections to be closed when idle. + * @param connection + */ + public void remove(HttpConnection connection) { + synchronized (timeoutToConnection) { + // TODO what about connections that are removed, and then readded before + // the original timeout has occurred? + connections.remove(connection); + } + } + + private void closeConnectionLoop() { + while (true) { + synchronized(Thread.currentThread()) { + try { + Thread.currentThread().wait(IdleConnectionHandler.this.timeoutGranularity); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + closeIdleConnections(); + } + } + + private void closeIdleConnections() { + + long currentTime = System.currentTimeMillis(); + + LOG.debug("Checking for connections, currentTime: " + currentTime); + + synchronized (timeoutToConnection) { + Iterator timeoutIter = timeoutToConnection.keySet().iterator(); + + while (timeoutIter.hasNext()) { + Long connectionTimeout = (Long) timeoutIter.next(); + if (connectionTimeout.longValue() <= currentTime) { + LOG.debug("Connection timed out: " + connectionTimeout); + List tempList = (List) timeoutToConnection.get(connectionTimeout); + Iterator connectionIter = tempList.iterator(); + while (connectionIter.hasNext()) { + HttpConnection conn = (HttpConnection) connectionIter.next(); + if (connections.remove(conn)) { + LOG.debug("Closing connection"); + conn.close(); + } else { + LOG.debug("NOT closing connection"); + } + } + timeoutIter.remove(); + } else { + break; + } + } + } + + } + + /** + * @return Returns the timeoutGranularity. + */ + public int getTimeoutGranularity() { + return timeoutGranularity; + } + /** + * Sets the idle connection timeout granularity. The value determines how often this + * handler tests for idle connections. + * + * @param timeoutGranularity The timeoutGranularity to set. + */ + public void setTimeoutGranularity(int timeoutGranularity) { + this.timeoutGranularity = timeoutGranularity; + } +} Index: src/test/org/apache/commons/httpclient/TestIdleConnectionQueue.java =================================================================== RCS file: src/test/org/apache/commons/httpclient/TestIdleConnectionQueue.java diff -N src/test/org/apache/commons/httpclient/TestIdleConnectionQueue.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/test/org/apache/commons/httpclient/TestIdleConnectionQueue.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,115 @@ +/* + * $Header: $ + * $Revision: $ + * $Date: $ + * + * ==================================================================== + * + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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 + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ +package org.apache.commons.httpclient; + +import org.apache.commons.httpclient.util.IdleConnectionHandler; + +/** + */ +public class TestIdleConnectionQueue extends TestNoHostBase { + /** + * + */ + public TestIdleConnectionQueue() { + super(); + } + /** + * @param arg0 + */ + public TestIdleConnectionQueue(String arg0) { + super(arg0); + } + + public void testClose() { + + TimeoutHttpConnection connection = new TimeoutHttpConnection(); + + IdleConnectionHandler handler = new IdleConnectionHandler(); + handler.setTimeoutGranularity(100); + + handler.add(connection, 100); + + synchronized(this) { + try { + this.wait(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + assertTrue("Connected not closed", connection.isClosed()); + + connection.setClosed(false); + + handler.add(connection, 100); + handler.remove(connection); + + synchronized(this) { + try { + this.wait(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + assertFalse("Connection closed", connection.isClosed()); + } + + private static class TimeoutHttpConnection extends HttpConnection { + + private boolean closed = false;; + + public TimeoutHttpConnection() { + super("fake-host", 80); + } + + /** + * @return Returns the closed. + */ + public boolean isClosed() { + return closed; + } + /** + * @param closed The closed to set. + */ + public void setClosed(boolean closed) { + this.closed = closed; + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.HttpConnection#close() + */ + public void close() { + closed = true; + } + } + +}