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
23 import java.net.SocketAddress;
24 import java.nio.channels.ClosedSelectorException;
25 import java.nio.channels.SelectionKey;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.Set;
34 import java.util.concurrent.ConcurrentLinkedQueue;
35 import java.util.concurrent.Executor;
36 import java.util.concurrent.Semaphore;
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
59
60
61 public abstract class AbstractPollingConnectionlessIoAcceptor<S extends AbstractIoSession, H> extends
62 AbstractIoAcceptor implements IoProcessor<S>
63 {
64
65 private static final IoSessionRecycler DEFAULT_RECYCLER = new ExpiringSessionRecycler();
66
67
68
69
70
71 private static final long SELECT_TIMEOUT = 1000L;
72
73
74 private final Semaphore lock = new Semaphore( 1 );
75
76 private final Queue<AcceptorOperationFuture> registerQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
77
78 private final Queue<AcceptorOperationFuture> cancelQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
79
80 private final Queue<S> flushingSessions = new ConcurrentLinkedQueue<S>();
81
82 private final Map<SocketAddress, H> boundHandles = Collections.synchronizedMap( new HashMap<SocketAddress, H>() );
83
84 private IoSessionRecycler sessionRecycler = DEFAULT_RECYCLER;
85
86 private final ServiceOperationFuture disposalFuture = new ServiceOperationFuture();
87
88 private volatile boolean selectable;
89
90
91 private Acceptor acceptor;
92
93 private long lastIdleCheckTime;
94
95
96
97
98
99 protected AbstractPollingConnectionlessIoAcceptor( IoSessionConfig sessionConfig )
100 {
101 this( sessionConfig, null );
102 }
103
104
105
106
107
108 protected AbstractPollingConnectionlessIoAcceptor( IoSessionConfig sessionConfig, Executor executor )
109 {
110 super( sessionConfig, executor );
111
112 try
113 {
114 init();
115 selectable = true;
116 }
117 catch ( RuntimeException e )
118 {
119 throw e;
120 }
121 catch ( Exception e )
122 {
123 throw new RuntimeIoException( "Failed to initialize.", e );
124 }
125 finally
126 {
127 if ( !selectable )
128 {
129 try
130 {
131 destroy();
132 }
133 catch ( Exception e )
134 {
135 ExceptionMonitor.getInstance().exceptionCaught( e );
136 }
137 }
138 }
139 }
140
141
142 protected abstract void init() throws Exception;
143
144
145 protected abstract void destroy() throws Exception;
146
147
148 protected abstract int select() throws Exception;
149
150
151 protected abstract int select( long timeout ) throws Exception;
152
153
154 protected abstract void wakeup();
155
156
157 protected abstract Set<SelectionKey> selectedHandles();
158
159
160 protected abstract H open( SocketAddress localAddress ) throws Exception;
161
162
163 protected abstract void close( H handle ) throws Exception;
164
165
166 protected abstract SocketAddress localAddress( H handle ) throws Exception;
167
168
169 protected abstract boolean isReadable( H handle );
170
171
172 protected abstract boolean isWritable( H handle );
173
174
175 protected abstract SocketAddress receive( H handle, IoBuffer buffer ) throws Exception;
176
177
178 protected abstract int send( S session, IoBuffer buffer, SocketAddress remoteAddress ) throws Exception;
179
180
181 protected abstract S newSession( IoProcessor<S> processor, H handle, SocketAddress remoteAddress ) throws Exception;
182
183
184 protected abstract void setInterestedInWrite( S session, boolean interested ) throws Exception;
185
186
187
188
189
190 @Override
191 protected void dispose0() throws Exception
192 {
193 unbind();
194 startupAcceptor();
195 wakeup();
196 }
197
198
199
200
201
202 @Override
203 protected final Set<SocketAddress> bindInternal( List<? extends SocketAddress> localAddresses ) throws Exception
204 {
205
206
207 AcceptorOperationFuture request = new AcceptorOperationFuture( localAddresses );
208
209
210
211 registerQueue.add( request );
212
213
214
215 startupAcceptor();
216
217
218
219
220 try
221 {
222 lock.acquire();
223
224
225 Thread.sleep( 10 );
226 wakeup();
227 }
228 finally
229 {
230 lock.release();
231 }
232
233
234 request.awaitUninterruptibly();
235
236 if ( request.getException() != null )
237 {
238 throw request.getException();
239 }
240
241
242
243
244 Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
245
246 for ( H handle : boundHandles.values() )
247 {
248 newLocalAddresses.add( localAddress( handle ) );
249 }
250
251 return newLocalAddresses;
252 }
253
254
255
256
257
258 @Override
259 protected final void unbind0( List<? extends SocketAddress> localAddresses ) throws Exception
260 {
261 AcceptorOperationFuture request = new AcceptorOperationFuture( localAddresses );
262
263 cancelQueue.add( request );
264 startupAcceptor();
265 wakeup();
266
267 request.awaitUninterruptibly();
268
269 if ( request.getException() != null )
270 {
271 throw request.getException();
272 }
273 }
274
275
276
277
278
279 public final IoSession newSession( SocketAddress remoteAddress, SocketAddress localAddress )
280 {
281 if ( isDisposing() )
282 {
283 throw new IllegalStateException( "Already disposed." );
284 }
285
286 if ( remoteAddress == null )
287 {
288 throw new IllegalArgumentException( "remoteAddress" );
289 }
290
291 synchronized ( bindLock )
292 {
293 if ( !isActive() )
294 {
295 throw new IllegalStateException( "Can't create a session from a unbound service." );
296 }
297
298 try
299 {
300 return newSessionWithoutLock( remoteAddress, localAddress );
301 }
302 catch ( RuntimeException e )
303 {
304 throw e;
305 }
306 catch ( Error e )
307 {
308 throw e;
309 }
310 catch ( Exception e )
311 {
312 throw new RuntimeIoException( "Failed to create a session.", e );
313 }
314 }
315 }
316
317
318 private IoSession newSessionWithoutLock( SocketAddress remoteAddress, SocketAddress localAddress ) throws Exception
319 {
320 H handle = boundHandles.get( localAddress );
321
322 if ( handle == null )
323 {
324 throw new IllegalArgumentException( "Unknown local address: " + localAddress );
325 }
326
327 IoSession session;
328
329 synchronized ( sessionRecycler )
330 {
331 session = sessionRecycler.recycle( remoteAddress );
332
333 if ( session != null )
334 {
335 return session;
336 }
337
338
339 S newSession = newSession( this, handle, remoteAddress );
340 getSessionRecycler().put( newSession );
341 session = newSession;
342 }
343
344 initSession( session, null, null );
345
346 try
347 {
348 this.getFilterChainBuilder().buildFilterChain( session.getFilterChain() );
349 getListeners().fireSessionCreated( session );
350 }
351 catch ( Throwable t )
352 {
353 ExceptionMonitor.getInstance().exceptionCaught( t );
354 }
355
356 return session;
357 }
358
359
360 public final IoSessionRecycler getSessionRecycler()
361 {
362 return sessionRecycler;
363 }
364
365
366 public final void setSessionRecycler( IoSessionRecycler sessionRecycler )
367 {
368 synchronized ( bindLock )
369 {
370 if ( isActive() )
371 {
372 throw new IllegalStateException( "sessionRecycler can't be set while the acceptor is bound." );
373 }
374
375 if ( sessionRecycler == null )
376 {
377 sessionRecycler = DEFAULT_RECYCLER;
378 }
379
380 this.sessionRecycler = sessionRecycler;
381 }
382 }
383
384
385
386
387
388 public void add( S session )
389 {
390
391 }
392
393
394
395
396
397 public void flush( S session )
398 {
399 if ( scheduleFlush( session ) )
400 {
401 wakeup();
402 }
403 }
404
405
406
407
408
409 public void write( S session, WriteRequest writeRequest )
410 {
411
412 long currentTime = System.currentTimeMillis();
413 final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
414 final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize()
415 + ( session.getConfig().getMaxReadBufferSize() >>> 1 );
416
417 int writtenBytes = 0;
418
419
420
421 IoBuffer buf = ( IoBuffer ) writeRequest.getMessage();
422
423 if ( buf.remaining() == 0 )
424 {
425
426 session.setCurrentWriteRequest( null );
427 buf.reset();
428 session.getFilterChain().fireMessageSent( writeRequest );
429 return;
430 }
431
432
433 try
434 {
435 for ( ;; )
436 {
437 if ( writeRequest == null )
438 {
439 writeRequest = writeRequestQueue.poll( session );
440
441 if ( writeRequest == null )
442 {
443 setInterestedInWrite( session, false );
444 break;
445 }
446
447 session.setCurrentWriteRequest( writeRequest );
448 }
449
450 buf = ( IoBuffer ) writeRequest.getMessage();
451
452 if ( buf.remaining() == 0 )
453 {
454
455 session.setCurrentWriteRequest( null );
456 buf.reset();
457 session.getFilterChain().fireMessageSent( writeRequest );
458 continue;
459 }
460
461 SocketAddress destination = writeRequest.getDestination();
462
463 if ( destination == null )
464 {
465 destination = session.getRemoteAddress();
466 }
467
468 int localWrittenBytes = send( session, buf, destination );
469
470 if ( ( localWrittenBytes == 0 ) || ( writtenBytes >= maxWrittenBytes ) )
471 {
472
473 setInterestedInWrite( session, true );
474
475 session.getWriteRequestQueue().offer( session, writeRequest );
476 scheduleFlush( session );
477 }
478 else
479 {
480 setInterestedInWrite( session, false );
481
482
483 session.setCurrentWriteRequest( null );
484 writtenBytes += localWrittenBytes;
485 buf.reset();
486 session.getFilterChain().fireMessageSent( writeRequest );
487
488 break;
489 }
490 }
491 }
492 catch ( Exception e )
493 {
494 session.getFilterChain().fireExceptionCaught( e );
495 }
496 finally
497 {
498 session.increaseWrittenBytes( writtenBytes, currentTime );
499 }
500 }
501
502
503
504
505
506 public void remove( S session )
507 {
508 getSessionRecycler().remove( session );
509 getListeners().fireSessionDestroyed( session );
510 }
511
512
513
514
515
516 public void updateTrafficControl( S session )
517 {
518 throw new UnsupportedOperationException();
519 }
520
521
522
523
524
525 private void startupAcceptor() throws InterruptedException
526 {
527 if ( !selectable )
528 {
529 registerQueue.clear();
530 cancelQueue.clear();
531 flushingSessions.clear();
532 }
533
534 lock.acquire();
535
536 if ( acceptor == null )
537 {
538 acceptor = new Acceptor();
539 executeWorker( acceptor );
540 }
541 else
542 {
543 lock.release();
544 }
545 }
546
547
548 private boolean scheduleFlush( S session )
549 {
550
551
552
553 if ( session.setScheduledForFlush( true ) )
554 {
555 flushingSessions.add( session );
556 return true;
557 }
558 else
559 {
560 return false;
561 }
562 }
563
564
565
566
567
568
569 private class Acceptor implements Runnable
570 {
571 public void run()
572 {
573 int nHandles = 0;
574 lastIdleCheckTime = System.currentTimeMillis();
575
576
577 lock.release();
578
579 while ( selectable )
580 {
581 try
582 {
583 int selected = select( SELECT_TIMEOUT );
584
585 nHandles += registerHandles();
586
587 if ( nHandles == 0 )
588 {
589 try
590 {
591 lock.acquire();
592
593 if ( registerQueue.isEmpty() && cancelQueue.isEmpty() )
594 {
595 acceptor = null;
596 break;
597 }
598 }
599 finally
600 {
601 lock.release();
602 }
603 }
604
605 if ( selected > 0 )
606 {
607 processReadySessions( selectedHandles() );
608 }
609
610 long currentTime = System.currentTimeMillis();
611 flushSessions( currentTime );
612 nHandles -= unregisterHandles();
613
614 notifyIdleSessions( currentTime );
615 }
616 catch ( ClosedSelectorException cse )
617 {
618
619 break;
620 }
621 catch ( Exception e )
622 {
623 ExceptionMonitor.getInstance().exceptionCaught( e );
624
625 try
626 {
627 Thread.sleep( 1000 );
628 }
629 catch ( InterruptedException e1 )
630 {
631 }
632 }
633 }
634
635 if ( selectable && isDisposing() )
636 {
637 selectable = false;
638 try
639 {
640 destroy();
641 }
642 catch ( Exception e )
643 {
644 ExceptionMonitor.getInstance().exceptionCaught( e );
645 }
646 finally
647 {
648 disposalFuture.setValue( true );
649 }
650 }
651 }
652 }
653
654
655 @SuppressWarnings("unchecked")
656 private void processReadySessions( Set<SelectionKey> handles )
657 {
658 Iterator<SelectionKey> iterator = handles.iterator();
659
660 while ( iterator.hasNext() )
661 {
662 SelectionKey key = iterator.next();
663 H handle = ( H ) key.channel();
664 iterator.remove();
665
666 try
667 {
668 if ( ( key != null ) && key.isValid() && key.isReadable() )
669 {
670 readHandle( handle );
671 }
672
673 if ( ( key != null ) && key.isValid() && key.isWritable() )
674 {
675 for ( IoSession session : getManagedSessions().values() )
676 {
677 scheduleFlush( ( S ) session );
678 }
679 }
680 }
681 catch ( Throwable t )
682 {
683 ExceptionMonitor.getInstance().exceptionCaught( t );
684 }
685 }
686 }
687
688
689 private void readHandle( H handle ) throws Exception
690 {
691 IoBuffer readBuf = IoBuffer.allocate( getSessionConfig().getReadBufferSize() );
692
693 SocketAddress remoteAddress = receive( handle, readBuf );
694
695 if ( remoteAddress != null )
696 {
697 IoSession session = newSessionWithoutLock( remoteAddress, localAddress( handle ) );
698
699 readBuf.flip();
700
701 session.getFilterChain().fireMessageReceived( readBuf );
702 }
703 }
704
705
706 private void flushSessions( long currentTime )
707 {
708 for ( ;; )
709 {
710 S session = flushingSessions.poll();
711
712 if ( session == null )
713 {
714 break;
715 }
716
717
718
719 session.unscheduledForFlush();
720
721 try
722 {
723 boolean flushedAll = flush( session, currentTime );
724 if ( flushedAll && !session.getWriteRequestQueue().isEmpty( session ) && !session.isScheduledForFlush() )
725 {
726 scheduleFlush( session );
727 }
728 }
729 catch ( Exception e )
730 {
731 session.getFilterChain().fireExceptionCaught( e );
732 }
733 }
734 }
735
736
737 private boolean flush( S session, long currentTime ) throws Exception
738 {
739 final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
740 final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize()
741 + ( session.getConfig().getMaxReadBufferSize() >>> 1 );
742
743 int writtenBytes = 0;
744
745 try
746 {
747 for ( ;; )
748 {
749 WriteRequest req = session.getCurrentWriteRequest();
750
751 if ( req == null )
752 {
753 req = writeRequestQueue.poll( session );
754
755 if ( req == null )
756 {
757 setInterestedInWrite( session, false );
758 break;
759 }
760
761 session.setCurrentWriteRequest( req );
762 }
763
764 IoBuffer buf = ( IoBuffer ) req.getMessage();
765
766 if ( buf.remaining() == 0 )
767 {
768
769 session.setCurrentWriteRequest( null );
770 buf.reset();
771 session.getFilterChain().fireMessageSent( req );
772 continue;
773 }
774
775 SocketAddress destination = req.getDestination();
776
777 if ( destination == null )
778 {
779 destination = session.getRemoteAddress();
780 }
781
782 int localWrittenBytes = send( session, buf, destination );
783
784 if ( ( localWrittenBytes == 0 ) || ( writtenBytes >= maxWrittenBytes ) )
785 {
786
787 setInterestedInWrite( session, true );
788
789 return false;
790 }
791 else
792 {
793 setInterestedInWrite( session, false );
794
795
796 session.setCurrentWriteRequest( null );
797 writtenBytes += localWrittenBytes;
798 buf.reset();
799 session.getFilterChain().fireMessageSent( req );
800 }
801 }
802 }
803 finally
804 {
805 session.increaseWrittenBytes( writtenBytes, currentTime );
806 }
807
808 return true;
809 }
810
811
812 private int registerHandles()
813 {
814 for ( ;; )
815 {
816 AcceptorOperationFuture req = registerQueue.poll();
817
818 if ( req == null )
819 {
820 break;
821 }
822
823 Map<SocketAddress, H> newHandles = new HashMap<SocketAddress, H>();
824 List<SocketAddress> localAddresses = req.getLocalAddresses();
825
826 try
827 {
828 for ( SocketAddress socketAddress : localAddresses )
829 {
830 H handle = open( socketAddress );
831 newHandles.put( localAddress( handle ), handle );
832 }
833
834 boundHandles.putAll( newHandles );
835
836 getListeners().fireServiceActivated();
837 req.setDone();
838
839 return newHandles.size();
840 }
841 catch ( Exception e )
842 {
843 req.setException( e );
844 }
845 finally
846 {
847
848 if ( req.getException() != null )
849 {
850 for ( H handle : newHandles.values() )
851 {
852 try
853 {
854 close( handle );
855 }
856 catch ( Exception e )
857 {
858 ExceptionMonitor.getInstance().exceptionCaught( e );
859 }
860 }
861
862 wakeup();
863 }
864 }
865 }
866
867 return 0;
868 }
869
870
871 private int unregisterHandles()
872 {
873 int nHandles = 0;
874
875 for ( ;; )
876 {
877 AcceptorOperationFuture request = cancelQueue.poll();
878 if ( request == null )
879 {
880 break;
881 }
882
883
884 for ( SocketAddress socketAddress : request.getLocalAddresses() )
885 {
886 H handle = boundHandles.remove( socketAddress );
887
888 if ( handle == null )
889 {
890 continue;
891 }
892
893 try
894 {
895 close( handle );
896 wakeup();
897 }
898 catch ( Throwable e )
899 {
900 ExceptionMonitor.getInstance().exceptionCaught( e );
901 }
902 finally
903 {
904 nHandles++;
905 }
906 }
907
908 request.setDone();
909 }
910
911 return nHandles;
912 }
913
914
915 private void notifyIdleSessions( long currentTime )
916 {
917
918 if ( currentTime - lastIdleCheckTime >= 1000 )
919 {
920 lastIdleCheckTime = currentTime;
921 AbstractIoSession.notifyIdleness( getListeners().getManagedSessions().values().iterator(), currentTime );
922 }
923 }
924 }