jsdever 2003/02/19 17:03:57
Modified: httpclient/src/java/org/apache/commons/httpclient
MultiThreadedHttpConnectionManager.java
httpclient/src/test/org/apache/commons/httpclient
TestHttpConnectionManager.java
Log:
Fix for the Request/Response race condition bug.
Bugzilla: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=13463
Contributed by: Micheal Becke
This fix was taken from the bug as attachement 4908.
Mike became a committer today!
Revision Changes Path
1.9 +396 -11
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
Index: MultiThreadedHttpConnectionManager.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- MultiThreadedHttpConnectionManager.java 30 Jan 2003 05:01:54 -0000 1.8
+++ MultiThreadedHttpConnectionManager.java 20 Feb 2003 01:03:56 -0000 1.9
@@ -63,14 +63,19 @@
package org.apache.commons.httpclient;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
+import java.net.SocketException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
+import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -95,13 +100,13 @@
* HostConnectionPool}s
*/
private final Map mapHosts = new HashMap();
-
+
/** Maximum number of connections allowed */
private int maxConnections = 2; // Per RFC 2616 sec 8.1.4
/** mapping from reference to hostConfiguration */
private final Map referenceToHostConfig;
-
+
/**
* the reference queue used to track when HttpConnections are lost to the
* garbage collector
@@ -165,7 +170,7 @@
*/
public HttpConnection getConnection(HostConfiguration hostConfiguration,
long timeout) throws HttpException {
-
+
LOG.trace("enter HttpConnectionManager.getConnection(HostConfiguration,
long)");
if (hostConfiguration == null) {
@@ -184,7 +189,9 @@
hostConfiguration, timeout
);
- return conn;
+ // wrap the connection in an adapter so we can ensure it is used
+ // only once
+ return new HttpConnectionAdapter(conn);
}
/**
@@ -305,9 +312,17 @@
public void releaseConnection(HttpConnection conn) {
LOG.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
+ if (conn instanceof HttpConnectionAdapter) {
+ // connections given out are wrapped in an HttpConnectionAdapter
+ conn = ((HttpConnectionAdapter) conn).getWrappedConnection();
+ } else {
+ // this is okay, when an HttpConnectionAdapter is released
+ // is releases the real connection
+ }
+
// make sure that the response has been read.
SimpleHttpConnectionManager.finishLastResponse(conn);
-
+
HostConfiguration connectionConfiguration = new HostConfiguration();
connectionConfiguration.setHost(conn.getHost(),
conn.getPort(), conn.getProtocol());
@@ -320,8 +335,7 @@
+ connectionConfiguration);
}
- final HostConnectionPool listConnections
- = getConnectionPool(connectionConfiguration);
+ final HostConnectionPool listConnections =
getConnectionPool(connectionConfiguration);
synchronized (listConnections) {
// Put the connect back in the available list and notify a waiter
listConnections.freeConnections.addFirst(conn);
@@ -342,7 +356,7 @@
private class HostConnectionPool {
/** The list of free connections */
private LinkedList freeConnections = new LinkedList();
-
+
/** The number of created connections */
private int numConnections = 0;
}
@@ -460,4 +474,375 @@
}
}
+ /**
+ * An HttpConnection wrapper that ensures a connection cannot be used
+ * once released.
+ */
+ private static class HttpConnectionAdapter extends HttpConnection {
+
+ // the wrapped connection
+ private HttpConnection wrappedConnection;
+
+ /**
+ * Creates a new HttpConnectionAdapter.
+ * @param connection the connection to be wrapped
+ */
+ public HttpConnectionAdapter(HttpConnection connection) {
+ super(connection.getHost(), connection.getPort(),
connection.getProtocol());
+ this.wrappedConnection = connection;
+ }
+
+ /**
+ * Tests if the wrapped connection is still available.
+ * @return boolean
+ */
+ protected boolean hasConnection() {
+ return wrappedConnection != null;
+ }
+
+ /**
+ * @return HttpConnection
+ */
+ HttpConnection getWrappedConnection() {
+ return wrappedConnection;
+ }
+
+ public void close() {
+ if (hasConnection()) {
+ wrappedConnection.close();
+ } else {
+ // do nothing
+ }
+ }
+
+ public String getHost() {
+ if (hasConnection()) {
+ return wrappedConnection.getHost();
+ } else {
+ return null;
+ }
+ }
+
+ public HttpConnectionManager getHttpConnectionManager() {
+ if (hasConnection()) {
+ return wrappedConnection.getHttpConnectionManager();
+ } else {
+ return null;
+ }
+ }
+
+ public InputStream getLastResponseInputStream() {
+ if (hasConnection()) {
+ return wrappedConnection.getLastResponseInputStream();
+ } else {
+ return null;
+ }
+ }
+
+ public int getPort() {
+ if (hasConnection()) {
+ return wrappedConnection.getPort();
+ } else {
+ return -1;
+ }
+ }
+
+ public Protocol getProtocol() {
+ if (hasConnection()) {
+ return wrappedConnection.getProtocol();
+ } else {
+ return null;
+ }
+ }
+
+ public String getProxyHost() {
+ if (hasConnection()) {
+ return wrappedConnection.getProxyHost();
+ } else {
+ return null;
+ }
+ }
+
+ public int getProxyPort() {
+ if (hasConnection()) {
+ return wrappedConnection.getProxyPort();
+ } else {
+ return -1;
+ }
+ }
+
+ public OutputStream getRequestOutputStream()
+ throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.getRequestOutputStream();
+ } else {
+ return null;
+ }
+ }
+
+ public OutputStream getRequestOutputStream(boolean useChunking)
+ throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.getRequestOutputStream(useChunking);
+ } else {
+ return null;
+ }
+ }
+
+ public InputStream getResponseInputStream()
+ throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.getResponseInputStream();
+ } else {
+ return null;
+ }
+ }
+
+ public InputStream getResponseInputStream(HttpMethod method)
+ throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.getResponseInputStream(method);
+ } else {
+ return null;
+ }
+ }
+
+ public boolean isOpen() {
+ if (hasConnection()) {
+ return wrappedConnection.isOpen();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isProxied() {
+ if (hasConnection()) {
+ return wrappedConnection.isProxied();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isResponseAvaliable() throws IOException {
+ if (hasConnection()) {
+ return wrappedConnection.isResponseAvaliable();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isSecure() {
+ if (hasConnection()) {
+ return wrappedConnection.isSecure();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isTransparent() {
+ if (hasConnection()) {
+ return wrappedConnection.isTransparent();
+ } else {
+ return false;
+ }
+ }
+
+ public void open() throws IOException {
+ if (hasConnection()) {
+ wrappedConnection.open();
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void print(String data)
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.print(data);
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void printLine()
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.printLine();
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void printLine(String data)
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.printLine(data);
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public String readLine() throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.readLine();
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void releaseConnection() {
+ if (hasConnection()) {
+ HttpConnection wrappedConnection = this.wrappedConnection;
+ this.wrappedConnection = null;
+ wrappedConnection.releaseConnection();
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setConnectionTimeout(int timeout) {
+ if (hasConnection()) {
+ wrappedConnection.setConnectionTimeout(timeout);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setHost(String host) throws IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setHost(host);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setHttpConnectionManager(HttpConnectionManager
httpConnectionManager) {
+ if (hasConnection()) {
+ wrappedConnection.setHttpConnectionManager(httpConnectionManager);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setLastResponseInputStream(InputStream inStream) {
+ if (hasConnection()) {
+ wrappedConnection.setLastResponseInputStream(inStream);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setPort(int port) throws IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setPort(port);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setProtocol(Protocol protocol) {
+ if (hasConnection()) {
+ wrappedConnection.setProtocol(protocol);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setProxyHost(String host) throws IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setProxyHost(host);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setProxyPort(int port) throws IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setProxyPort(port);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setSecure(boolean secure) throws IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setSecure(secure);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void setSoTimeout(int timeout)
+ throws SocketException, IllegalStateException {
+ if (hasConnection()) {
+ wrappedConnection.setSoTimeout(timeout);
+ } else {
+ // do nothing
+ }
+ }
+
+ public void shutdownOutput() {
+ if (hasConnection()) {
+ wrappedConnection.shutdownOutput();
+ } else {
+ // do nothing
+ }
+ }
+
+ public void tunnelCreated() throws IllegalStateException, IOException {
+ if (hasConnection()) {
+ wrappedConnection.tunnelCreated();
+ } else {
+ // do nothing
+ }
+ }
+
+ public boolean waitForResponse(long timeout_ms)
+ throws IOException, IllegalStateException {
+ if (hasConnection()) {
+ return wrappedConnection.waitForResponse(timeout_ms);
+ } else {
+ return false;
+ }
+ }
+
+ public void write(byte[] data, int offset, int length)
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.write(data, offset, length);
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void write(byte[] data)
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.write(data);
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void writeLine()
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.writeLine();
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ public void writeLine(byte[] data)
+ throws IOException, IllegalStateException, HttpRecoverableException {
+ if (hasConnection()) {
+ wrappedConnection.writeLine(data);
+ } else {
+ throw new IllegalStateException("Connection has been released");
+ }
+ }
+
+ }
+
}
+
1.5 +4 -32
jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java
Index: TestHttpConnectionManager.java
===================================================================
RCS file:
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- TestHttpConnectionManager.java 23 Jan 2003 22:48:27 -0000 1.4
+++ TestHttpConnectionManager.java 20 Feb 2003 01:03:57 -0000 1.5
@@ -298,34 +298,6 @@
}
}
-
- public void testGetMultipleConnections() {
- HttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
-
- HostConfiguration hostConfig1 = new HostConfiguration();
- hostConfig1.setHost("www.nosuchserver.com", 80, "http");
-
- HostConfiguration hostConfig2 = new HostConfiguration();
- hostConfig2.setHost("www.nosuchserver.com", -1, "http");
-
- HostConfiguration hostConfig3 = new HostConfiguration();
- hostConfig3.setHost("www.nosuchserver.com", -1, "http");
-
- // Create a new connection
- HttpConnection conn1 = mgr.getConnection(hostConfig1);
- // Release the connection
- mgr.releaseConnection(conn1);
-
- // Get the same connection again
- HttpConnection conn2 = mgr.getConnection(hostConfig2);
- assertEquals("Same connection", conn1, conn2);
- // don't release yet
-
- // Get another new connection
- HttpConnection conn3 = mgr.getConnection(hostConfig3);
- assertTrue(conn2 != conn3);
-
- }
public void testTimeout()
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]