1
2
3
4
5
6
7
8
9
10
11 package psiprobe.tools;
12
13 import java.io.IOException;
14 import java.net.Socket;
15 import java.util.concurrent.locks.ReentrantLock;
16
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20
21
22
23 public final class AsyncSocketFactory {
24
25
26 private static final Logger logger = LoggerFactory.getLogger(AsyncSocketFactory.class);
27
28
29
30
31 private AsyncSocketFactory() {
32
33 }
34
35
36
37
38
39
40
41
42
43
44
45
46 public static Socket createSocket(String server, int port, long timeout) throws IOException {
47 SocketWrapper socketWrapper = new SocketWrapper();
48 socketWrapper.server = server;
49 socketWrapper.port = port;
50
51 ReentrantLock sync = new ReentrantLock();
52 Thread socketThread = new Thread(new SocketRunnable(socketWrapper, sync));
53 socketThread.setDaemon(true);
54 Thread timeoutThread = new Thread(new TimeoutRunnable(sync, timeout * 1000));
55 timeoutThread.setDaemon(true);
56
57 timeoutThread.start();
58 socketThread.start();
59
60 sync.lock();
61 try {
62 if (socketWrapper.socket == null) {
63 boolean inProgress = true;
64 while (inProgress) {
65 try {
66 sync.wait(timeout * 1000);
67 } catch (InterruptedException e) {
68
69 Thread.currentThread().interrupt();
70 logger.trace("", e);
71 }
72 inProgress = false;
73 }
74 }
75 } finally {
76 sync.unlock();
77 }
78
79 timeoutThread.interrupt();
80 socketThread.interrupt();
81
82 socketWrapper.valid = false;
83
84 if (socketWrapper.socket == null && socketWrapper.exception != null) {
85 throw socketWrapper.exception;
86 }
87 if (socketWrapper.socket == null) {
88 throw new TimeoutException();
89 }
90
91 return socketWrapper.getSocket();
92 }
93
94
95
96
97 static final class SocketWrapper {
98
99
100 Socket socket;
101
102
103 String server;
104
105
106 int port;
107
108
109 IOException exception;
110
111
112 boolean valid = true;
113
114
115
116
117
118
119 public Socket getSocket() {
120 return socket;
121 }
122
123
124
125
126
127
128 public void setSocket(Socket socket) {
129 this.socket = socket;
130 }
131
132
133
134
135
136
137 public String getServer() {
138 return server;
139 }
140
141
142
143
144
145
146 public int getPort() {
147 return port;
148 }
149
150
151
152
153
154
155 public void setException(IOException exception) {
156 this.exception = exception;
157 }
158
159
160
161
162
163
164 public boolean isValid() {
165 return valid;
166 }
167
168
169
170
171 private SocketWrapper() {}
172
173 }
174
175
176
177
178 static final class SocketRunnable implements Runnable {
179
180
181 private final SocketWrapper socketWrapper;
182
183
184 private final ReentrantLock sync;
185
186
187
188
189
190
191
192 private SocketRunnable(SocketWrapper socketWrapper, ReentrantLock sync) {
193 this.socketWrapper = socketWrapper;
194 this.sync = sync;
195 }
196
197 @Override
198 public void run() {
199 try (Socket socket = new Socket(socketWrapper.getServer(), socketWrapper.getPort())) {
200 socketWrapper.setSocket(socket);
201 if (!socketWrapper.isValid()) {
202 socketWrapper.getSocket().close();
203 socketWrapper.setSocket(null);
204 }
205 } catch (IOException e) {
206 logger.trace("", e);
207 socketWrapper.setException(e);
208 }
209 sync.lock();
210 try {
211 sync.notifyAll();
212 } finally {
213 sync.unlock();
214 }
215 }
216
217 }
218
219
220
221
222 static final class TimeoutRunnable implements Runnable {
223
224
225 private final ReentrantLock sync;
226
227
228 private final long timeout;
229
230
231
232
233
234
235
236 private TimeoutRunnable(ReentrantLock sync, long timeout) {
237 this.sync = sync;
238 this.timeout = timeout;
239 }
240
241 @Override
242 public void run() {
243 try {
244 Thread.sleep(timeout);
245 sync.lock();
246 try {
247 sync.notifyAll();
248 } finally {
249 sync.unlock();
250 }
251 } catch (InterruptedException e) {
252
253 Thread.currentThread().interrupt();
254 logger.trace("", e);
255 }
256 }
257
258 }
259
260 }