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