View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/protocol/ReflectionSocketFactory.java,v 1.2 2004/04/18 23:51:38 jsdever Exp $
3    * $Revision: 1.2 $
4    * $Date: 2004/04/18 23:51:38 $
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 Class SOCKETTIMEOUTEXCEPTION_CLASS = null;
57  
58      private ReflectionSocketFactory() {
59          super();
60      }
61  
62      /***
63       * This method attempts to execute Socket method available since Java 1.4
64       * using reflection. If the methods are not available or could not be executed
65       * <tt>null</tt> is returned
66       *   
67       * @param host the host name/IP
68       * @param port the port on the host
69       * @param localAddress the local host name/IP to bind the socket to
70       * @param localPort the port on the local machine
71       * @param timeout the timeout value to be used in milliseconds. If the socket cannot be
72       *        completed within the given time limit, it will be abandoned
73       * 
74       * @return a connected Socket
75       * 
76       * @throws IOException if an I/O error occurs while creating the socket
77       * @throws UnknownHostException if the IP address of the host cannot be
78       * determined
79       * @throws ConnectTimeoutException if socket cannot be connected within the
80       *  given time limit
81       * 
82       */
83      public static Socket createSocket(
84          final String socketfactoryName, 
85          final String host,
86          final int port,
87          final InetAddress localAddress,
88          final int localPort,
89          int timeout)
90       throws IOException, UnknownHostException, ConnectTimeoutException
91      {
92          if (REFLECTION_FAILED) {
93              //This is known to have failed before. Do not try it again
94              return null;
95          }
96          // This code uses reflection to essentially do the following:
97          //
98          //  SocketFactory socketFactory = Class.forName(socketfactoryName).getDefault();
99          //  Socket socket = socketFactory.createSocket();
100         //  SocketAddress addr = new InetSocketAddress(host, port);
101         //  socket.connect(addr, timeout);
102         //  return socket;
103         try {
104             Class socketfactoryClass = Class.forName(socketfactoryName);
105             Method method = socketfactoryClass.getMethod("getDefault", 
106                 new Class[] {});
107             Object socketfactory = method.invoke(null, 
108                 new Object[] {});
109             method = socketfactoryClass.getMethod("createSocket", 
110                 new Class[] {});
111             Socket socket = (Socket) method.invoke(socketfactory, new Object[] {});
112             
113             if (INETSOCKETADDRESS_CONSTRUCTOR == null) {
114                 Class addressClass = Class.forName("java.net.InetSocketAddress");
115                 INETSOCKETADDRESS_CONSTRUCTOR = addressClass.getConstructor(
116                     new Class[] { String.class, Integer.TYPE });
117             }
118                 
119             Object addr = INETSOCKETADDRESS_CONSTRUCTOR.newInstance(
120                 new Object[] { host, new Integer(port)});
121 
122             if (SOCKETCONNECT_METHOD == null) {
123                 SOCKETCONNECT_METHOD = Socket.class.getMethod("connect", 
124                     new Class[] {Class.forName("java.net.SocketAddress"), Integer.TYPE});
125             }
126 
127             SOCKETCONNECT_METHOD.invoke(socket, 
128                 new Object[] { addr, new Integer(timeout)});
129 
130             return socket;
131         }
132         catch (InvocationTargetException e) {
133             Throwable cause = e.getTargetException(); 
134             if (SOCKETTIMEOUTEXCEPTION_CLASS == null) {
135                 try {
136                     SOCKETTIMEOUTEXCEPTION_CLASS = Class.forName("java.net.SocketTimeoutException");
137                 } catch (ClassNotFoundException ex) {
138                     // At this point this should never happen. Really.
139                     REFLECTION_FAILED = true;
140                     return null;
141                 }
142             }
143             if (SOCKETTIMEOUTEXCEPTION_CLASS.isInstance(cause)) {
144                 throw new ConnectTimeoutException(
145                     "The host did not accept the connection within timeout of " 
146                     + timeout + " ms", cause);
147             }
148             if (cause instanceof IOException) {
149                 throw (IOException)cause;
150             }
151             return null;
152         }
153         catch (Exception e) {
154             REFLECTION_FAILED = true;
155             return null;
156         }
157     }
158 }