package org.jboss.pitbull.internal.nio.socket; import org.jboss.pitbull.internal.logging.Logger; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class Acceptor implements Runnable { protected Selector selector; protected ServerSocketChannel channel; protected CountDownLatch shutdownLatch = new CountDownLatch(1); protected volatile boolean shutdown; protected int workerIndex = 0; protected ManagedChannelFactory channelFactory; protected Worker[] workers; protected static final Logger logger = Logger.getLogger(Acceptor.class); protected static final AtomicInteger counter = new AtomicInteger(); protected long acceptCount; public Acceptor(ServerSocketChannel channel, ManagedChannelFactory channelFactory, Worker[] workers) throws IOException { this.channelFactory = channelFactory; this.workers = workers; this.channel = channel; selector = Selector.open(); channel.register(selector, SelectionKey.OP_ACCEPT); } protected Worker nextWorker() { return workers[Math.abs( workerIndex++ % workers.length)]; } protected void registerAcceptedChannel(SocketChannel accepted) throws IOException { logger.trace("Accepted Connection."); Worker worker = nextWorker(); try { worker.queueRegistration(channelFactory.create(accepted)); } catch (Exception e) { logger.error("Error registering accepted socket", e); try { accepted.close(); } catch (Exception ignored) { } } } public void clearMetrics() { acceptCount = 0; } public long getAcceptCount() { return acceptCount; } public void shutdown() { if (shutdown) return; try { synchronized (this) // to flush shutdown { shutdown = true; } selector.wakeup(); shutdownLatch.await(); } catch (Exception e) { logger.error("Failed to shutdown Acceptor thread", e); } } @Override public void run() { String oldName = Thread.currentThread().getName(); Thread.currentThread().setName("Pitbull Acceptor Thread: " + counter.incrementAndGet()); try { for (; ; ) { if (shutdown) break; try { if (selector.select(1000) > 0) { selector.selectedKeys().clear(); } if (shutdown) break; SocketChannel acceptedSocket = channel.accept(); if (acceptedSocket != null) { acceptCount++; registerAcceptedChannel(acceptedSocket); } } catch (SocketTimeoutException e) { // Thrown every second to get ClosedChannelException // raised. } catch (CancelledKeyException e) { // Raised by accept() when the server socket was closed. } catch (ClosedSelectorException e) { // Raised by accept() when the server socket was closed. } catch (ClosedChannelException e) { // Closed as requested. break; } catch (Throwable e) { logger.warn( "Failed to accept a connection.", e); try { Thread.sleep(1000); } catch (InterruptedException e1) { // Ignore } } } } finally { closeSelector(); shutdownLatch.countDown(); Thread.currentThread().setName(oldName); } } protected void closeSelector() { try { selector.close(); } catch (Exception e) { logger.warn("Failed to close a selector.", e); } } }