001: /*
002: * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/protocol/ReflectionSocketFactory.java,v 1.4 2004/12/21 23:15:21 olegk Exp $
003: * $Revision: 480424 $
004: * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
005: *
006: * ====================================================================
007: *
008: * Licensed to the Apache Software Foundation (ASF) under one or more
009: * contributor license agreements. See the NOTICE file distributed with
010: * this work for additional information regarding copyright ownership.
011: * The ASF licenses this file to You under the Apache License, Version 2.0
012: * (the "License"); you may not use this file except in compliance with
013: * the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing, software
018: * distributed under the License is distributed on an "AS IS" BASIS,
019: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020: * See the License for the specific language governing permissions and
021: * limitations under the License.
022: * ====================================================================
023: *
024: * This software consists of voluntary contributions made by many
025: * individuals on behalf of the Apache Software Foundation. For more
026: * information on the Apache Software Foundation, please see
027: * <http://www.apache.org/>.
028: *
029: */
030:
031: package org.apache.commons.httpclient.protocol;
032:
033: import java.io.IOException;
034: import java.lang.reflect.Constructor;
035: import java.lang.reflect.InvocationTargetException;
036: import java.lang.reflect.Method;
037: import java.net.InetAddress;
038: import java.net.Socket;
039: import java.net.UnknownHostException;
040:
041: import org.apache.commons.httpclient.ConnectTimeoutException;
042:
043: /**
044: * This helper class uses refelction in order to execute Socket methods
045: * available in Java 1.4 and above
046: *
047: * @author Oleg Kalnichevski
048: *
049: * @since 3.0
050: */
051: public final class ReflectionSocketFactory {
052:
053: private static boolean REFLECTION_FAILED = false;
054:
055: private static Constructor INETSOCKETADDRESS_CONSTRUCTOR = null;
056: private static Method SOCKETCONNECT_METHOD = null;
057: private static Method SOCKETBIND_METHOD = null;
058: private static Class SOCKETTIMEOUTEXCEPTION_CLASS = null;
059:
060: private ReflectionSocketFactory() {
061: super ();
062: }
063:
064: /**
065: * This method attempts to execute Socket method available since Java 1.4
066: * using reflection. If the methods are not available or could not be executed
067: * <tt>null</tt> is returned
068: *
069: * @param socketfactoryName name of the socket factory class
070: * @param host the host name/IP
071: * @param port the port on the host
072: * @param localAddress the local host name/IP to bind the socket to
073: * @param localPort the port on the local machine
074: * @param timeout the timeout value to be used in milliseconds. If the socket cannot be
075: * completed within the given time limit, it will be abandoned
076: *
077: * @return a connected Socket
078: *
079: * @throws IOException if an I/O error occurs while creating the socket
080: * @throws UnknownHostException if the IP address of the host cannot be
081: * determined
082: * @throws ConnectTimeoutException if socket cannot be connected within the
083: * given time limit
084: *
085: */
086: public static Socket createSocket(final String socketfactoryName,
087: final String host, final int port,
088: final InetAddress localAddress, final int localPort,
089: int timeout) throws IOException, UnknownHostException,
090: ConnectTimeoutException {
091: if (REFLECTION_FAILED) {
092: //This is known to have failed before. Do not try it again
093: return null;
094: }
095: // This code uses reflection to essentially do the following:
096: //
097: // SocketFactory socketFactory = Class.forName(socketfactoryName).getDefault();
098: // Socket socket = socketFactory.createSocket();
099: // SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
100: // SocketAddress remoteaddr = new InetSocketAddress(host, port);
101: // socket.bind(localaddr);
102: // socket.connect(remoteaddr, timeout);
103: // return socket;
104: try {
105: Class socketfactoryClass = Class.forName(socketfactoryName);
106: Method method = socketfactoryClass.getMethod("getDefault",
107: new Class[] {});
108: Object socketfactory = method.invoke(null, new Object[] {});
109: method = socketfactoryClass.getMethod("createSocket",
110: new Class[] {});
111: Socket socket = (Socket) method.invoke(socketfactory,
112: new Object[] {});
113:
114: if (INETSOCKETADDRESS_CONSTRUCTOR == null) {
115: Class addressClass = Class
116: .forName("java.net.InetSocketAddress");
117: INETSOCKETADDRESS_CONSTRUCTOR = addressClass
118: .getConstructor(new Class[] {
119: InetAddress.class, Integer.TYPE });
120: }
121:
122: Object remoteaddr = INETSOCKETADDRESS_CONSTRUCTOR
123: .newInstance(new Object[] {
124: InetAddress.getByName(host),
125: new Integer(port) });
126:
127: Object localaddr = INETSOCKETADDRESS_CONSTRUCTOR
128: .newInstance(new Object[] { localAddress,
129: new Integer(localPort) });
130:
131: if (SOCKETCONNECT_METHOD == null) {
132: SOCKETCONNECT_METHOD = Socket.class
133: .getMethod(
134: "connect",
135: new Class[] {
136: Class
137: .forName("java.net.SocketAddress"),
138: Integer.TYPE });
139: }
140:
141: if (SOCKETBIND_METHOD == null) {
142: SOCKETBIND_METHOD = Socket.class.getMethod("bind",
143: new Class[] { Class
144: .forName("java.net.SocketAddress") });
145: }
146: SOCKETBIND_METHOD
147: .invoke(socket, new Object[] { localaddr });
148: SOCKETCONNECT_METHOD.invoke(socket, new Object[] {
149: remoteaddr, new Integer(timeout) });
150: return socket;
151: } catch (InvocationTargetException e) {
152: Throwable cause = e.getTargetException();
153: if (SOCKETTIMEOUTEXCEPTION_CLASS == null) {
154: try {
155: SOCKETTIMEOUTEXCEPTION_CLASS = Class
156: .forName("java.net.SocketTimeoutException");
157: } catch (ClassNotFoundException ex) {
158: // At this point this should never happen. Really.
159: REFLECTION_FAILED = true;
160: return null;
161: }
162: }
163: if (SOCKETTIMEOUTEXCEPTION_CLASS.isInstance(cause)) {
164: throw new ConnectTimeoutException(
165: "The host did not accept the connection within timeout of "
166: + timeout + " ms", cause);
167: }
168: if (cause instanceof IOException) {
169: throw (IOException) cause;
170: }
171: return null;
172: } catch (Exception e) {
173: REFLECTION_FAILED = true;
174: return null;
175: }
176: }
177: }
|