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 }