View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.tools;
12  
13  import java.io.IOException;
14  import java.net.Socket;
15  
16  import org.slf4j.Logger;
17  import org.slf4j.LoggerFactory;
18  
19  /**
20   * A factory for creating AsyncSocket objects.
21   */
22  public final class AsyncSocketFactory {
23  
24    /** The Constant logger. */
25    private static final Logger logger = LoggerFactory.getLogger(AsyncSocketFactory.class);
26  
27    /**
28     * Prevent Instantiation.
29     */
30    private AsyncSocketFactory() {
31      // Prevent Instantiation
32    }
33  
34    /**
35     * Creates a new AsyncSocket object.
36     *
37     * @param server the server
38     * @param port the port
39     * @param timeout the timeout
40     *
41     * @return the socket
42     *
43     * @throws IOException Signals that an I/O exception has occurred.
44     */
45    public static Socket createSocket(String server, int port, long timeout) throws IOException {
46      SocketWrapper socketWrapper = new SocketWrapper();
47      socketWrapper.server = server;
48      socketWrapper.port = port;
49  
50      Object sync = new Object();
51      Thread socketThread = new Thread(new SocketRunnable(socketWrapper, sync));
52      socketThread.setDaemon(true);
53      Thread timeoutThread = new Thread(new TimeoutRunnable(sync, timeout * 1000));
54      timeoutThread.setDaemon(true);
55  
56      timeoutThread.start();
57      socketThread.start();
58  
59      synchronized (sync) {
60        if (socketWrapper.socket == null) {
61          boolean inProgress = true;
62          while (inProgress) {
63            try {
64              sync.wait(timeout * 1000);
65            } catch (InterruptedException e) {
66              // Restore interrupted state...
67              Thread.currentThread().interrupt();
68              logger.trace("", e);
69            }
70            inProgress = false;
71          }
72        }
73      }
74  
75      timeoutThread.interrupt();
76      socketThread.interrupt();
77  
78      socketWrapper.valid = false;
79  
80      if (socketWrapper.socket == null && socketWrapper.exception != null) {
81        throw socketWrapper.exception;
82      }
83      if (socketWrapper.socket == null) {
84        throw new TimeoutException();
85      }
86  
87      return socketWrapper.getSocket();
88    }
89  
90    /**
91     * The Class SocketWrapper.
92     */
93    static final class SocketWrapper {
94  
95      /** The socket. */
96      Socket socket;
97  
98      /** The server. */
99      String server;
100 
101     /** The port. */
102     int port;
103 
104     /** The exception. */
105     IOException exception;
106 
107     /** The valid. */
108     boolean valid = true;
109 
110     /**
111      * Gets the socket.
112      *
113      * @return the socket
114      */
115     public Socket getSocket() {
116       return socket;
117     }
118 
119     /**
120      * Sets the socket.
121      *
122      * @param socket the new socket
123      */
124     public void setSocket(Socket socket) {
125       this.socket = socket;
126     }
127 
128     /**
129      * Gets the server.
130      *
131      * @return the server
132      */
133     public String getServer() {
134       return server;
135     }
136 
137     /**
138      * Gets the port.
139      *
140      * @return the port
141      */
142     public int getPort() {
143       return port;
144     }
145 
146     /**
147      * Sets the exception.
148      *
149      * @param exception the new exception
150      */
151     public void setException(IOException exception) {
152       this.exception = exception;
153     }
154 
155     /**
156      * Checks if is valid.
157      *
158      * @return true, if is valid
159      */
160     public boolean isValid() {
161       return valid;
162     }
163 
164     /**
165      * Instantiates a new socket wrapper.
166      */
167     private SocketWrapper() {}
168 
169   }
170 
171   /**
172    * The Class SocketRunnable.
173    */
174   static final class SocketRunnable implements Runnable {
175 
176     /** The socket wrapper. */
177     private final SocketWrapper socketWrapper;
178 
179     /** The sync. */
180     private final Object sync;
181 
182     /**
183      * Instantiates a new socket runnable.
184      *
185      * @param socketWrapper the socket wrapper
186      * @param sync the sync
187      */
188     private SocketRunnable(SocketWrapper socketWrapper, Object sync) {
189       this.socketWrapper = socketWrapper;
190       this.sync = sync;
191     }
192 
193     @Override
194     public void run() {
195       try (Socket socket = new Socket(socketWrapper.getServer(), socketWrapper.getPort())) {
196         socketWrapper.setSocket(socket);
197         if (!socketWrapper.isValid()) {
198           socketWrapper.getSocket().close();
199           socketWrapper.setSocket(null);
200         }
201       } catch (IOException e) {
202         logger.trace("", e);
203         socketWrapper.setException(e);
204       }
205       synchronized (sync) {
206         sync.notifyAll();
207       }
208     }
209 
210   }
211 
212   /**
213    * The Class TimeoutRunnable.
214    */
215   static final class TimeoutRunnable implements Runnable {
216 
217     /** The sync. */
218     private final Object sync;
219 
220     /** The timeout. */
221     private final long timeout;
222 
223     /**
224      * Instantiates a new timeout runnable.
225      *
226      * @param sync the sync
227      * @param timeout the timeout
228      */
229     private TimeoutRunnable(Object sync, long timeout) {
230       this.sync = sync;
231       this.timeout = timeout;
232     }
233 
234     @Override
235     public void run() {
236       try {
237         Thread.sleep(timeout);
238         synchronized (sync) {
239           sync.notifyAll();
240         }
241       } catch (InterruptedException e) {
242         // Restore interrupted state...
243         Thread.currentThread().interrupt();
244         logger.trace("", e);
245       }
246     }
247 
248   }
249 
250 }