View Javadoc

1   /*
2    * $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 $
3    * $Revision: 155418 $
4    * $Date: 2005-02-26 08:01:52 -0500 (Sat, 26 Feb 2005) $
5    *
6    * ====================================================================
7    *
8    *  Copyright 2002-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient.protocol;
31  
32  import java.io.IOException;
33  import java.lang.reflect.Constructor;
34  import java.lang.reflect.InvocationTargetException;
35  import java.lang.reflect.Method;
36  import java.net.InetAddress;
37  import java.net.Socket;
38  import java.net.UnknownHostException;
39  
40  import org.apache.commons.httpclient.ConnectTimeoutException;
41  
42  /***
43   * This helper class uses refelction in order to execute Socket methods
44   * available in Java 1.4 and above  
45   * 
46   * @author Oleg Kalnichevski
47   * 
48   * @since 3.0
49   */
50  public final class ReflectionSocketFactory {
51  
52      private static boolean REFLECTION_FAILED = false;
53      
54      private static Constructor INETSOCKETADDRESS_CONSTRUCTOR = null;
55      private static Method SOCKETCONNECT_METHOD = null;
56      private static Method SOCKETBIND_METHOD = null;
57      private static Class SOCKETTIMEOUTEXCEPTION_CLASS = null;
58  
59      private ReflectionSocketFactory() {
60          super();
61      }
62  
63      /***
64       * This method attempts to execute Socket method available since Java 1.4
65       * using reflection. If the methods are not available or could not be executed
66       * <tt>null</tt> is returned
67       *   
68       * @param socketfactoryName name of the socket factory class
69       * @param host the host name/IP
70       * @param port the port on the host
71       * @param localAddress the local host name/IP to bind the socket to
72       * @param localPort the port on the local machine
73       * @param timeout the timeout value to be used in milliseconds. If the socket cannot be
74       *        completed within the given time limit, it will be abandoned
75       * 
76       * @return a connected Socket
77       * 
78       * @throws IOException if an I/O error occurs while creating the socket
79       * @throws UnknownHostException if the IP address of the host cannot be
80       * determined
81       * @throws ConnectTimeoutException if socket cannot be connected within the
82       *  given time limit
83       * 
84       */
85      public static Socket createSocket(
86          final String socketfactoryName, 
87          final String host,
88          final int port,
89          final InetAddress localAddress,
90          final int localPort,
91          int timeout)
92       throws IOException, UnknownHostException, ConnectTimeoutException
93      {
94          if (REFLECTION_FAILED) {
95              //This is known to have failed before. Do not try it again
96              return null;
97          }
98          // This code uses reflection to essentially do the following:
99          //
100         //  SocketFactory socketFactory = Class.forName(socketfactoryName).getDefault();
101         //  Socket socket = socketFactory.createSocket();
102         //  SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
103         //  SocketAddress remoteaddr = new InetSocketAddress(host, port);
104         //  socket.bind(localaddr);
105         //  socket.connect(remoteaddr, timeout);
106         //  return socket;
107         try {
108             Class socketfactoryClass = Class.forName(socketfactoryName);
109             Method method = socketfactoryClass.getMethod("getDefault", 
110                 new Class[] {});
111             Object socketfactory = method.invoke(null, 
112                 new Object[] {});
113             method = socketfactoryClass.getMethod("createSocket", 
114                 new Class[] {});
115             Socket socket = (Socket) method.invoke(socketfactory, new Object[] {});
116             
117             if (INETSOCKETADDRESS_CONSTRUCTOR == null) {
118                 Class addressClass = Class.forName("java.net.InetSocketAddress");
119                 INETSOCKETADDRESS_CONSTRUCTOR = addressClass.getConstructor(
120                     new Class[] { InetAddress.class, Integer.TYPE });
121             }
122                 
123             Object remoteaddr = INETSOCKETADDRESS_CONSTRUCTOR.newInstance(
124                 new Object[] { InetAddress.getByName(host), new Integer(port)});
125 
126             Object localaddr = INETSOCKETADDRESS_CONSTRUCTOR.newInstance(
127                     new Object[] { localAddress, new Integer(localPort)});
128 
129             if (SOCKETCONNECT_METHOD == null) {
130                 SOCKETCONNECT_METHOD = Socket.class.getMethod("connect", 
131                     new Class[] {Class.forName("java.net.SocketAddress"), Integer.TYPE});
132             }
133 
134             if (SOCKETBIND_METHOD == null) {
135                 SOCKETBIND_METHOD = Socket.class.getMethod("bind", 
136                     new Class[] {Class.forName("java.net.SocketAddress")});
137             }
138             SOCKETBIND_METHOD.invoke(socket, new Object[] { localaddr});
139             SOCKETCONNECT_METHOD.invoke(socket, new Object[] { remoteaddr, new Integer(timeout)});
140             return socket;
141         }
142         catch (InvocationTargetException e) {
143             Throwable cause = e.getTargetException(); 
144             if (SOCKETTIMEOUTEXCEPTION_CLASS == null) {
145                 try {
146                     SOCKETTIMEOUTEXCEPTION_CLASS = Class.forName("java.net.SocketTimeoutException");
147                 } catch (ClassNotFoundException ex) {
148                     // At this point this should never happen. Really.
149                     REFLECTION_FAILED = true;
150                     return null;
151                 }
152             }
153             if (SOCKETTIMEOUTEXCEPTION_CLASS.isInstance(cause)) {
154                 throw new ConnectTimeoutException(
155                     "The host did not accept the connection within timeout of " 
156                     + timeout + " ms", cause);
157             }
158             if (cause instanceof IOException) {
159                 throw (IOException)cause;
160             }
161             return null;
162         }
163         catch (Exception e) {
164             REFLECTION_FAILED = true;
165             return null;
166         }
167     }
168 }