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