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