View Javadoc

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