View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/protocol/ControllerThreadSocketFactory.java $
3    * $Revision: 1425331 $
4    * $Date: 2012-12-22 19:29:41 +0100 (Sat, 22 Dec 2012) $
5    *
6    * ====================================================================
7    *
8    *  Licensed to the Apache Software Foundation (ASF) under one or more
9    *  contributor license agreements.  See the NOTICE file distributed with
10   *  this work for additional information regarding copyright ownership.
11   *  The ASF licenses this file to You under the Apache License, Version 2.0
12   *  (the "License"); you may not use this file except in compliance with
13   *  the License.  You may obtain a copy of the License at
14   *
15   *      http://www.apache.org/licenses/LICENSE-2.0
16   *
17   *  Unless required by applicable law or agreed to in writing, software
18   *  distributed under the License is distributed on an "AS IS" BASIS,
19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   *  See the License for the specific language governing permissions and
21   *  limitations under the License.
22   * ====================================================================
23   *
24   * This software consists of voluntary contributions made by many
25   * individuals on behalf of the Apache Software Foundation.  For more
26   * information on the Apache Software Foundation, please see
27   * <http://www.apache.org/>.
28   *
29   */
30  
31  package org.apache.commons.httpclient.protocol;
32  
33  import java.io.IOException;
34  import java.net.InetAddress;
35  import java.net.Socket;
36  import java.net.UnknownHostException;
37  
38  import org.apache.commons.httpclient.ConnectTimeoutException;
39  import org.apache.commons.httpclient.util.TimeoutController;
40  
41  /***
42   * This helper class is intedned to help work around the limitation of older Java versions
43   * (older than 1.4) that prevents from specifying a connection timeout when creating a
44   * socket. This factory executes a controller thread overssing the process of socket 
45   * initialisation. If the socket constructor cannot be created within the specified time
46   * limit, the controller terminates and throws an {@link ConnectTimeoutException} 
47   * 
48   * @author Ortwin Glueck
49   * @author Oleg Kalnichevski
50   * 
51   * @since 3.0
52   */
53  public final class ControllerThreadSocketFactory {
54  
55      private ControllerThreadSocketFactory() {
56          super();
57      }
58  
59      /***
60       * This method spawns a controller thread overseeing the process of socket 
61       * initialisation. If the socket constructor cannot be created within the specified time
62       * limit, the controller terminates and throws an {@link ConnectTimeoutException}
63       * 
64       * @param host the host name/IP
65       * @param port the port on the host
66       * @param localAddress the local host name/IP to bind the socket to
67       * @param localPort the port on the local machine
68       * @param timeout the timeout value to be used in milliseconds. If the socket cannot be
69       *        completed within the given time limit, it will be abandoned
70       * 
71       * @return a connected Socket
72       * 
73       * @throws IOException if an I/O error occurs while creating the socket
74       * @throws UnknownHostException if the IP address of the host cannot be
75       * determined
76       * @throws ConnectTimeoutException if socket cannot be connected within the
77       *  given time limit
78       * 
79       */
80      public static Socket createSocket(
81          final ProtocolSocketFactory socketfactory, 
82          final String host,
83          final int port,
84          final InetAddress localAddress,
85          final int localPort,
86          int timeout)
87       throws IOException, UnknownHostException, ConnectTimeoutException
88      {
89              SocketTask task = new SocketTask() {
90                  public void doit() throws IOException {
91                      setSocket(socketfactory.createSocket(host, port, localAddress, localPort));
92                  }                 
93              };
94              try {
95                  TimeoutController.execute(task, timeout);
96              } catch (TimeoutController.TimeoutException e) {
97                  throw new ConnectTimeoutException(
98                      "The host did not accept the connection within timeout of " 
99                      + timeout + " ms");
100             }
101             Socket socket = task.getSocket();
102             if (task.exception != null) {
103                 throw task.exception;
104             }
105             return socket;
106     }
107 
108     public static Socket createSocket(final SocketTask task, int timeout)
109      throws IOException, UnknownHostException, ConnectTimeoutException
110     {
111             try {
112                 TimeoutController.execute(task, timeout);
113             } catch (TimeoutController.TimeoutException e) {
114                 throw new ConnectTimeoutException(
115                     "The host did not accept the connection within timeout of " 
116                     + timeout + " ms");
117             }
118             Socket socket = task.getSocket();
119             if (task.exception != null) {
120                 throw task.exception;
121             }
122             return socket;
123     }
124 
125     /***
126     * Helper class for wrapping socket based tasks.
127     */
128     public static abstract class SocketTask implements Runnable {
129         /*** The socket */
130         private Socket socket;
131         /*** The exception */
132         private IOException exception;
133 
134         /***
135          * Set the socket.
136          * @param newSocket The new socket.
137          */
138         protected void setSocket(final Socket newSocket) {
139             socket = newSocket;
140         }
141 
142         /***
143          * Return the socket.
144          * @return Socket The socket.
145          */
146         protected Socket getSocket() {
147             return socket;
148         }
149         /***
150          * Perform the logic.
151          * @throws IOException If an IO problem occurs
152          */
153         public abstract void doit() throws IOException;
154 
155         /*** Execute the logic in this object and keep track of any exceptions. */
156         public void run() {
157             try {
158                 doit();
159             } catch (IOException e) {
160                 exception = e;
161             }
162         }
163     }
164 }