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.Inet4Address;
23 import java.net.Inet6Address;
24 import java.net.InetAddress;
25 import java.net.InetSocketAddress;
26 import java.net.SocketAddress;
27 import java.nio.channels.ClosedSelectorException;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Queue;
35 import java.util.Set;
36 import java.util.concurrent.ConcurrentLinkedQueue;
37 import java.util.concurrent.Executor;
38 import java.util.concurrent.Semaphore;
39 import org.apache.mina.core.RuntimeIoException;
40 import org.apache.mina.core.buffer.IoBuffer;
41 import org.apache.mina.core.service.AbstractIoAcceptor;
42 import org.apache.mina.core.service.IoAcceptor;
43 import org.apache.mina.core.service.IoProcessor;
44 import org.apache.mina.core.session.AbstractIoSession;
45 import org.apache.mina.core.session.ExpiringSessionRecycler;
46 import org.apache.mina.core.session.IoSession;
47 import org.apache.mina.core.session.IoSessionConfig;
48 import org.apache.mina.core.session.IoSessionRecycler;
49 import org.apache.mina.core.write.WriteRequest;
50 import org.apache.mina.core.write.WriteRequestQueue;
51 import org.apache.mina.util.ExceptionMonitor;
52
53
54
55
56
57
58
59
60
61 public abstract class AbstractPollingConnectionlessIoAcceptor<S extends AbstractIoSession, H>
62 extends AbstractIoAcceptor {
63
64 private static final IoSessionRecycler DEFAULT_RECYCLER = new ExpiringSessionRecycler();
65
66
67
68
69
70 private static final long SELECT_TIMEOUT = 1000L;
71
72
73 private final Semaphore lock = new Semaphore(1);
74
75 private final IoProcessor<S> processor = new ConnectionlessAcceptorProcessor();
76 private final Queue<AcceptorOperationFuture> registerQueue =
77 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
78 private final Queue<AcceptorOperationFuture> cancelQueue =
79 new ConcurrentLinkedQueue<AcceptorOperationFuture>();
80
81 private final Queue<S> flushingSessions = new ConcurrentLinkedQueue<S>();
82 private final Map<String, H> boundHandles =
83 Collections.synchronizedMap(new HashMap<String, H>());
84
85 private IoSessionRecycler sessionRecycler = DEFAULT_RECYCLER;
86
87 private final ServiceOperationFuture disposalFuture =
88 new ServiceOperationFuture();
89 private volatile boolean selectable;
90
91
92 private Acceptor acceptor;
93
94 private long lastIdleCheckTime;
95
96 private String getAddressAsString(SocketAddress address) {
97 InetAddress inetAddress = ((InetSocketAddress)address).getAddress();
98 int port = ((InetSocketAddress)address).getPort();
99
100 if (inetAddress == null) {
101 return "null";
102 }
103
104 String result = null;
105
106 if ( inetAddress instanceof Inet4Address ) {
107 result = "/" + inetAddress.getHostAddress() + ":" + port;
108 } else {
109
110 if ( ((Inet6Address)inetAddress).isIPv4CompatibleAddress() ) {
111 byte[] bytes = inetAddress.getAddress();
112
113 result = "/" + bytes[12] + "." + bytes[13] + "." + bytes[14] + "." + bytes[15] + ":" + port;
114 } else {
115 result = inetAddress.toString();
116 }
117 }
118
119 return result;
120 }
121
122
123
124
125 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig) {
126 this(sessionConfig, null);
127 }
128
129
130
131
132 protected AbstractPollingConnectionlessIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
133 super(sessionConfig, executor);
134
135 try {
136 init();
137 selectable = true;
138 } catch (RuntimeException e) {
139 throw e;
140 } catch (Exception e) {
141 throw new RuntimeIoException("Failed to initialize.", e);
142 } finally {
143 if (!selectable) {
144 try {
145 destroy();
146 } catch (Exception e) {
147 ExceptionMonitor.getInstance().exceptionCaught(e);
148 }
149 }
150 }
151 }
152
153 protected abstract void init() throws Exception;
154 protected abstract void destroy() throws Exception;
155 protected abstract int select() throws Exception;
156 protected abstract int select(long timeout) throws Exception;
157 protected abstract void wakeup();
158 protected abstract Iterator<H> selectedHandles();
159 protected abstract H open(SocketAddress localAddress) throws Exception;
160 protected abstract void close(H handle) throws Exception;
161 protected abstract SocketAddress localAddress(H handle) throws Exception;
162 protected abstract boolean isReadable(H handle);
163 protected abstract boolean isWritable(H handle);
164 protected abstract SocketAddress receive(H handle, IoBuffer buffer) throws Exception;
165
166 protected abstract int send(S session, IoBuffer buffer, SocketAddress remoteAddress) throws Exception;
167
168 protected abstract S newSession(IoProcessor<S> processor, H handle, SocketAddress remoteAddress) throws Exception;
169
170 protected abstract void setInterestedInWrite(S session, boolean interested) throws Exception;
171
172
173
174
175 @Override
176 protected void dispose0() throws Exception {
177 unbind();
178 startupAcceptor();
179 wakeup();
180 }
181
182
183
184
185 @Override
186 protected final Set<SocketAddress> bindInternal(
187 List<? extends SocketAddress> localAddresses) throws Exception {
188
189
190 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
191
192
193
194 registerQueue.add(request);
195
196
197
198 startupAcceptor();
199
200
201
202
203 try
204 {
205 lock.acquire();
206
207
208 Thread.sleep( 10 );
209 wakeup();
210 }
211 finally
212 {
213 lock.release();
214 }
215
216
217 request.awaitUninterruptibly();
218
219 if (request.getException() != null) {
220 throw request.getException();
221 }
222
223
224
225
226 Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
227
228 for (H handle : boundHandles.values()) {
229 newLocalAddresses.add(localAddress(handle));
230 }
231
232 return newLocalAddresses;
233 }
234
235
236
237
238 @Override
239 protected final void unbind0(List<? extends SocketAddress> localAddresses) throws Exception {
240 AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
241
242 cancelQueue.add(request);
243 startupAcceptor();
244 wakeup();
245
246 request.awaitUninterruptibly();
247
248 if (request.getException() != null) {
249 throw request.getException();
250 }
251 }
252
253
254
255
256 public final IoSession newSession(SocketAddress remoteAddress, SocketAddress localAddress) {
257 if (isDisposing()) {
258 throw new IllegalStateException("Already disposed.");
259 }
260
261 if (remoteAddress == null) {
262 throw new IllegalArgumentException("remoteAddress");
263 }
264
265 synchronized (bindLock) {
266 if (!isActive()) {
267 throw new IllegalStateException(
268 "Can't create a session from a unbound service.");
269 }
270
271 try {
272 return newSessionWithoutLock(remoteAddress, localAddress);
273 } catch (RuntimeException e) {
274 throw e;
275 } catch (Error e) {
276 throw e;
277 } catch (Exception e) {
278 throw new RuntimeIoException("Failed to create a session.", e);
279 }
280 }
281 }
282
283 private IoSession newSessionWithoutLock(
284 SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
285 H handle = boundHandles.get(getAddressAsString(localAddress));
286
287 if (handle == null) {
288 throw new IllegalArgumentException("Unknown local address: " + localAddress);
289 }
290
291 IoSession session;
292 IoSessionRecycler sessionRecycler = getSessionRecycler();
293
294 synchronized (sessionRecycler) {
295 session = sessionRecycler.recycle(localAddress, remoteAddress);
296
297 if (session != null) {
298 return session;
299 }
300
301
302 S newSession = newSession(processor, handle, remoteAddress);
303 getSessionRecycler().put(newSession);
304 session = newSession;
305 }
306
307 initSession(session, null, null);
308
309 try {
310 this.getFilterChainBuilder().buildFilterChain(session.getFilterChain());
311 getListeners().fireSessionCreated(session);
312 } catch (Throwable t) {
313 ExceptionMonitor.getInstance().exceptionCaught(t);
314 }
315
316 return session;
317 }
318
319 public final IoSessionRecycler getSessionRecycler() {
320 return sessionRecycler;
321 }
322
323 public final void setSessionRecycler(IoSessionRecycler sessionRecycler) {
324 synchronized (bindLock) {
325 if (isActive()) {
326 throw new IllegalStateException(
327 "sessionRecycler can't be set while the acceptor is bound.");
328 }
329
330 if (sessionRecycler == null) {
331 sessionRecycler = DEFAULT_RECYCLER;
332 }
333
334 this.sessionRecycler = sessionRecycler;
335 }
336 }
337
338 private class ConnectionlessAcceptorProcessor implements IoProcessor<S> {
339
340 public void add(S session) {
341 }
342
343 public void flush(S session) {
344 if (scheduleFlush(session)) {
345 wakeup();
346 }
347 }
348
349 public void remove(S session) {
350 getSessionRecycler().remove(session);
351 getListeners().fireSessionDestroyed(session);
352 }
353
354 public void updateTrafficControl(S session) {
355 throw new UnsupportedOperationException();
356 }
357
358 public void dispose() {
359 }
360
361 public boolean isDisposed() {
362 return false;
363 }
364
365 public boolean isDisposing() {
366 return false;
367 }
368 }
369
370
371
372
373 private void startupAcceptor() throws InterruptedException {
374 if (!selectable) {
375 registerQueue.clear();
376 cancelQueue.clear();
377 flushingSessions.clear();
378 }
379
380 lock.acquire();
381
382 if (acceptor == null) {
383 acceptor = new Acceptor();
384 executeWorker(acceptor);
385 } else {
386 lock.release();
387 }
388 }
389
390 private boolean scheduleFlush(S session) {
391
392
393
394 if (session.setScheduledForFlush(true)) {
395 flushingSessions.add(session);
396 return true;
397 } else {
398 return false;
399 }
400 }
401
402
403
404
405
406
407 private class Acceptor implements Runnable {
408 public void run() {
409 int nHandles = 0;
410 lastIdleCheckTime = System.currentTimeMillis();
411
412
413 lock.release();
414
415 while (selectable) {
416 try {
417 int selected = select(SELECT_TIMEOUT);
418
419 nHandles += registerHandles();
420
421 if (nHandles == 0) {
422 try {
423 lock.acquire();
424
425 if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
426 acceptor = null;
427 break;
428 }
429 }
430 finally
431 {
432 lock.release();
433 }
434 }
435
436 if (selected > 0) {
437 processReadySessions(selectedHandles());
438 }
439
440 long currentTime = System.currentTimeMillis();
441 flushSessions(currentTime);
442 nHandles -= unregisterHandles();
443
444 notifyIdleSessions(currentTime);
445 } catch (ClosedSelectorException cse) {
446
447 break;
448 } catch (Exception e) {
449 ExceptionMonitor.getInstance().exceptionCaught(e);
450
451 try {
452 Thread.sleep(1000);
453 } catch (InterruptedException e1) {
454 }
455 }
456 }
457
458 if (selectable && isDisposing()) {
459 selectable = false;
460 try {
461 destroy();
462 } catch (Exception e) {
463 ExceptionMonitor.getInstance().exceptionCaught(e);
464 } finally {
465 disposalFuture.setValue(true);
466 }
467 }
468 }
469 }
470
471 @SuppressWarnings("unchecked")
472 private void processReadySessions(Iterator<H> handles) {
473 while (handles.hasNext()) {
474 H h = handles.next();
475 handles.remove();
476
477 try {
478 if (isReadable(h)) {
479 readHandle(h);
480 }
481
482 if (isWritable(h)) {
483 for (IoSession session : getManagedSessions().values()) {
484 scheduleFlush((S) session);
485 }
486 }
487 } catch (Throwable t) {
488 ExceptionMonitor.getInstance().exceptionCaught(t);
489 }
490 }
491 }
492
493 private void readHandle(H handle) throws Exception {
494 IoBuffer readBuf = IoBuffer.allocate(
495 getSessionConfig().getReadBufferSize());
496
497 SocketAddress remoteAddress = receive(handle, readBuf);
498
499 if (remoteAddress != null) {
500 IoSession session = newSessionWithoutLock(
501 remoteAddress, localAddress(handle));
502
503 readBuf.flip();
504
505 IoBuffer newBuf = IoBuffer.allocate(readBuf.limit());
506 newBuf.put(readBuf);
507 newBuf.flip();
508
509 session.getFilterChain().fireMessageReceived(newBuf);
510 }
511 }
512
513 private void flushSessions(long currentTime) {
514 for (;;) {
515 S session = flushingSessions.poll();
516
517 if (session == null) {
518 break;
519 }
520
521
522
523 session.unscheduledForFlush();
524
525 try {
526 boolean flushedAll = flush(session, currentTime);
527 if (flushedAll && !session.getWriteRequestQueue().isEmpty(session) &&
528 !session.isScheduledForFlush()) {
529 scheduleFlush(session);
530 }
531 } catch (Exception e) {
532 session.getFilterChain().fireExceptionCaught(e);
533 }
534 }
535 }
536
537 private boolean flush(S session, long currentTime) throws Exception {
538
539 setInterestedInWrite(session, false);
540
541 final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
542 final int maxWrittenBytes =
543 session.getConfig().getMaxReadBufferSize() +
544 (session.getConfig().getMaxReadBufferSize() >>> 1);
545
546 int writtenBytes = 0;
547
548 try {
549 for (;;) {
550 WriteRequest req = session.getCurrentWriteRequest();
551
552 if (req == null) {
553 req = writeRequestQueue.poll(session);
554 if (req == null) {
555 break;
556 }
557 session.setCurrentWriteRequest(req);
558 }
559
560 IoBuffer buf = (IoBuffer) req.getMessage();
561
562 if (buf.remaining() == 0) {
563
564 session.setCurrentWriteRequest(null);
565 buf.reset();
566 session.getFilterChain().fireMessageSent(req);
567 continue;
568 }
569
570 SocketAddress destination = req.getDestination();
571
572 if (destination == null) {
573 destination = session.getRemoteAddress();
574 }
575
576 int localWrittenBytes = send(session, buf, destination);
577
578 if (( localWrittenBytes == 0 ) || ( writtenBytes >= maxWrittenBytes )) {
579
580 setInterestedInWrite(session, true);
581 return false;
582 } else {
583 setInterestedInWrite(session, false);
584
585
586 session.setCurrentWriteRequest(null);
587 writtenBytes += localWrittenBytes;
588 buf.reset();
589 session.getFilterChain().fireMessageSent(req);
590 }
591 }
592 } finally {
593 session.increaseWrittenBytes(writtenBytes, currentTime);
594 }
595
596 return true;
597 }
598
599 private int registerHandles() {
600 for (;;) {
601 AcceptorOperationFuture req = registerQueue.poll();
602
603 if (req == null) {
604 break;
605 }
606
607 Map<String, H> newHandles = new HashMap<String, H>();
608 List<SocketAddress> localAddresses = req.getLocalAddresses();
609
610 try {
611 for (SocketAddress socketAddress : localAddresses) {
612 H handle = open(socketAddress);
613 newHandles.put(getAddressAsString(localAddress(handle)), handle);
614 }
615
616 boundHandles.putAll(newHandles);
617
618 getListeners().fireServiceActivated();
619 req.setDone();
620
621 return newHandles.size();
622 } catch (Exception e) {
623 req.setException(e);
624 } finally {
625
626 if (req.getException() != null) {
627 for (H handle : newHandles.values()) {
628 try {
629 close(handle);
630 } catch (Exception e) {
631 ExceptionMonitor.getInstance().exceptionCaught(e);
632 }
633 }
634
635 wakeup();
636 }
637 }
638 }
639
640 return 0;
641 }
642
643 private int unregisterHandles() {
644 int nHandles = 0;
645
646 for (;;) {
647 AcceptorOperationFuture request = cancelQueue.poll();
648 if (request == null) {
649 break;
650 }
651
652
653 for (SocketAddress socketAddress : request.getLocalAddresses()) {
654 H handle = boundHandles.remove(getAddressAsString(socketAddress));
655
656 if (handle == null) {
657 continue;
658 }
659
660 try {
661 close(handle);
662 wakeup();
663 } catch (Throwable e) {
664 ExceptionMonitor.getInstance().exceptionCaught(e);
665 } finally {
666 nHandles++;
667 }
668 }
669
670 request.setDone();
671 }
672
673 return nHandles;
674 }
675
676 private void notifyIdleSessions(long currentTime) {
677
678 if (currentTime - lastIdleCheckTime >= 1000) {
679 lastIdleCheckTime = currentTime;
680 AbstractIoSession.notifyIdleness(
681 getListeners().getManagedSessions().values().iterator(),
682 currentTime);
683 }
684 }
685 }