View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.core.polling;
21  
22  import java.net.SocketAddress;
23  import java.nio.channels.ClosedSelectorException;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Queue;
31  import java.util.Set;
32  import java.util.concurrent.ConcurrentHashMap;
33  import java.util.concurrent.ConcurrentLinkedQueue;
34  import java.util.concurrent.Executor;
35  import java.util.concurrent.Executors;
36  import java.util.concurrent.Semaphore;
37  import java.util.concurrent.atomic.AtomicReference;
38  
39  import org.apache.mina.core.RuntimeIoException;
40  import org.apache.mina.core.filterchain.IoFilter;
41  import org.apache.mina.core.service.AbstractIoAcceptor;
42  import org.apache.mina.core.service.IoAcceptor;
43  import org.apache.mina.core.service.IoHandler;
44  import org.apache.mina.core.service.IoProcessor;
45  import org.apache.mina.core.service.SimpleIoProcessorPool;
46  import org.apache.mina.core.session.AbstractIoSession;
47  import org.apache.mina.core.session.IoSession;
48  import org.apache.mina.core.session.IoSessionConfig;
49  import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
50  import org.apache.mina.util.ExceptionMonitor;
51  
52  /**
53   * A base class for implementing transport using a polling strategy. The
54   * underlying sockets will be checked in an active loop and woke up when an
55   * socket needed to be processed. This class handle the logic behind binding,
56   * accepting and disposing the server sockets. An {@link Executor} will be used
57   * for running client accepting and an {@link AbstractPollingIoProcessor} will
58   * be used for processing client I/O operations like reading, writing and
59   * closing.
60   * 
61   * All the low level methods for binding, accepting, closing need to be provided
62   * by the subclassing implementation.
63   * 
64   * @see NioSocketAcceptor for a example of implementation
65   * 
66   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
67   */
68  public abstract class AbstractPollingIoAcceptor<S extends AbstractIoSession, H>
69          extends AbstractIoAcceptor {
70      /** A lock used to protect the selector to be waked up before it's created */
71      private final Semaphore lock = new Semaphore(1);
72  
73      private final IoProcessor<S> processor;
74  
75      private final boolean createdProcessor;
76  
77      private final Queue<AcceptorOperationFuture> registerQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
78  
79      private final Queue<AcceptorOperationFuture> cancelQueue = new ConcurrentLinkedQueue<AcceptorOperationFuture>();
80  
81      private final Map<SocketAddress, H> boundHandles = Collections
82              .synchronizedMap(new HashMap<SocketAddress, H>());
83  
84      private final ServiceOperationFuture disposalFuture = new ServiceOperationFuture();
85  
86      /** A flag set when the acceptor has been created and initialized */
87      private volatile boolean selectable;
88  
89      /** The thread responsible of accepting incoming requests */
90      private AtomicReference<Acceptor> acceptorRef = new AtomicReference<Acceptor>();
91  
92      protected boolean reuseAddress = false;
93  
94      /**
95       * Define the number of socket that can wait to be accepted. Default
96       * to 50 (as in the SocketServer default).
97       */
98      protected int backlog = 50;
99  
100     /**
101      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
102      * session configuration, a class of {@link IoProcessor} which will be instantiated in a
103      * {@link SimpleIoProcessorPool} for better scaling in multiprocessor systems. The default
104      * pool size will be used.
105      * 
106      * @see SimpleIoProcessorPool
107      * 
108      * @param sessionConfig
109      *            the default configuration for the managed {@link IoSession}
110      * @param processorClass a {@link Class} of {@link IoProcessor} for the associated {@link IoSession}
111      *            type.
112      */
113     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
114             Class<? extends IoProcessor<S>> processorClass) {
115         this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass),
116                 true);
117     }
118 
119     /**
120      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
121      * session configuration, a class of {@link IoProcessor} which will be instantiated in a
122      * {@link SimpleIoProcessorPool} for using multiple thread for better scaling in multiprocessor
123      * systems.
124      * 
125      * @see SimpleIoProcessorPool
126      * 
127      * @param sessionConfig
128      *            the default configuration for the managed {@link IoSession}
129      * @param processorClass a {@link Class} of {@link IoProcessor} for the associated {@link IoSession}
130      *            type.
131      * @param processorCount the amount of processor to instantiate for the pool
132      */
133     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
134             Class<? extends IoProcessor<S>> processorClass, int processorCount) {
135         this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass,
136                 processorCount), true);
137     }
138 
139     /**
140      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
141      * session configuration, a default {@link Executor} will be created using
142      * {@link Executors#newCachedThreadPool()}.
143      * 
144      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
145      * 
146      * @param sessionConfig
147      *            the default configuration for the managed {@link IoSession}
148      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of this transport, triggering
149      *            events to the bound {@link IoHandler} and processing the chains of {@link IoFilter}
150      */
151     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
152             IoProcessor<S> processor) {
153         this(sessionConfig, null, processor, false);
154     }
155 
156     /**
157      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
158      * session configuration and an {@link Executor} for handling I/O events. If a
159      * null {@link Executor} is provided, a default one will be created using
160      * {@link Executors#newCachedThreadPool()}.
161      * 
162      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
163      * 
164      * @param sessionConfig
165      *            the default configuration for the managed {@link IoSession}
166      * @param executor
167      *            the {@link Executor} used for handling asynchronous execution of I/O
168      *            events. Can be <code>null</code>.
169      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of this transport, triggering
170      *            events to the bound {@link IoHandler} and processing the chains of {@link IoFilter}
171      */
172     protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
173             Executor executor, IoProcessor<S> processor) {
174         this(sessionConfig, executor, processor, false);
175     }
176 
177     /**
178      * Constructor for {@link AbstractPollingIoAcceptor}. You need to provide a default
179      * session configuration and an {@link Executor} for handling I/O events. If a
180      * null {@link Executor} is provided, a default one will be created using
181      * {@link Executors#newCachedThreadPool()}.
182      * 
183      * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
184      * 
185      * @param sessionConfig
186      *            the default configuration for the managed {@link IoSession}
187      * @param executor
188      *            the {@link Executor} used for handling asynchronous execution of I/O
189      *            events. Can be <code>null</code>.
190      * @param processor the {@link IoProcessor} for processing the {@link IoSession} of
191      * this transport, triggering events to the bound {@link IoHandler} and processing
192      * the chains of {@link IoFilter}
193      * @param createdProcessor tagging the processor as automatically created, so it
194      * will be automatically disposed
195      */
196     private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
197             Executor executor, IoProcessor<S> processor,
198             boolean createdProcessor) {
199         super(sessionConfig, executor);
200 
201         if (processor == null) {
202             throw new IllegalArgumentException("processor");
203         }
204 
205         this.processor = processor;
206         this.createdProcessor = createdProcessor;
207 
208         try {
209             // Initialize the selector
210             init();
211             
212             // The selector is now ready, we can switch the
213             // flag to true so that incoming connection can be accepted
214             selectable = true;
215         } catch (RuntimeException e) {
216             throw e;
217         } catch (Exception e) {
218             throw new RuntimeIoException("Failed to initialize.", e);
219         } finally {
220             if (!selectable) {
221                 try {
222                     destroy();
223                 } catch (Exception e) {
224                     ExceptionMonitor.getInstance().exceptionCaught(e);
225                 }
226             }
227         }
228     }
229 
230     /**
231      * Initialize the polling system, will be called at construction time.
232      * @throws Exception any exception thrown by the underlying system calls
233      */
234     protected abstract void init() throws Exception;
235 
236     /**
237      * Destroy the polling system, will be called when this {@link IoAcceptor}
238      * implementation will be disposed.
239      * @throws Exception any exception thrown by the underlying systems calls
240      */
241     protected abstract void destroy() throws Exception;
242 
243     /**
244      * Check for acceptable connections, interrupt when at least a server is ready for accepting.
245      * All the ready server socket descriptors need to be returned by {@link #selectedHandles()}
246      * @return The number of sockets having got incoming client
247      * @throws Exception any exception thrown by the underlying systems calls
248      */
249     protected abstract int select() throws Exception;
250 
251     /**
252      * Interrupt the {@link #select()} method. Used when the poll set need to be modified.
253      */
254     protected abstract void wakeup();
255 
256     /**
257      * {@link Iterator} for the set of server sockets found with acceptable incoming connections
258      *  during the last {@link #select()} call.
259      * @return the list of server handles ready
260      */
261     protected abstract Iterator<H> selectedHandles();
262 
263     /**
264      * Open a server socket for a given local address.
265      * @param localAddress the associated local address
266      * @return the opened server socket
267      * @throws Exception any exception thrown by the underlying systems calls
268      */
269     protected abstract H open(SocketAddress localAddress) throws Exception;
270 
271     /**
272      * Get the local address associated with a given server socket
273      * @param handle the server socket
274      * @return the local {@link SocketAddress} associated with this handle
275      * @throws Exception any exception thrown by the underlying systems calls
276      */
277     protected abstract SocketAddress localAddress(H handle) throws Exception;
278 
279     /**
280      * Accept a client connection for a server socket and return a new {@link IoSession}
281      * associated with the given {@link IoProcessor}
282      * @param processor the {@link IoProcessor} to associate with the {@link IoSession}
283      * @param handle the server handle
284      * @return the created {@link IoSession}
285      * @throws Exception any exception thrown by the underlying systems calls
286      */
287     protected abstract S accept(IoProcessor<S> processor, H handle)
288             throws Exception;
289 
290     /**
291      * Close a server socket.
292      * @param handle the server socket
293      * @throws Exception any exception thrown by the underlying systems calls
294      */
295     protected abstract void close(H handle) throws Exception;
296 
297     /**
298      * {@inheritDoc}
299      */
300     @Override
301     protected void dispose0() throws Exception {
302         unbind();
303 
304         startupAcceptor();
305         wakeup();
306     }
307 
308     /**
309      * {@inheritDoc}
310      */
311     @Override
312     protected final Set<SocketAddress> bindInternal(
313             List<? extends SocketAddress> localAddresses) throws Exception {
314         // Create a bind request as a Future operation. When the selector
315         // have handled the registration, it will signal this future.
316         AcceptorOperationFuture request = new AcceptorOperationFuture(
317                 localAddresses);
318 
319         // adds the Registration request to the queue for the Workers
320         // to handle
321         registerQueue.add(request);
322 
323         // creates the Acceptor instance and has the local
324         // executor kick it off.
325         startupAcceptor();
326         
327         // As we just started the acceptor, we have to unblock the select()
328         // in order to process the bind request we just have added to the
329         // registerQueue.
330         try {
331             lock.acquire();
332             
333             // Wait a bit to give a chance to the Acceptor thread to do the select()
334             Thread.sleep( 10 );
335             wakeup();
336         }
337         finally
338         {
339             lock.release();
340         }
341         
342         // Now, we wait until this request is completed.
343         request.awaitUninterruptibly();
344 
345         if (request.getException() != null) {
346             throw request.getException();
347         }
348 
349         // Update the local addresses.
350         // setLocalAddresses() shouldn't be called from the worker thread
351         // because of deadlock.
352         Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
353         
354         for (H handle:boundHandles.values()) {
355             newLocalAddresses.add(localAddress(handle));
356         }
357 
358         return newLocalAddresses;
359     }
360 
361     /**
362      * This method is called by the doBind() and doUnbind()
363      * methods.  If the acceptor is null, the acceptor object will
364      * be created and kicked off by the executor.  If the acceptor
365      * object is null, probably already created and this class
366      * is now working, then nothing will happen and the method
367      * will just return.
368      */
369     private void startupAcceptor() throws InterruptedException {
370         // If the acceptor is not ready, clear the queues
371         // TODO : they should already be clean : do we have to do that ?
372         if (!selectable) {
373             registerQueue.clear();
374             cancelQueue.clear();
375         }
376 
377         // start the acceptor if not already started
378         Acceptor acceptor = acceptorRef.get();
379 
380         if (acceptor == null) {
381             lock.acquire();
382             acceptor = new Acceptor();
383 
384             if (acceptorRef.compareAndSet(null, acceptor)) {
385                 executeWorker(acceptor);
386             } else {
387                 lock.release();
388             }
389         }
390     }
391 
392     /**
393      * {@inheritDoc}
394      */
395     @Override
396     protected final void unbind0(List<? extends SocketAddress> localAddresses)
397             throws Exception {
398         AcceptorOperationFuture future = new AcceptorOperationFuture(
399                 localAddresses);
400 
401         cancelQueue.add(future);
402         startupAcceptor();
403         wakeup();
404 
405         future.awaitUninterruptibly();
406         if (future.getException() != null) {
407             throw future.getException();
408         }
409     }
410 
411     /**
412      * This class is called by the startupAcceptor() method and is
413      * placed into a NamePreservingRunnable class.
414      * It's a thread accepting incoming connections from clients.
415      * The loop is stopped when all the bound handlers are unbound.
416      */
417     private class Acceptor implements Runnable {
418         public void run() {
419             assert (acceptorRef.get() == this);
420 
421             int nHandles = 0;
422 
423             // Release the lock
424             lock.release();
425 
426             while (selectable) {
427                 try {
428                     // Detect if we have some keys ready to be processed
429                     // The select() will be woke up if some new connection
430                     // have occurred, or if the selector has been explicitly
431                     // woke up
432                     int selected = select();
433 
434                     // this actually sets the selector to OP_ACCEPT,
435                     // and binds to the port on which this class will
436                     // listen on
437                     nHandles += registerHandles();
438 
439                     // Now, if the number of registred handles is 0, we can
440                     // quit the loop: we don't have any socket listening
441                     // for incoming connection.
442                     if (nHandles == 0) {
443                         acceptorRef.set(null);
444 
445                         if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
446                             assert (acceptorRef.get() != this);
447                             break;
448                         }
449                         
450                         if (!acceptorRef.compareAndSet(null, this)) {
451                             assert (acceptorRef.get() != this);
452                             break;
453                         }
454                         
455                         assert (acceptorRef.get() == this);
456                     }
457 
458                     if (selected > 0) {
459                         // We have some connection request, let's process
460                         // them here.
461                         processHandles(selectedHandles());
462                     }
463 
464                     // check to see if any cancellation request has been made.
465                     nHandles -= unregisterHandles();
466                 } catch (ClosedSelectorException cse) {
467                     // If the selector has been closed, we can exit the loop
468                     break;
469                 } catch (Throwable e) {
470                     ExceptionMonitor.getInstance().exceptionCaught(e);
471 
472                     try {
473                         Thread.sleep(1000);
474                     } catch (InterruptedException e1) {
475                         ExceptionMonitor.getInstance().exceptionCaught(e1);
476                     }
477                 }
478             }
479 
480             // Cleanup all the processors, and shutdown the acceptor.
481             if (selectable && isDisposing()) {
482                 selectable = false;
483                 try {
484                     if (createdProcessor) {
485                         processor.dispose();
486                     }
487                 } finally {
488                     try {
489                         synchronized (disposalLock) {
490                             if (isDisposing()) {
491                                 destroy();
492                             }
493                         }
494                     } catch (Exception e) {
495                         ExceptionMonitor.getInstance().exceptionCaught(e);
496                     } finally {
497                         disposalFuture.setDone();
498                     }
499                 }
500             }
501         }
502 
503         /**
504          * This method will process new sessions for the Worker class.  All
505          * keys that have had their status updates as per the Selector.selectedKeys()
506          * method will be processed here.  Only keys that are ready to accept
507          * connections are handled here.
508          * <p/>
509          * Session objects are created by making new instances of SocketSessionImpl
510          * and passing the session object to the SocketIoProcessor class.
511          */
512         @SuppressWarnings("unchecked")
513         private void processHandles(Iterator<H> handles) throws Exception {
514             while (handles.hasNext()) {
515                 H handle = handles.next();
516                 handles.remove();
517 
518                 // Associates a new created connection to a processor,
519                 // and get back a session
520                 S session = accept(processor, handle);
521                 
522                 if (session == null) {
523                     continue;
524                 }
525 
526                 initSession(session, null, null);
527 
528                 // add the session to the SocketIoProcessor
529                 session.getProcessor().add(session);
530             }
531         }
532     }
533 
534     /**
535      * Sets up the socket communications.  Sets items such as:
536      * <p/>
537      * Blocking
538      * Reuse address
539      * Receive buffer size
540      * Bind to listen port
541      * Registers OP_ACCEPT for selector
542      */
543     private int registerHandles() {
544         for (;;) {
545             // The register queue contains the list of services to manage
546             // in this acceptor.
547             AcceptorOperationFuture future = registerQueue.poll();
548             
549             if (future == null) {
550                 return 0;
551             }
552 
553             // We create a temporary map to store the bound handles,
554             // as we may have to remove them all if there is an exception
555             // during the sockets opening.
556             Map<SocketAddress, H> newHandles = new ConcurrentHashMap<SocketAddress, H>();
557             List<SocketAddress> localAddresses = future.getLocalAddresses();
558 
559             try {
560                 // Process all the addresses
561                 for (SocketAddress a : localAddresses) {
562                     H handle = open(a);
563                     newHandles.put(localAddress(handle), handle);
564                 }
565 
566                 // Everything went ok, we can now update the map storing
567                 // all the bound sockets.
568                 boundHandles.putAll(newHandles);
569 
570                 // and notify.
571                 future.setDone();
572                 return newHandles.size();
573             } catch (Exception e) {
574                 // We store the exception in the future
575                 future.setException(e);
576             } finally {
577                 // Roll back if failed to bind all addresses.
578                 if (future.getException() != null) {
579                     for (H handle : newHandles.values()) {
580                         try {
581                             close(handle);
582                         } catch (Exception e) {
583                             ExceptionMonitor.getInstance().exceptionCaught(e);
584                         }
585                     }
586                     
587                     // TODO : add some comment : what is the wakeup() waking up ?
588                     wakeup();
589                 }
590             }
591         }
592     }
593 
594     /**
595      * This method just checks to see if anything has been placed into the
596      * cancellation queue.  The only thing that should be in the cancelQueue
597      * is CancellationRequest objects and the only place this happens is in
598      * the doUnbind() method.
599      */
600     private int unregisterHandles() {
601         int cancelledHandles = 0;
602         for (;;) {
603             AcceptorOperationFuture future = cancelQueue.poll();
604             if (future == null) {
605                 break;
606             }
607 
608             // close the channels
609             for (SocketAddress a : future.getLocalAddresses()) {
610                 H handle = boundHandles.remove(a);
611                 
612                 if (handle == null) {
613                     continue;
614                 }
615 
616                 try {
617                     close(handle);
618                     wakeup(); // wake up again to trigger thread death
619                 } catch (Throwable e) {
620                     ExceptionMonitor.getInstance().exceptionCaught(e);
621                 } finally {
622                     cancelledHandles++;
623                 }
624             }
625 
626             future.setDone();
627         }
628 
629         return cancelledHandles;
630     }
631 
632     /**
633      * {@inheritDoc}
634      */
635     public final IoSession newSession(SocketAddress remoteAddress,
636             SocketAddress localAddress) {
637         throw new UnsupportedOperationException();
638     }
639 
640     /**
641      * {@inheritDoc}
642      */
643     public int getBacklog() {
644         return backlog;
645     }
646 
647     /**
648      * {@inheritDoc}
649      */
650     public void setBacklog(int backlog) {
651         synchronized (bindLock) {
652             if (isActive()) {
653                 throw new IllegalStateException(
654                         "backlog can't be set while the acceptor is bound.");
655             }
656 
657             this.backlog = backlog;
658         }
659     }
660 
661     /**
662      * {@inheritDoc}
663      */
664     public boolean isReuseAddress() {
665         return reuseAddress;
666     }
667 
668     /**
669      * {@inheritDoc}
670      */
671     public void setReuseAddress(boolean reuseAddress) {
672         synchronized (bindLock) {
673             if (isActive()) {
674                 throw new IllegalStateException(
675                         "backlog can't be set while the acceptor is bound.");
676             }
677 
678             this.reuseAddress = reuseAddress;
679         }
680     }
681 }