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