1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
96 return null;
97 }
98
99
100
101
102
103
104
105
106
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
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 }