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. package org.apache.commons.httpclient.protocol;
  30. import java.io.IOException;
  31. import java.lang.reflect.Constructor;
  32. import java.lang.reflect.InvocationTargetException;
  33. import java.lang.reflect.Method;
  34. import java.net.InetAddress;
  35. import java.net.Socket;
  36. import java.net.UnknownHostException;
  37. import org.apache.commons.httpclient.ConnectTimeoutException;
  38. /**
  39. * This helper class uses refelction in order to execute Socket methods
  40. * available in Java 1.4 and above
  41. *
  42. * @author Oleg Kalnichevski
  43. *
  44. * @since 3.0
  45. */
  46. public final class ReflectionSocketFactory {
  47. private static boolean REFLECTION_FAILED = false;
  48. private static Constructor INETSOCKETADDRESS_CONSTRUCTOR = null;
  49. private static Method SOCKETCONNECT_METHOD = null;
  50. private static Class SOCKETTIMEOUTEXCEPTION_CLASS = null;
  51. private ReflectionSocketFactory() {
  52. super();
  53. }
  54. /**
  55. * This method attempts to execute Socket method available since Java 1.4
  56. * using reflection. If the methods are not available or could not be executed
  57. * <tt>null</tt> is returned
  58. *
  59. * @param socketfactoryName name of the socket factory class
  60. * @param host the host name/IP
  61. * @param port the port on the host
  62. * @param localAddress the local host name/IP to bind the socket to
  63. * @param localPort the port on the local machine
  64. * @param timeout the timeout value to be used in milliseconds. If the socket cannot be
  65. * completed within the given time limit, it will be abandoned
  66. *
  67. * @return a connected Socket
  68. *
  69. * @throws IOException if an I/O error occurs while creating the socket
  70. * @throws UnknownHostException if the IP address of the host cannot be
  71. * determined
  72. * @throws ConnectTimeoutException if socket cannot be connected within the
  73. * given time limit
  74. *
  75. */
  76. public static Socket createSocket(
  77. final String socketfactoryName,
  78. final String host,
  79. final int port,
  80. final InetAddress localAddress,
  81. final int localPort,
  82. int timeout)
  83. throws IOException, UnknownHostException, ConnectTimeoutException
  84. {
  85. if (REFLECTION_FAILED) {
  86. //This is known to have failed before. Do not try it again
  87. return null;
  88. }
  89. // This code uses reflection to essentially do the following:
  90. //
  91. // SocketFactory socketFactory = Class.forName(socketfactoryName).getDefault();
  92. // Socket socket = socketFactory.createSocket();
  93. // SocketAddress addr = new InetSocketAddress(host, port);
  94. // socket.connect(addr, timeout);
  95. // return socket;
  96. try {
  97. Class socketfactoryClass = Class.forName(socketfactoryName);
  98. Method method = socketfactoryClass.getMethod("getDefault",
  99. new Class[] {});
  100. Object socketfactory = method.invoke(null,
  101. new Object[] {});
  102. method = socketfactoryClass.getMethod("createSocket",
  103. new Class[] {});
  104. Socket socket = (Socket) method.invoke(socketfactory, new Object[] {});
  105. if (INETSOCKETADDRESS_CONSTRUCTOR == null) {
  106. Class addressClass = Class.forName("java.net.InetSocketAddress");
  107. INETSOCKETADDRESS_CONSTRUCTOR = addressClass.getConstructor(
  108. new Class[] { String.class, Integer.TYPE });
  109. }
  110. Object addr = INETSOCKETADDRESS_CONSTRUCTOR.newInstance(
  111. new Object[] { host, new Integer(port)});
  112. if (SOCKETCONNECT_METHOD == null) {
  113. SOCKETCONNECT_METHOD = Socket.class.getMethod("connect",
  114. new Class[] {Class.forName("java.net.SocketAddress"), Integer.TYPE});
  115. }
  116. SOCKETCONNECT_METHOD.invoke(socket,
  117. new Object[] { addr, new Integer(timeout)});
  118. return socket;
  119. }
  120. catch (InvocationTargetException e) {
  121. Throwable cause = e.getTargetException();
  122. if (SOCKETTIMEOUTEXCEPTION_CLASS == null) {
  123. try {
  124. SOCKETTIMEOUTEXCEPTION_CLASS = Class.forName("java.net.SocketTimeoutException");
  125. } catch (ClassNotFoundException ex) {
  126. // At this point this should never happen. Really.
  127. REFLECTION_FAILED = true;
  128. return null;
  129. }
  130. }
  131. if (SOCKETTIMEOUTEXCEPTION_CLASS.isInstance(cause)) {
  132. throw new ConnectTimeoutException(
  133. "The host did not accept the connection within timeout of "
  134. + timeout + " ms", cause);
  135. }
  136. if (cause instanceof IOException) {
  137. throw (IOException)cause;
  138. }
  139. return null;
  140. }
  141. catch (Exception e) {
  142. REFLECTION_FAILED = true;
  143. return null;
  144. }
  145. }
  146. }