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