From f2260d22e6219f7142cd7d7cf3db62cc8e4ed6a6 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Fri, 8 Dec 2006 20:59:42 +0000 Subject: [PATCH] Ported HttpConfiguration, HttpConnection, HttpConnectionManager and SimpleHttpConnectionManager from HttpClient 3.x to the new API git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@484777 13f79535-47bb-0310-9956-ffa450edef68 --- .../conn/ConnectionPoolTimeoutException.java | 60 +++ .../apache/http/conn/HostConfiguration.java | 358 ++++++++++++++++++ .../http/conn/HttpConnectionManager.java | 99 +++++ .../apache/http/conn/HttpHostConnection.java | 48 +++ .../conn/impl/DefaultHttpHostConnection.java | 274 ++++++++++++++ .../impl/SimpleHttpConnectionManager.java | 232 ++++++++++++ 6 files changed, 1071 insertions(+) create mode 100644 src/java/org/apache/http/conn/ConnectionPoolTimeoutException.java create mode 100644 src/java/org/apache/http/conn/HostConfiguration.java create mode 100644 src/java/org/apache/http/conn/HttpConnectionManager.java create mode 100644 src/java/org/apache/http/conn/HttpHostConnection.java create mode 100644 src/java/org/apache/http/conn/impl/DefaultHttpHostConnection.java create mode 100644 src/java/org/apache/http/conn/impl/SimpleHttpConnectionManager.java diff --git a/src/java/org/apache/http/conn/ConnectionPoolTimeoutException.java b/src/java/org/apache/http/conn/ConnectionPoolTimeoutException.java new file mode 100644 index 000000000..d5e5b8188 --- /dev/null +++ b/src/java/org/apache/http/conn/ConnectionPoolTimeoutException.java @@ -0,0 +1,60 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 1999-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 + * . + * + */ + +package org.apache.http.conn; + +/** + * A timeout while connecting waiting for an available connection + * from an HttpConnectionManager. + * + * @author Laura Werner + * + * @since 3.0 + */ +public class ConnectionPoolTimeoutException extends ConnectTimeoutException { + + private static final long serialVersionUID = -7898874842020245128L; + + /** + * Creates a ConnectTimeoutException with a null detail message. + */ + public ConnectionPoolTimeoutException() { + super(); + } + + /** + * Creates a ConnectTimeoutException with the specified detail message. + * + * @param message The exception detail message + */ + public ConnectionPoolTimeoutException(String message) { + super(message); + } + +} diff --git a/src/java/org/apache/http/conn/HostConfiguration.java b/src/java/org/apache/http/conn/HostConfiguration.java new file mode 100644 index 000000000..ea73b4427 --- /dev/null +++ b/src/java/org/apache/http/conn/HostConfiguration.java @@ -0,0 +1,358 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 2002-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 + * . + * + */ + +package org.apache.http.conn; + +import java.net.InetAddress; + +import org.apache.http.HttpHost; +import org.apache.http.util.LangUtils; + +/** + * Holds all of the variables needed to describe an HTTP connection to a host. This includes + * remote host, port and protocol, proxy host and port, local address, and virtual host. + * + * @author Michael Becke + * @author Mike Bowler + * @author Oleg Kalnichevski + * @author Laura Werner + * + * @since 2.0 + */ +public class HostConfiguration implements Cloneable { + + /** + * A value to represent any host configuration, instead of using something like + * null. This value should be treated as immutable and only used in + * lookups and other such places to represent "any" host config. + */ + public static final HostConfiguration ANY_HOST_CONFIGURATION = new HostConfiguration(); + + /** The host to use. */ + private HttpHost host = null; + + /** The host name of the proxy server */ + private HttpHost proxyHost = null; + + /** The local address to use when creating the socket, or null to use the default */ + private InetAddress localAddress = null; + + /** + * Constructor for HostConfiguration. + */ + public HostConfiguration() { + super(); + } + + /** + * Copy constructor for HostConfiguration + * + * @param hostConfiguration the hostConfiguration to copy + */ + public HostConfiguration (final HostConfiguration hostConfiguration) { + // wrap all of the assignments in a synchronized block to avoid + // having to negotiate the monitor for each method call + synchronized (hostConfiguration) { + if (hostConfiguration.host != null) { + this.host = new HttpHost(hostConfiguration.host); + } else { + this.host = null; + } + if (hostConfiguration.proxyHost != null) { + this.proxyHost = new HttpHost(hostConfiguration.proxyHost); + } else { + this.proxyHost = null; + } + this.localAddress = hostConfiguration.getLocalAddress(); + } + } + + /** + * @see java.lang.Object#clone() + */ + public Object clone() { + return new HostConfiguration(this); + } + + /** + * @see java.lang.Object#toString() + */ + public synchronized String toString() { + + boolean appendComma = false; + StringBuffer b = new StringBuffer(50); + b.append("HostConfiguration["); + + if (this.host != null) { + appendComma = true; + b.append("host=").append(this.host); + } + if (this.proxyHost != null) { + if (appendComma) { + b.append(", "); + } else { + appendComma = true; + } + b.append("proxyHost=").append(this.proxyHost); + } + if (this.localAddress != null) { + if (appendComma) { + b.append(", "); + } else { + appendComma = true; + } + b.append("localAddress=").append(this.localAddress); + if (appendComma) { + b.append(", "); + } else { + appendComma = true; + } + } + b.append("]"); + return b.toString(); + } + + /** + * Sets the given host + * + * @param host the host + */ + public synchronized void setHost(final HttpHost host) { + this.host = host; + } + + /** + * Sets the given host, port and protocol + * + * @param host the host(IP or DNS name) + * @param port The port + * @param protocol The protocol. + */ + public synchronized void setHost(final String host, int port, final String protocol) { + this.host = new HttpHost(host, port, protocol); + } + + /** + * Sets the given host and port. Uses the default protocol "http". + * + * @param host the host(IP or DNS name) + * @param port The port + */ + public synchronized void setHost(final String host, int port) { + setHost(host, port, "http"); + } + + /** + * Set the given host. Uses the default protocol("http") and its port. + * + * @param host The host(IP or DNS name). + */ + public synchronized void setHost(final String host) { + setHost(host, -1, "http"); + } + + /** + * Return the host url. + * + * @return The host url. + */ + public synchronized String getHostURL() { + if (this.host == null) { + throw new IllegalStateException("Host must be set to create a host URL"); + } else { + return this.host.toURI(); + } + } + + /** + * Returns the target host. + * + * @return the target host, or null if not set + * + * @see #isHostSet() + */ + public synchronized HttpHost getHost() { + return this.host; + } + + /** + * Returns the host name. + * + * @return the host(IP or DNS name), or null if not set + * + * @see #isHostSet() + */ + public synchronized String getHostName() { + if (this.host != null) { + return this.host.getHostName(); + } else { + return null; + } + } + + /** + * Returns the port. + * + * @return the host port, or -1 if not set + * + * @see #isHostSet() + */ + public synchronized int getPort() { + if (this.host != null) { + return this.host.getPort(); + } else { + return -1; + } + } + + /** + * Returns the protocol. + * @return The protocol. + */ + public synchronized Scheme getScheme() { + if (this.host != null) { + return Scheme.getScheme(this.host.getSchemeName()); + } else { + return null; + } + } + + /** + * Sets the given proxy host + * + * @param proxyHost the proxy host + */ + public synchronized void setProxyHost(final HttpHost proxyHost) { + this.proxyHost = proxyHost; + } + + /** + * Set the proxy settings. + * @param proxyHost The proxy host + * @param proxyPort The proxy port + */ + public synchronized void setProxy(final String proxyHost, int proxyPort) { + this.proxyHost = new HttpHost(proxyHost, proxyPort); + } + + /** + * Returns the proxyHost. + * + * @return the proxy host, or null if not set + * + * @see #isProxySet() + */ + public synchronized HttpHost getProxyHost() { + return this.proxyHost; + } + + /** + * Returns the proxyHost. + * + * @return the proxy host, or null if not set + * + * @see #isProxySet() + */ + public synchronized String getProxyHostName() { + if (this.proxyHost != null) { + return this.proxyHost.getHostName(); + } else { + return null; + } + } + + /** + * Returns the proxyPort. + * + * @return the proxy port, or -1 if not set + * + * @see #isProxySet() + */ + public synchronized int getProxyPort() { + if (this.proxyHost != null) { + return this.proxyHost.getPort(); + } else { + return -1; + } + } + + /** + * Set the local address to be used when creating connections. + * If this is unset, the default address will be used. + * This is useful for specifying the interface to use on multi-homed or clustered systems. + * + * @param localAddress the local address to use + */ + + public synchronized void setLocalAddress(InetAddress localAddress) { + this.localAddress = localAddress; + } + + /** + * Return the local address to be used when creating connections. + * If this is unset, the default address should be used. + * + * @return the local address to be used when creating Sockets, or null + */ + + public synchronized InetAddress getLocalAddress() { + return this.localAddress; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public synchronized boolean equals(final Object o) { + if (o instanceof HostConfiguration) { + // shortcut if we're comparing with ourselves + if (o == this) { + return true; + } + HostConfiguration that = (HostConfiguration) o; + return LangUtils.equals(this.host, that.host) + && LangUtils.equals(this.proxyHost, that.proxyHost) + && LangUtils.equals(this.localAddress, that.localAddress); + } else { + return false; + } + + } + + /** + * @see java.lang.Object#hashCode() + */ + public synchronized int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.host); + hash = LangUtils.hashCode(hash, this.proxyHost); + hash = LangUtils.hashCode(hash, this.localAddress); + return hash; + } + +} diff --git a/src/java/org/apache/http/conn/HttpConnectionManager.java b/src/java/org/apache/http/conn/HttpConnectionManager.java new file mode 100644 index 000000000..53e3eff8b --- /dev/null +++ b/src/java/org/apache/http/conn/HttpConnectionManager.java @@ -0,0 +1,99 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 1999-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 + * . + * + */ + +package org.apache.http.conn; + +/** + * An interface for classes that manage {@link HttpHostConnection}s. + * + * @author Michael Becke + * @author Mike Bowler + * @author Oleg Kalnichevski + * + * @since 2.0 + */ +public interface HttpConnectionManager { + + /** + * Gets an HttpConnection for a given host configuration. If a connection is + * not available this method will block until one is. + * + * The connection manager should be registered with any HttpConnection that + * is created. + * + * @param hostConfiguration the host configuration to use to configure the + * connection + * + * @return an HttpConnection for the given configuration + */ + HttpHostConnection getConnection(HostConfiguration hostConfiguration); + + /** + * Gets an HttpConnection for a given host configuration. If a connection is + * not available, this method will block for at most the specified number of + * milliseconds or until a connection becomes available. + * + * The connection manager should be registered with any HttpConnection that + * is created. + * + * @param hostConfiguration the host configuration to use to configure the + * connection + * @param timeout - the time (in milliseconds) to wait for a connection to + * become available, 0 to specify an infinite timeout + * + * @return an HttpConnection for the given configuraiton + * + * @throws ConnectionPoolTimeoutException if no connection becomes available before the + * timeout expires + * + * @since 3.0 + */ + HttpHostConnection getConnection(HostConfiguration hostConfiguration, long timeout) + throws ConnectionPoolTimeoutException; + + /** + * Releases the given HttpConnection for use by other requests. + * + * @param conn - The HttpHostConnection to make available. + */ + void releaseConnection(HttpHostConnection conn); + + /** + * Closes connections that have been idle for at least the given amount of time. Only + * connections that are currently owned, not checked out, are subject to idle timeouts. + * + * @param idleTimeout the minimum idle time, in milliseconds, for connections to be closed + * + * @since 3.0 + */ + void closeIdleConnections(long idleTimeout); + + void shutdown(); + +} diff --git a/src/java/org/apache/http/conn/HttpHostConnection.java b/src/java/org/apache/http/conn/HttpHostConnection.java new file mode 100644 index 000000000..cfbfd471f --- /dev/null +++ b/src/java/org/apache/http/conn/HttpHostConnection.java @@ -0,0 +1,48 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 1999-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 + * . + * + */ + +package org.apache.http.conn; + +import java.io.IOException; +import java.net.SocketException; + +import org.apache.http.HttpClientConnection; +import org.apache.http.params.HttpParams; + +public interface HttpHostConnection extends HttpClientConnection { + + HostConfiguration getHostConfiguration(); + + void open(HttpParams params) throws IOException; + + void tunnelCreated(HttpParams params) throws IOException; + + void setSocketTimeout(int timeout) throws SocketException; + +} diff --git a/src/java/org/apache/http/conn/impl/DefaultHttpHostConnection.java b/src/java/org/apache/http/conn/impl/DefaultHttpHostConnection.java new file mode 100644 index 000000000..bd35ca317 --- /dev/null +++ b/src/java/org/apache/http/conn/impl/DefaultHttpHostConnection.java @@ -0,0 +1,274 @@ +/* + * $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 + * . + * + */ + +package org.apache.http.conn.impl; + +import java.io.IOException; +import java.net.Socket; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.conn.HostConfiguration; +import org.apache.http.conn.HttpConnectionManager; +import org.apache.http.conn.HttpHostConnection; +import org.apache.http.conn.Scheme; +import org.apache.http.conn.SecureSocketFactory; +import org.apache.http.conn.SocketFactory; +import org.apache.http.impl.SocketHttpClientConnection; +import org.apache.http.params.HttpParams; + +/** + * Default {@link HttpHostConnection} implementation. + * + * @author Rod Waldhoff + * @author Sean C. Sullivan + * @author Ortwin Glueck + * @author Jeff Dever + * @author Mike Bowler + * @author Oleg Kalnichevski + * @author Michael Becke + * @author Eric E Johnson + * @author Laura Werner + * + * @version $Revision$ $Date$ + */ +public class DefaultHttpHostConnection + extends SocketHttpClientConnection implements HttpHostConnection { + + private static final Log LOG = LogFactory.getLog(DefaultHttpHostConnection.class); + + /** the connection manager that created this connection or null */ + private final HttpConnectionManager manager; + + private HostConfiguration hostconf; + + /** flag to indicate if this connection can be released, if locked the connection cannot be + * released */ + private boolean locked = false; + + /** Whether the connection is open via a secure tunnel or not */ + private boolean tunnelEstablished = false; + + private HttpResponse lastResponse; + + public DefaultHttpHostConnection(final HttpConnectionManager manager) { + super(); + this.manager = manager; + } + + public void setHostConfiguration(final HostConfiguration hostconf) { + assertNotOpen(); + this.hostconf = hostconf; + } + + public HostConfiguration getHostConfiguration() { + return this.hostconf; + } + + /** + * Establishes a connection to the specified host and port + * (via a proxy if specified). + * The underlying socket is created from the {@link SocketFactory}. + * + * @throws IOException if an attempt to establish the connection results in an + * I/O error. + */ + public void open(final HttpParams params) + throws IllegalStateException, IOException { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + assertNotOpen(); + if (this.hostconf == null) { + throw new IllegalArgumentException("Host configuration is null"); + } + + HttpHost host = this.hostconf.getHost(); + if (host == null) { + throw new IllegalStateException("Target http host is null"); + } + HttpHost proxyHost = this.hostconf.getProxyHost(); + + if (LOG.isDebugEnabled()) { + if (proxyHost == null) { + LOG.debug("Open connection to " + host); + } else { + LOG.debug("Open connection to " + host + " via proxy " + proxyHost); + } + } + + // Determine the type of the connection + Scheme scheme = Scheme.getScheme(host.getSchemeName()); + SocketFactory socketFactory = scheme.getSocketFactory(); + boolean secure = (socketFactory instanceof SecureSocketFactory); + boolean proxied = (proxyHost != null); + + // Determine the target host + HttpHost target = null; + if (proxyHost != null) { + target = proxyHost; + } else { + target = host; + } + + // Create the socket + String hostname = target.getHostName(); + int port = target.getPort(); + if (port < 0) { + port = scheme.getDefaultPort(); + } + if (secure && proxied) { + scheme = Scheme.getScheme("http"); + socketFactory = scheme.getSocketFactory(); + } else { + scheme = Scheme.getScheme(target.getSchemeName()); + } + socketFactory = scheme.getSocketFactory(); + Socket socket = socketFactory.createSocket( + hostname, port, + this.hostconf.getLocalAddress(), 0, params); + + // Bind connection to the socket + bind(socket, params); + } + + /** + * Instructs the proxy to establish a secure tunnel to the host. The socket will + * be switched to the secure socket. Subsequent communication is done via the secure + * socket. The method can only be called once on a proxied secure connection. + * + * @throws IllegalStateException if connection is not secure and proxied or + * if the socket is already secure. + * @throws IOException if an attempt to establish the secure tunnel results in an + * I/O error. + */ + public void tunnelCreated(final HttpParams params) + throws IllegalStateException, IOException { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + assertOpen(); + if (this.tunnelEstablished) { + throw new IllegalStateException("Tunnel already established"); + } + HttpHost host = this.hostconf.getHost(); + if (host == null) { + throw new IllegalStateException("Target http host is null"); + } + HttpHost proxyHost = this.hostconf.getProxyHost(); + + if (LOG.isDebugEnabled()) { + LOG.debug("Secure tunnel to " + host); + } + + Scheme scheme = Scheme.getScheme(host.getSchemeName()); + SocketFactory socketFactory = scheme.getSocketFactory(); + boolean secure = (socketFactory instanceof SecureSocketFactory); + boolean proxied = (proxyHost != null); + + if (!secure || !proxied) { + throw new IllegalStateException( + "Connection must be secure " + + "and proxied to use this feature"); + } + + String hostname = host.getHostName(); + int port = host.getPort(); + if (port < 0) { + port = scheme.getDefaultPort(); + } + SecureSocketFactory securesocketFactory = (SecureSocketFactory) socketFactory; + + Socket tunnel = securesocketFactory.createSocket( + this.socket, hostname, port, true); + bind(tunnel, params); + this.tunnelEstablished = true; + } + + /** + * Returns the httpConnectionManager. + * @return HttpConnectionManager + */ + public HttpConnectionManager getHttpConnectionManager() { + return this.manager; + } + + /** + * Releases the connection. If the connection is locked or does not have a connection + * manager associated with it, this method has no effect. Note that it is completely safe + * to call this method multiple times. + */ + public void releaseConnection() { + if (locked) { + LOG.debug("Connection is locked. Call to releaseConnection() ignored."); + } else if (this.manager != null) { + LOG.debug("Releasing connection back to connection manager."); + this.manager.releaseConnection(this); + } else { + LOG.warn("HttpConnectionManager is null. Connection cannot be released."); + } + } + + /** + * Tests if the connection is locked. Locked connections cannot be released. + * An attempt to release a locked connection will have no effect. + * + * @return true if the connection is locked, false otherwise. + * + * @since 3.0 + */ + protected boolean isLocked() { + return locked; + } + + /** + * Locks or unlocks the connection. Locked connections cannot be released. + * An attempt to release a locked connection will have no effect. + * + * @param locked true to lock the connection, false to unlock + * the connection. + * + * @since 3.0 + */ + protected void setLocked(boolean locked) { + this.locked = locked; + } + + public HttpResponse getLastResponse() { + return this.lastResponse; + } + + public void setLastResponse(final HttpResponse lastResponse) { + this.lastResponse = lastResponse; + } + +} diff --git a/src/java/org/apache/http/conn/impl/SimpleHttpConnectionManager.java b/src/java/org/apache/http/conn/impl/SimpleHttpConnectionManager.java new file mode 100644 index 000000000..d2f573116 --- /dev/null +++ b/src/java/org/apache/http/conn/impl/SimpleHttpConnectionManager.java @@ -0,0 +1,232 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 2002-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 + * . + * + */ + +package org.apache.http.conn.impl; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.conn.HostConfiguration; +import org.apache.http.conn.HttpConnectionManager; +import org.apache.http.conn.HttpHostConnection; +import org.apache.http.impl.DefaultHttpParams; +import org.apache.http.params.HttpParams; + +/** + * A connection manager that provides access to a single HttpConnection. This + * manager makes no attempt to provide exclusive access to the contained + * HttpConnection. + * + * @author Michael Becke + * @author Eric Johnson + * @author Mike Bowler + * @author Oleg Kalnichevski + * @author Laura Werner + * + * @since 2.0 + */ +public class SimpleHttpConnectionManager implements HttpConnectionManager { + + private static final Log LOG = LogFactory.getLog(SimpleHttpConnectionManager.class); + + private static final String MISUSE_MESSAGE = + "SimpleHttpConnectionManager being used incorrectly. Be sure that" + + " HttpMethod.releaseConnection() is always called and that only one thread" + + " and/or method is using this connection manager at a time."; + + /** + * Since the same connection is about to be reused, make sure the + * previous request was completely processed, and if not + * consume it now. + * @param conn The connection + */ + private void finishLastResponse(DefaultHttpHostConnection conn) { + HttpResponse lastResponse = conn.getLastResponse(); + if (lastResponse != null) { + conn.setLastResponse(null); + HttpEntity entity = lastResponse.getEntity(); + if (entity != null) { + try { + entity.consumeContent(); + } catch (IOException ex) { + LOG.debug("I/O error consuming response content", ex); + } + } + } + } + + /** The http connection */ + protected DefaultHttpHostConnection httpConnection; + + /** + * Collection of parameters associated with this connection manager. + */ + private HttpParams params = new DefaultHttpParams(); + + /** + * The time the connection was made idle. + */ + private long idleStartTime = Long.MAX_VALUE; + + /** + * Used to test if {@link #httpConnection} is currently in use + * (i.e. checked out). This is only used as a sanity check to help + * debug cases where this connection manager is being used incorrectly. + * It will not be used to enforce thread safety. + */ + private volatile boolean inUse = false; + + private boolean alwaysClose = false; + + /** + * The connection manager created with this constructor will try to keep the + * connection open (alive) between consecutive requests if the alwaysClose + * parameter is set to false. Otherwise the connection manager will + * always close connections upon release. + * + * @param alwaysClose if set true, the connection manager will always + * close connections upon release. + */ + public SimpleHttpConnectionManager(boolean alwaysClose) { + super(); + this.alwaysClose = alwaysClose; + } + + /** + * The connection manager created with this constructor will always try to keep + * the connection open (alive) between consecutive requests. + */ + public SimpleHttpConnectionManager() { + super(); + } + + /** + * @see HttpConnectionManager#getConnection(HostConfiguration) + */ + public HttpHostConnection getConnection(HostConfiguration hostConfiguration) { + return getConnection(hostConfiguration, 0); + } + + private void closeConnection() { + try { + this.httpConnection.close(); + } catch (IOException ex) { + LOG.debug("I/O error closing connection", ex); + } + } + + /** + * This method always returns the same connection object. If the connection is already + * open, it will be closed and the new host configuration will be applied. + * + * @param hostConfiguration The host configuration specifying the connection + * details. + * @param timeout this parameter has no effect. The connection is always returned + * immediately. + * @since 3.0 + */ + public HttpHostConnection getConnection( + HostConfiguration hostConfiguration, long timeout) { + + if (httpConnection == null) { + httpConnection = new DefaultHttpHostConnection(this); + } else { + // make sure the host and proxy are correct for this connection + // close it and set the values if they are not + if (!hostConfiguration.equals(httpConnection)) { + if (httpConnection.isOpen()) { + closeConnection(); + } + httpConnection.setHostConfiguration(hostConfiguration); + } else { + finishLastResponse(httpConnection); + } + } + + // remove the connection from the timeout handler + idleStartTime = Long.MAX_VALUE; + + if (inUse) LOG.warn(MISUSE_MESSAGE); + inUse = true; + + return httpConnection; + } + + /** + * @see HttpConnectionManager#releaseConnection(org.apache.commons.httpclient.HttpConnection) + */ + public void releaseConnection(HttpHostConnection conn) { + if (conn != httpConnection) { + throw new IllegalStateException("Unexpected release of an unknown connection."); + } + if (this.alwaysClose) { + closeConnection(); + } else { + // make sure the connection is reuseable + finishLastResponse(httpConnection); + } + + inUse = false; + + // track the time the connection was made idle + idleStartTime = System.currentTimeMillis(); + } + + public HttpParams getParams() { + return this.params; + } + + public void setParams(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + this.params = params; + } + + /** + * @since 3.0 + */ + public void closeIdleConnections(long idleTimeout) { + long maxIdleTime = System.currentTimeMillis() - idleTimeout; + if (idleStartTime <= maxIdleTime) { + closeConnection(); + } + } + + /** + * since 3.1 + */ + public void shutdown() { + closeConnection(); + } + +} \ No newline at end of file