olegk 2004/04/13 14:47:29
Modified: httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl
EasySSLProtocolSocketFactory.java
StrictSSLProtocolSocketFactory.java
httpclient/src/java/org/apache/commons/httpclient
HttpConnection.java
httpclient/src/java/org/apache/commons/httpclient/auth
AuthPolicy.java CredentialsProvider.java
httpclient/src/java/org/apache/commons/httpclient/params
HttpConnectionManagerParams.java
httpclient/src/java/org/apache/commons/httpclient/protocol
DefaultProtocolSocketFactory.java
ProtocolSocketFactory.java
SSLProtocolSocketFactory.java
httpclient/src/test/org/apache/commons/httpclient
TestHttpConnection.java
TestHttpConnectionManager.java
Added: httpclient/src/java/org/apache/commons/httpclient/protocol
ControllerThreadSocketFactory.java
ReflectionSocketFactory.java
Log:
PR #28322 (Connection timeout logic redesign)
Changelog:
* CreateSocket method with timeout parameter added to the ProtocolSocketFactory
interface
* TimeoutController related code factored out of HttpConnection class and moved
into ControllerThreadSocketFactory helper class
* ReflectionSocketFactory helper class added. This factory encapsulates
reflection code to call JDK 1.4 Socket#connect method if supported
* All protocol socket factories now attempt to initially use
ReflectionSocketFactory if required to create a socket within a given limit of
time. If reflection fails protocol socket factories fall back onto the good ol'
ControllerThreadSocketFactory
Benefits:
* HttpConnection code got a lot cleaner
* When running in modern JREs expensive timeout controller thread per connection
attempt is no longer needed
* Ugly code intended to work around limitations of the older JREs is now
confined to a few helper classes that can be easily thrown away once we move
onto Java 1.4
Contributed by Oleg Kalnichevski
Reviewed by Michael Becke
Revision Changes Path
1.5 +64 -5 jakarta-commons/httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java
Index: EasySSLProtocolSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl/EasySSLProtocolSocketFactory.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- EasySSLProtocolSocketFactory.java 22 Feb 2004 18:08:45 -0000 1.4
+++ EasySSLProtocolSocketFactory.java 13 Apr 2004 21:47:28 -0000 1.5
@@ -36,6 +36,11 @@
import com.sun.net.ssl.SSLContext;
import com.sun.net.ssl.TrustManager;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.HttpClientError;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
+import org.apache.commons.httpclient.protocol.ReflectionSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -64,6 +69,7 @@
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class);
+ private static SSLContext SSL_CONTEXT_SINGLETON = null;
/**
* Constructor for EasySSLProtocolSocketFactory.
*
@@ -81,21 +87,26 @@
super();
}
- private static SSLSocketFactory getEasySSLSocketFactory() {
- SSLContext context = null;
+ private static SSLContext createEasySSLContext() {
try {
- context = SSLContext.getInstance("SSL");
+ SSLContext context = SSLContext.getInstance("SSL");
context.init(
null,
new TrustManager[] {new EasyX509TrustManager(null)},
null);
+ return context;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
- throw new RuntimeException(e.toString());
+ throw new HttpClientError(e.toString());
}
- return context.getSocketFactory();
}
+ private static SSLSocketFactory getEasySSLSocketFactory() {
+ if (SSL_CONTEXT_SINGLETON == null) {
+ SSL_CONTEXT_SINGLETON = createEasySSLContext();
+ }
+ return SSL_CONTEXT_SINGLETON.getSocketFactory();
+ }
/**
* @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
@@ -114,6 +125,54 @@
clientPort
);
return socket;
+ }
+
+ /**
+ * Attempts to get a new socket connection to the given host within the given time
limit.
+ * <p>
+ * This method employs several techniques to circumvent the limitations of older JREs
that
+ * do not support connect timeout. When running in JRE 1.4 or above reflection is used
to
+ * call Socket#connect(SocketAddress endpoint, int timeout) method. When executing
in older
+ * JREs a controller thread is executed. The controller thread attempts to create a
new socket
+ * within the given limit of time. If socket constructor does not return until the
timeout
+ * expires, the controller terminates and throws an {@link ConnectTimeoutException}
+ * </p>
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param clientHost the local host name/IP to bind the socket to
+ * @param clientPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ */
+ public Socket createSocket(
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort,
+ final HttpConnectionParams params
+ ) throws IOException, UnknownHostException, ConnectTimeoutException {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ if (timeout == 0) {
+ return createSocket(host, port, localAddress, localPort);
+ } else {
+ // To be eventually deprecated when migrated to Java 1.4 or above
+ Socket socket = ReflectionSocketFactory.createSocket(
+ "javax.net.ssl.SSLSocketFactory", host, port, localAddress, localPort,
timeout);
+ if (socket == null) {
+ socket = ControllerThreadSocketFactory.createSocket(
+ this, host, port, localAddress, localPort, timeout);
+ }
+ return socket;
+ }
}
/**
1.4 +56 -3 jakarta-commons/httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl/StrictSSLProtocolSocketFactory.java
Index: StrictSSLProtocolSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/contrib/org/apache/commons/httpclient/contrib/ssl/StrictSSLProtocolSocketFactory.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- StrictSSLProtocolSocketFactory.java 22 Feb 2004 18:08:45 -0000 1.3
+++ StrictSSLProtocolSocketFactory.java 13 Apr 2004 21:47:28 -0000 1.4
@@ -54,6 +54,10 @@
import javax.net.ssl.SSLSocketFactory;
import javax.security.cert.X509Certificate;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
+import org.apache.commons.httpclient.protocol.ReflectionSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -159,6 +163,55 @@
verifyHostname(sslSocket);
return sslSocket;
+ }
+
+ /**
+ * Attempts to get a new socket connection to the given host within the given time
limit.
+ * <p>
+ * This method employs several techniques to circumvent the limitations of older JREs
that
+ * do not support connect timeout. When running in JRE 1.4 or above reflection is used
to
+ * call Socket#connect(SocketAddress endpoint, int timeout) method. When executing
in older
+ * JREs a controller thread is executed. The controller thread attempts to create a
new socket
+ * within the given limit of time. If socket constructor does not return until the
timeout
+ * expires, the controller terminates and throws an {@link ConnectTimeoutException}
+ * </p>
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param clientHost the local host name/IP to bind the socket to
+ * @param clientPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ */
+ public Socket createSocket(
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort,
+ final HttpConnectionParams params
+ ) throws IOException, UnknownHostException, ConnectTimeoutException {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ if (timeout == 0) {
+ return createSocket(host, port, localAddress, localPort);
+ } else {
+ // To be eventually deprecated when migrated to Java 1.4 or above
+ SSLSocket sslSocket = (SSLSocket) ReflectionSocketFactory.createSocket(
+ "javax.net.ssl.SSLSocketFactory", host, port, localAddress, localPort,
timeout);
+ if (sslSocket == null) {
+ sslSocket = (SSLSocket) ControllerThreadSocketFactory.createSocket(
+ this, host, port, localAddress, localPort, timeout);
+ }
+ verifyHostname(sslSocket);
+ return sslSocket;
+ }
}
/**
1.88 +9 -85 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java
Index: HttpConnection.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
retrieving revision 1.87
retrieving revision 1.88
diff -u -r1.87 -r1.88
--- HttpConnection.java 13 Apr 2004 18:20:23 -0000 1.87
+++ HttpConnection.java 13 Apr 2004 21:47:28 -0000 1.88
@@ -48,7 +48,6 @@
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.httpclient.util.EncodingUtil;
-import org.apache.commons.httpclient.util.TimeoutController;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -641,41 +640,18 @@
final int port = (proxyHostName == null) ? portNumber : proxyPortNumber;
assertNotOpen();
try {
- if (null == socket) {
-
-
+ if (this.socket == null) {
usingSecureSocket = isSecure() && !isProxied();
-
// use the protocol's socket factory unless this is a secure
// proxied connection
final ProtocolSocketFactory socketFactory =
(isSecure() && isProxied()
? new DefaultProtocolSocketFactory()
: protocolInUse.getSocketFactory());
-
- if (this.params.getConnectionTimeout() == 0) {
- if (localAddress != null) {
- socket = socketFactory.createSocket(host, port, localAddress, 0);
- } else {
- socket = socketFactory.createSocket(host, port);
- }
- } else {
- SocketTask task = new SocketTask() {
- public void doit() throws IOException {
- if (localAddress != null) {
- setSocket(socketFactory.createSocket(host, port, localAddress,
0));
- } else {
- setSocket(socketFactory.createSocket(host, port));
- }
- }
- };
- TimeoutController.execute(task, this.params.getConnectionTimeout());
- socket = task.getSocket();
- if (task.exception != null) {
- throw task.exception;
- }
- }
-
+ this.socket = socketFactory.createSocket(
+ host, port,
+ localAddress, 0,
+ this.params);
}
/*
@@ -712,16 +688,6 @@
// so close everything out
closeSocketAndStreams();
throw e;
- } catch (TimeoutController.TimeoutException e) {
- StringBuffer buffer = new StringBuffer();
- buffer.append("The host ");
- buffer.append(host);
- buffer.append(":");
- buffer.append(port);
- buffer.append(" did not accept the connection within timeout of ");
- buffer.append(this.params.getConnectionTimeout());
- buffer.append(" milliseconds");
- throw new ConnectTimeoutException(buffer.toString());
}
}
@@ -1143,7 +1109,6 @@
void setLocked(boolean locked) {
this.locked = locked;
}
-
// ------------------------------------------------------ Protected Methods
/**
@@ -1247,47 +1212,6 @@
*/
public void setSendBufferSize(int sendBufferSize) throws SocketException {
this.params.setSendBufferSize(sendBufferSize);
- }
-
- /**
- * Helper class for wrapping socket based tasks.
- */
- private abstract class SocketTask implements Runnable {
-
- /** The socket */
- private Socket socket;
- /** The exception */
- private IOException exception;
-
- /**
- * Set the socket.
- * @param newSocket The new socket.
- */
- protected void setSocket(final Socket newSocket) {
- socket = newSocket;
- }
-
- /**
- * Return the socket.
- * @return Socket The socket.
- */
- protected Socket getSocket() {
- return socket;
- }
- /**
- * Perform the logic.
- * @throws IOException If an IO problem occurs
- */
- public abstract void doit() throws IOException;
-
- /** Execute the logic in this object and keep track of any exceptions. */
- public void run() {
- try {
- doit();
- } catch (IOException e) {
- exception = e;
- }
- }
}
/**
1.4 +5 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthPolicy.java
Index: AuthPolicy.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthPolicy.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- AuthPolicy.java 22 Feb 2004 18:08:47 -0000 1.3
+++ AuthPolicy.java 13 Apr 2004 21:47:28 -0000 1.4
@@ -64,7 +64,7 @@
/**
* The key used to look up the list of IDs of supported {@link AuthScheme
* authentication schemes} in their order of preference. The scheme IDs are
- * stored in a {@link Collection} as {@link java.lang.String}s.
+ * stored in a {@link java.util.Collection} as {@link java.lang.String}s.
*
* <p>
* If several schemes are returned in the <tt>WWW-Authenticate</tt>
1.3 +7 -6 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/CredentialsProvider.java
Index: CredentialsProvider.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/CredentialsProvider.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- CredentialsProvider.java 22 Feb 2004 18:08:47 -0000 1.2
+++ CredentialsProvider.java 13 Apr 2004 21:47:28 -0000 1.3
@@ -35,9 +35,10 @@
/**
* <p>
- * Credentials provider interface can be used to provide {@link HttpMethod
- * HTTP method} with a means to request authentication credentials if no
- * credentials have been given or given credentials are incorrect.
+ * Credentials provider interface can be used to provide {@link
+ * org.apache.commons.httpclient.HttpMethod HTTP method} with a means
+ * to request authentication credentials if no credentials have been given
+ * or given credentials are incorrect.
* </p>
*
* @author Ortwin Glueck
1.4 +6 -6 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/params/HttpConnectionManagerParams.java
Index: HttpConnectionManagerParams.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/params/HttpConnectionManagerParams.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- HttpConnectionManagerParams.java 22 Feb 2004 18:08:48 -0000 1.3
+++ HttpConnectionManagerParams.java 13 Apr 2004 21:47:28 -0000 1.4
@@ -45,7 +45,7 @@
public class HttpConnectionManagerParams extends HttpConnectionParams {
/**
- * Defines the maximum number of connections allowed per host
+ * Defines the maximum number of connections allowed per host.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
@@ -53,7 +53,7 @@
public static String MAX_HOST_CONNECTIONS = "http.connection-manager.max-per-host";
/**
- * Defines the maximum number of connections allowed overall
+ * Defines the maximum number of connections allowed overall.
* <p>
* This parameter expects a value of type {@link Integer}.
* </p>
1.8 +59 -6 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/DefaultProtocolSocketFactory.java
Index: DefaultProtocolSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/DefaultProtocolSocketFactory.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- DefaultProtocolSocketFactory.java 22 Feb 2004 18:08:49 -0000 1.7
+++ DefaultProtocolSocketFactory.java 13 Apr 2004 21:47:28 -0000 1.8
@@ -36,6 +36,9 @@
import java.net.Socket;
import java.net.UnknownHostException;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+
/**
* The default class for creating protocol sockets. This class just uses the
* {@link java.net.Socket socket} constructors.
@@ -72,10 +75,60 @@
public Socket createSocket(
String host,
int port,
- InetAddress clientHost,
- int clientPort
+ InetAddress localAddress,
+ int localPort
) throws IOException, UnknownHostException {
- return new Socket(host, port, clientHost, clientPort);
+ return new Socket(host, port, localAddress, localPort);
+ }
+
+ /**
+ * Attempts to get a new socket connection to the given host within the given time
limit.
+ * <p>
+ * This method employs several techniques to circumvent the limitations of older JREs
that
+ * do not support connect timeout. When running in JRE 1.4 or above reflection is used
to
+ * call Socket#connect(SocketAddress endpoint, int timeout) method. When executing
in older
+ * JREs a controller thread is executed. The controller thread attempts to create a
new socket
+ * within the given limit of time. If socket constructor does not return until the
timeout
+ * expires, the controller terminates and throws an {@link ConnectTimeoutException}
+ * </p>
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param localAddress the local host name/IP to bind the socket to
+ * @param localPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ * @throws ConnectTimeoutException if socket cannot be connected within the
+ * given time limit
+ */
+ public Socket createSocket(
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort,
+ final HttpConnectionParams params
+ ) throws IOException, UnknownHostException, ConnectTimeoutException {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ if (timeout == 0) {
+ return createSocket(host, port, localAddress, localPort);
+ } else {
+ // To be eventually deprecated when migrated to Java 1.4 or above
+ Socket socket = ReflectionSocketFactory.createSocket(
+ "javax.net.SocketFactory", host, port, localAddress, localPort, timeout);
+ if (socket == null) {
+ socket = ControllerThreadSocketFactory.createSocket(
+ this, host, port, localAddress, localPort, timeout);
+ }
+ return socket;
+ }
}
/**
1.8 +35 -7 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ProtocolSocketFactory.java
Index: ProtocolSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ProtocolSocketFactory.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- ProtocolSocketFactory.java 22 Feb 2004 18:08:49 -0000 1.7
+++ ProtocolSocketFactory.java 13 Apr 2004 21:47:28 -0000 1.8
@@ -36,6 +36,9 @@
import java.net.Socket;
import java.net.UnknownHostException;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+
/**
* A factory for creating Sockets.
*
@@ -59,8 +62,8 @@
*
* @param host the host name/IP
* @param port the port on the host
- * @param clientHost the local host name/IP to bind the socket to
- * @param clientPort the port on the local machine
+ * @param localAddress the local host name/IP to bind the socket to
+ * @param localPort the port on the local machine
*
* @return Socket a new socket
*
@@ -71,9 +74,34 @@
Socket createSocket(
String host,
int port,
- InetAddress clientHost,
- int clientPort
+ InetAddress localAddress,
+ int localPort
) throws IOException, UnknownHostException;
+
+ /**
+ * Gets a new socket connection to the given host.
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param localAddress the local host name/IP to bind the socket to
+ * @param localPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ * @throws ConnectTimeoutException if socket cannot be connected within the
+ * given time limit
+ */
+ Socket createSocket(
+ String host,
+ int port,
+ InetAddress localAddress,
+ int localPort,
+ HttpConnectionParams params
+ ) throws IOException, UnknownHostException, ConnectTimeoutException;
/**
* Gets a new socket connection to the given host.
1.8 +54 -3 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/SSLProtocolSocketFactory.java
Index: SSLProtocolSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/SSLProtocolSocketFactory.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- SSLProtocolSocketFactory.java 22 Feb 2004 18:08:49 -0000 1.7
+++ SSLProtocolSocketFactory.java 13 Apr 2004 21:47:28 -0000 1.8
@@ -38,6 +38,9 @@
import javax.net.ssl.SSLSocketFactory;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+
/**
* A SecureProtocolSocketFactory that uses JSSE to create sockets.
*
@@ -83,6 +86,54 @@
clientHost,
clientPort
);
+ }
+
+ /**
+ * Attempts to get a new socket connection to the given host within the given time
limit.
+ * <p>
+ * This method employs several techniques to circumvent the limitations of older JREs
that
+ * do not support connect timeout. When running in JRE 1.4 or above reflection is used
to
+ * call Socket#connect(SocketAddress endpoint, int timeout) method. When executing
in older
+ * JREs a controller thread is executed. The controller thread attempts to create a
new socket
+ * within the given limit of time. If socket constructor does not return until the
timeout
+ * expires, the controller terminates and throws an {@link ConnectTimeoutException}
+ * </p>
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param localAddress the local host name/IP to bind the socket to
+ * @param localPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ */
+ public Socket createSocket(
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort,
+ final HttpConnectionParams params
+ ) throws IOException, UnknownHostException, ConnectTimeoutException {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ if (timeout == 0) {
+ return createSocket(host, port, localAddress, localPort);
+ } else {
+ // To be eventually deprecated when migrated to Java 1.4 or above
+ Socket socket = ReflectionSocketFactory.createSocket(
+ "javax.net.ssl.SSLSocketFactory", host, port, localAddress, localPort,
timeout);
+ if (socket == null) {
+ socket = ControllerThreadSocketFactory.createSocket(
+ this, host, port, localAddress, localPort, timeout);
+ }
+ return socket;
+ }
}
/**
1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ControllerThreadSocketFactory.java
Index: ControllerThreadSocketFactory.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ControllerThreadSocketFactory.java,v
1.1 2004/04/13 21:47:28 olegk Exp $
* $Revision: 1.1 $
* $Date: 2004/04/13 21:47:28 $
*
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient.protocol;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.util.TimeoutController;
/**
* This helper class is intedned to help work around the limitation of older Java versions
* (older than 1.4) that prevents from specifying a connection timeout when creating a
* socket. This factory executes a controller thread overssing the process of socket
* initialisation. If the socket constructor cannot be created within the specified time
* limit, the controller terminates and throws an {@link ConnectTimeoutException}
*
* @author Ortwin Glueck
* @author Oleg Kalnichevski
*
* @since 3.0
*/
public final class ControllerThreadSocketFactory {
private ControllerThreadSocketFactory() {
super();
}
/**
* This method spawns a controller thread overseeing the process of socket
* initialisation. If the socket constructor cannot be created within the specified
time
* limit, the controller terminates and throws an {@link ConnectTimeoutException}
*
* @param host the host name/IP
* @param port the port on the host
* @param localAddress the local host name/IP to bind the socket to
* @param localPort the port on the local machine
* @param timeout the timeout value to be used in milliseconds. If the socket cannot
be
* completed within the given time limit, it will be abandoned
*
* @return a connected Socket
*
* @throws IOException if an I/O error occurs while creating the socket
* @throws UnknownHostException if the IP address of the host cannot be
* determined
* @throws ConnectTimeoutException if socket cannot be connected within the
* given time limit
*
*/
public static Socket createSocket(
final ProtocolSocketFactory socketfactory,
final String host,
final int port,
final InetAddress localAddress,
final int localPort,
int timeout)
throws IOException, UnknownHostException, ConnectTimeoutException
{
SocketTask task = new SocketTask() {
public void doit() throws IOException {
setSocket(socketfactory.createSocket(host, port, localAddress, localPort));
}
};
try {
TimeoutController.execute(task, timeout);
} catch (TimeoutController.TimeoutException e) {
throw new ConnectTimeoutException(
"The host did not accept the connection within timeout of "
+ timeout + " ms");
}
Socket socket = task.getSocket();
if (task.exception != null) {
throw task.exception;
}
return socket;
}
public static Socket createSocket(final SocketTask task, int timeout)
throws IOException, UnknownHostException, ConnectTimeoutException
{
try {
TimeoutController.execute(task, timeout);
} catch (TimeoutController.TimeoutException e) {
throw new ConnectTimeoutException(
"The host did not accept the connection within timeout of "
+ timeout + " ms");
}
Socket socket = task.getSocket();
if (task.exception != null) {
throw task.exception;
}
return socket;
}
/**
* Helper class for wrapping socket based tasks.
*/
public static abstract class SocketTask implements Runnable {
/** The socket */
private Socket socket;
/** The exception */
private IOException exception;
/**
* Set the socket.
* @param newSocket The new socket.
*/
protected void setSocket(final Socket newSocket) {
socket = newSocket;
}
/**
* Return the socket.
* @return Socket The socket.
*/
protected Socket getSocket() {
return socket;
}
/**
* Perform the logic.
* @throws IOException If an IO problem occurs
*/
public abstract void doit() throws IOException;
/** Execute the logic in this object and keep track of any exceptions. */
public void run() {
try {
doit();
} catch (IOException e) {
exception = e;
}
}
}
}
1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ReflectionSocketFactory.java
Index: ReflectionSocketFactory.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ReflectionSocketFactory.java,v
1.1 2004/04/13 21:47:28 olegk Exp $
* $Revision: 1.1 $
* $Date: 2004/04/13 21:47:28 $
*
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient.protocol;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.commons.httpclient.ConnectTimeoutException;
/**
* This helper class uses refelction in order to execute Socket methods
* available in Java 1.4 and above
*
* @author Oleg Kalnichevski
*
* @since 3.0
*/
public final class ReflectionSocketFactory {
private static boolean REFLECTION_FAILED = false;
private static Constructor INETSOCKETADDRESS_CONSTRUCTOR = null;
private static Method SOCKETCONNECT_METHOD = null;
private static Class SOCKETTIMEOUTEXCEPTION_CLASS = null;
private ReflectionSocketFactory() {
super();
}
/**
* This method attempts to execute Socket method available since Java 1.4
* using reflection. If the methods are not available or could not be executed
* <tt>null</tt> is returned
*
* @param host the host name/IP
* @param port the port on the host
* @param localAddress the local host name/IP to bind the socket to
* @param localPort the port on the local machine
* @param timeout the timeout value to be used in milliseconds. If the socket cannot
be
* completed within the given time limit, it will be abandoned
*
* @return a connected Socket
*
* @throws IOException if an I/O error occurs while creating the socket
* @throws UnknownHostException if the IP address of the host cannot be
* determined
* @throws ConnectTimeoutException if socket cannot be connected within the
* given time limit
*
*/
public static Socket createSocket(
final String socketfactoryName,
final String host,
final int port,
final InetAddress localAddress,
final int localPort,
int timeout)
throws IOException, UnknownHostException, ConnectTimeoutException
{
if (REFLECTION_FAILED) {
//This is known to have failed before. Do not try it again
return null;
}
// This code uses reflection to essentially do the following:
//
// SocketFactory socketFactory = Class.forName(socketfactoryName).getDefault();
// Socket socket = socketFactory.createSocket();
// SocketAddress addr = new InetSocketAddress(host, port);
// socket.connect(addr, timeout);
// return socket;
try {
Class socketfactoryClass = Class.forName(socketfactoryName);
Method method = socketfactoryClass.getMethod("getDefault",
new Class[] {});
Object socketfactory = method.invoke(null,
new Object[] {});
method = socketfactoryClass.getMethod("createSocket",
new Class[] {});
Socket socket = (Socket) method.invoke(socketfactory, new Object[] {});
if (INETSOCKETADDRESS_CONSTRUCTOR == null) {
Class addressClass = Class.forName("java.net.InetSocketAddress");
INETSOCKETADDRESS_CONSTRUCTOR = addressClass.getConstructor(
new Class[] { String.class, Integer.TYPE });
}
Object addr = INETSOCKETADDRESS_CONSTRUCTOR.newInstance(
new Object[] { host, new Integer(port)});
if (SOCKETCONNECT_METHOD == null) {
SOCKETCONNECT_METHOD = Socket.class.getMethod("connect",
new Class[] {Class.forName("java.net.SocketAddress"), Integer.TYPE});
}
SOCKETCONNECT_METHOD.invoke(socket,
new Object[] { addr, new Integer(timeout)});
return socket;
}
catch (InvocationTargetException e) {
Throwable cause = e.getTargetException();
if (SOCKETTIMEOUTEXCEPTION_CLASS == null) {
try {
SOCKETTIMEOUTEXCEPTION_CLASS = Class.forName("java.net.SocketTimeoutException");
} catch (ClassNotFoundException ex) {
// At this point this should never happen. Really.
REFLECTION_FAILED = true;
return null;
}
}
if (SOCKETTIMEOUTEXCEPTION_CLASS.isInstance(cause)) {
throw new ConnectTimeoutException(
"The host did not accept the connection within timeout of "
+ timeout + " ms", cause);
}
if (cause instanceof IOException) {
throw (IOException)cause;
}
return null;
}
catch (Exception e) {
REFLECTION_FAILED = true;
return null;
}
}
}
1.16 +30 -3 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java
Index: TestHttpConnection.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- TestHttpConnection.java 22 Feb 2004 18:08:49 -0000 1.15
+++ TestHttpConnection.java 13 Apr 2004 21:47:29 -0000 1.16
@@ -41,8 +41,10 @@
import junit.framework.TestSuite;
import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
/**
*
@@ -189,8 +191,8 @@
public Socket createSocket(
String host,
int port,
- InetAddress clientHost,
- int clientPort
+ InetAddress localAddress,
+ int localPort
) throws IOException, UnknownHostException {
synchronized (this) {
@@ -198,7 +200,32 @@
this.wait(delay);
} catch (InterruptedException e) {}
}
- return realFactory.createSocket(host, port, clientHost, clientPort);
+ return realFactory.createSocket(host, port, localAddress, localPort);
+ }
+
+ public Socket createSocket(
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort,
+ final HttpConnectionParams params
+ ) throws IOException, UnknownHostException {
+
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ ControllerThreadSocketFactory.SocketTask task = new ControllerThreadSocketFactory.SocketTask()
{
+ public void doit() throws IOException {
+ synchronized (this) {
+ try {
+ this.wait(delay);
+ } catch (InterruptedException e) {}
+ }
+ setSocket(realFactory.createSocket(host, port, localAddress, localPort));
+ }
+ };
+ return ControllerThreadSocketFactory.createSocket(task, timeout);
}
public Socket createSocket(String host, int port)
1.21 +10 -4 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.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- TestHttpConnectionManager.java 8 Apr 2004 22:25:11 -0000 1.20
+++ TestHttpConnectionManager.java 13 Apr 2004 21:47:29 -0000 1.21
@@ -41,6 +41,7 @@
import org.apache.commons.httpclient.methods.GetMethod;
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.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
@@ -683,6 +684,11 @@
throw new IllegalStateException("createSocket() should never have been called.");
}
+ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort,
+ HttpConnectionParams params)
+ throws IOException, UnknownHostException {
+ throw new IllegalStateException("createSocket() should never have been called.");
+ }
}
static class GetConnectionThread extends Thread {
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org
|