1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.core.polling;
21
22 import java.net.ConnectException;
23 import java.net.SocketAddress;
24 import java.util.Iterator;
25 import java.util.Queue;
26 import java.util.concurrent.ConcurrentLinkedQueue;
27 import java.util.concurrent.Executor;
28 import java.util.concurrent.Executors;
29
30 import org.apache.mina.core.RuntimeIoException;
31 import org.apache.mina.core.filterchain.IoFilter;
32 import org.apache.mina.core.future.ConnectFuture;
33 import org.apache.mina.core.future.DefaultConnectFuture;
34 import org.apache.mina.core.future.IoFuture;
35 import org.apache.mina.core.service.AbstractIoConnector;
36 import org.apache.mina.core.service.IoConnector;
37 import org.apache.mina.core.service.IoHandler;
38 import org.apache.mina.core.service.IoProcessor;
39 import org.apache.mina.core.service.SimpleIoProcessorPool;
40 import org.apache.mina.core.session.AbstractIoSession;
41 import org.apache.mina.core.session.IoSession;
42 import org.apache.mina.core.session.IoSessionConfig;
43 import org.apache.mina.core.session.IoSessionInitializer;
44 import org.apache.mina.transport.socket.nio.NioSocketConnector;
45 import org.apache.mina.util.ExceptionMonitor;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public abstract class AbstractPollingIoConnector<T extends AbstractIoSession, H>
66 extends AbstractIoConnector {
67
68 private final Object lock = new Object();
69 private final Queue<ConnectionRequest> connectQueue = new ConcurrentLinkedQueue<ConnectionRequest>();
70 private final Queue<ConnectionRequest> cancelQueue = new ConcurrentLinkedQueue<ConnectionRequest>();
71 private final IoProcessor<T> processor;
72 private final boolean createdProcessor;
73
74 private final ServiceOperationFuture disposalFuture =
75 new ServiceOperationFuture();
76 private volatile boolean selectable;
77
78
79 private Connector connector;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Class<? extends IoProcessor<T>> processorClass) {
95 this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass), true);
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Class<? extends IoProcessor<T>> processorClass, int processorCount) {
113 this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass, processorCount), true);
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127
128 protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, IoProcessor<T> processor) {
129 this(sessionConfig, null, processor, false);
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Executor executor, IoProcessor<T> processor) {
149 this(sessionConfig, executor, processor, false);
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 private AbstractPollingIoConnector(IoSessionConfig sessionConfig, Executor executor, IoProcessor<T> processor, boolean createdProcessor) {
170 super(sessionConfig, executor);
171
172 if (processor == null) {
173 throw new NullPointerException("processor");
174 }
175
176 this.processor = processor;
177 this.createdProcessor = createdProcessor;
178
179 try {
180 init();
181 selectable = true;
182 } catch (RuntimeException e){
183 throw e;
184 } catch (Exception e) {
185 throw new RuntimeIoException("Failed to initialize.", e);
186 } finally {
187 if (!selectable) {
188 try {
189 destroy();
190 } catch (Exception e) {
191 ExceptionMonitor.getInstance().exceptionCaught(e);
192 }
193 }
194 }
195 }
196
197
198
199
200
201 protected abstract void init() throws Exception;
202
203
204
205
206
207
208 protected abstract void destroy() throws Exception;
209
210
211
212
213
214
215
216 protected abstract H newHandle(SocketAddress localAddress) throws Exception;
217
218
219
220
221
222
223
224
225
226
227
228 protected abstract boolean connect(H handle, SocketAddress remoteAddress) throws Exception;
229
230
231
232
233
234
235
236
237
238 protected abstract boolean finishConnect(H handle) throws Exception;
239
240
241
242
243
244
245
246
247
248
249 protected abstract T newSession(IoProcessor<T> processor, H handle) throws Exception;
250
251
252
253
254
255
256 protected abstract void close(H handle) throws Exception;
257
258
259
260
261 protected abstract void wakeup();
262
263
264
265
266
267
268
269
270 protected abstract int select(int timeout) throws Exception;
271
272
273
274
275
276
277 protected abstract Iterator<H> selectedHandles();
278
279
280
281
282
283 protected abstract Iterator<H> allHandles();
284
285
286
287
288
289
290
291 protected abstract void register(H handle, ConnectionRequest request) throws Exception;
292
293
294
295
296
297
298 protected abstract ConnectionRequest getConnectionRequest(H handle);
299
300
301
302
303 @Override
304 protected final IoFuture dispose0() throws Exception {
305 if (!disposalFuture.isDone()) {
306 startupWorker();
307 wakeup();
308 }
309 return disposalFuture;
310 }
311
312
313
314
315 @Override
316 @SuppressWarnings("unchecked")
317 protected final ConnectFuture connect0(
318 SocketAddress remoteAddress, SocketAddress localAddress,
319 IoSessionInitializer<? extends ConnectFuture> sessionInitializer) {
320 H handle = null;
321 boolean success = false;
322 try {
323 handle = newHandle(localAddress);
324 if (connect(handle, remoteAddress)) {
325 ConnectFuture future = new DefaultConnectFuture();
326 T session = newSession(processor, handle);
327 finishSessionInitialization(session, future, sessionInitializer);
328
329 session.getProcessor().add(session);
330 success = true;
331 return future;
332 }
333
334 success = true;
335 } catch (Exception e) {
336 return DefaultConnectFuture.newFailedFuture(e);
337 } finally {
338 if (!success && handle != null) {
339 try {
340 close(handle);
341 } catch (Exception e) {
342 ExceptionMonitor.getInstance().exceptionCaught(e);
343 }
344 }
345 }
346
347 ConnectionRequest request = new ConnectionRequest(handle, sessionInitializer);
348 connectQueue.add(request);
349 startupWorker();
350 wakeup();
351
352 return request;
353 }
354
355 private void startupWorker() {
356 if (!selectable) {
357 connectQueue.clear();
358 cancelQueue.clear();
359 }
360
361 synchronized (lock) {
362 if (connector == null) {
363 connector = new Connector();
364 executeWorker(connector);
365 }
366 }
367 }
368
369 private int registerNew() {
370 int nHandles = 0;
371 for (; ;) {
372 ConnectionRequest req = connectQueue.poll();
373 if (req == null) {
374 break;
375 }
376
377 H handle = req.handle;
378 try {
379 register(handle, req);
380 nHandles ++;
381 } catch (Exception e) {
382 req.setException(e);
383 try {
384 close(handle);
385 } catch (Exception e2) {
386 ExceptionMonitor.getInstance().exceptionCaught(e2);
387 }
388 }
389 }
390 return nHandles;
391 }
392
393 private int cancelKeys() {
394 int nHandles = 0;
395 for (; ;) {
396 ConnectionRequest req = cancelQueue.poll();
397 if (req == null) {
398 break;
399 }
400
401 H handle = req.handle;
402 try {
403 close(handle);
404 } catch (Exception e) {
405 ExceptionMonitor.getInstance().exceptionCaught(e);
406 } finally {
407 nHandles ++;
408 }
409 }
410 return nHandles;
411 }
412
413
414
415
416
417 private int processConnections(Iterator<H> handlers) {
418 int nHandles = 0;
419
420
421 while (handlers.hasNext()) {
422 H handle = handlers.next();
423 handlers.remove();
424
425 ConnectionRequest connectionRequest = getConnectionRequest(handle);
426
427 if ( connectionRequest == null) {
428 continue;
429 }
430
431 boolean success = false;
432 try {
433 if (finishConnect(handle)) {
434 T session = newSession(processor, handle);
435 finishSessionInitialization(session, connectionRequest, connectionRequest.getSessionInitializer());
436
437 session.getProcessor().add(session);
438 nHandles ++;
439 }
440 success = true;
441 } catch (Throwable e) {
442 connectionRequest.setException(e);
443 } finally {
444 if (!success) {
445
446 cancelQueue.offer(connectionRequest);
447 }
448 }
449 }
450 return nHandles;
451 }
452
453 private void processTimedOutSessions(Iterator<H> handles) {
454 long currentTime = System.currentTimeMillis();
455
456 while (handles.hasNext()) {
457 H handle = handles.next();
458 ConnectionRequest connectionRequest = getConnectionRequest(handle);
459
460 if ((connectionRequest != null) && (currentTime >= connectionRequest.deadline)) {
461 connectionRequest.setException(
462 new ConnectException("Connection timed out."));
463 cancelQueue.offer(connectionRequest);
464 }
465 }
466 }
467
468 private class Connector implements Runnable {
469
470 public void run() {
471 int nHandles = 0;
472 while (selectable) {
473 try {
474
475
476 int timeout = (int)Math.min(getConnectTimeoutMillis(), 1000L);
477 int selected = select(timeout);
478
479 nHandles += registerNew();
480
481 if (selected > 0) {
482 nHandles -= processConnections(selectedHandles());
483 }
484
485 processTimedOutSessions(allHandles());
486
487 nHandles -= cancelKeys();
488
489 if (nHandles == 0) {
490 synchronized (lock) {
491 if (connectQueue.isEmpty()) {
492 connector = null;
493 break;
494 }
495 }
496 }
497 } catch (Throwable e) {
498 ExceptionMonitor.getInstance().exceptionCaught(e);
499
500 try {
501 Thread.sleep(1000);
502 } catch (InterruptedException e1) {
503 ExceptionMonitor.getInstance().exceptionCaught(e1);
504 }
505 }
506 }
507
508 if (selectable && isDisposing()) {
509 selectable = false;
510 try {
511 if (createdProcessor) {
512 processor.dispose();
513 }
514 } finally {
515 try {
516 synchronized (disposalLock) {
517 if (isDisposing()) {
518 destroy();
519 }
520 }
521 } catch (Exception e) {
522 ExceptionMonitor.getInstance().exceptionCaught(e);
523 } finally {
524 disposalFuture.setDone();
525 }
526 }
527 }
528 }
529 }
530
531 public final class ConnectionRequest extends DefaultConnectFuture {
532 private final H handle;
533 private final long deadline;
534 private final IoSessionInitializer<? extends ConnectFuture> sessionInitializer;
535
536 public ConnectionRequest(H handle, IoSessionInitializer<? extends ConnectFuture> callback) {
537 this.handle = handle;
538 long timeout = getConnectTimeoutMillis();
539 if (timeout <= 0L) {
540 this.deadline = Long.MAX_VALUE;
541 } else {
542 this.deadline = System.currentTimeMillis() + timeout;
543 }
544 this.sessionInitializer = callback;
545 }
546
547 public H getHandle() {
548 return handle;
549 }
550
551 public long getDeadline() {
552 return deadline;
553 }
554
555 public IoSessionInitializer<? extends ConnectFuture> getSessionInitializer() {
556 return sessionInitializer;
557 }
558
559 @Override
560 public void cancel() {
561 super.cancel();
562 cancelQueue.add(this);
563 startupWorker();
564 wakeup();
565 }
566 }
567 }