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