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