package org.limewire.nio; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.ServerSocketChannel; import org.limewire.concurrent.ThreadExecutor; import org.limewire.nio.observer.AcceptObserver; /** * A <code>ServerSocket</code> that allows asynchronous accepts but is backed * by a legacy I/O <code>ServerSocket</code>. * <p> * This is intended primarily for ease of use debugging via swapping out * an {@link NIOServerSocket} with a <code>BlockingServerSocketAdapter</code>, so * NIO code can be compared to regular I/O code. */ public class BlockingServerSocketAdapter extends ServerSocket { private final AcceptObserver observer; private final ServerSocket delegate; /** * Constructs a new, unbound, BlockingServerSocketAdapter. * You must call 'bind' to start listening for incoming connections. */ public BlockingServerSocketAdapter() throws IOException { this(null); } /** * Constructs a new, unbound, BlockingServerSocketAdapter. * You must call 'bind' to start listening for incoming connections. * All accepted connections will be routed to the given AcceptObserver. */ public BlockingServerSocketAdapter(AcceptObserver observer) throws IOException { this.delegate = new ServerSocket(); this.observer = observer; } /** Constructs a new BlockingServerSocketAdapter bound to the given port. */ public BlockingServerSocketAdapter(int port) throws IOException { this(port, null); } /** * Constructs a new BlockingServerSocketAdapter bound to the given port * All accepted connections will be routed to the given AcceptObserver. */ public BlockingServerSocketAdapter(int port, AcceptObserver observer) throws IOException { this(observer); bind(new InetSocketAddress(port)); } /** * Constructs a new BlockingServerSocketAdapter bound to the given port, able to accept * the given backlog of connections. */ public BlockingServerSocketAdapter(int port, int backlog) throws IOException { this(port, backlog, (AcceptObserver)null); } /** * Constructs a new BlockingServerSocketAdapter bound to the given port, able to accept * the given backlog of connections. * All accepted connections will be routed to the given AcceptObserver. */ public BlockingServerSocketAdapter(int port, int backlog, AcceptObserver observer) throws IOException { this(observer); bind(new InetSocketAddress(port), backlog); } /** * Constructs a new BlockingServerSocketAdapter bound to the given port & addr, able to accept * the given backlog of connections. */ public BlockingServerSocketAdapter(int port, int backlog, InetAddress bindAddr) throws IOException { this(port, backlog, bindAddr, null); } /** * Constructs a new BlockingServerSocketAdapter bound to the given port & addr, able to accept * the given backlog of connections. * All accepted connections will be routed to the given AcceptObserver. */ public BlockingServerSocketAdapter(int port, int backlog, InetAddress bindAddr, AcceptObserver observer) throws IOException { this(observer); bind(new InetSocketAddress(bindAddr, port), backlog); } @Override public void bind(SocketAddress endpoint, int backlog) throws IOException { delegate.bind(endpoint, backlog); startListening(); } @Override public void bind(SocketAddress endpoint) throws IOException { delegate.bind(endpoint); startListening(); } /** * Blocks until a socket is accepted. * If this was constructed with an AcceptObserver, this will * throw an IllegalBlockingModeException, as the AcceptObserver * will be asynchronously routed all the connections. */ @Override public Socket accept() throws IOException { if(observer != null) throw new IllegalBlockingModeException(); return delegate.accept(); } @Override public void close() throws IOException { delegate.close(); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } @Override public ServerSocketChannel getChannel() { return delegate.getChannel(); } @Override public InetAddress getInetAddress() { return delegate.getInetAddress(); } @Override public int getLocalPort() { return delegate.getLocalPort(); } @Override public SocketAddress getLocalSocketAddress() { return delegate.getLocalSocketAddress(); } @Override public int getReceiveBufferSize() throws SocketException { return delegate.getReceiveBufferSize(); } @Override public boolean getReuseAddress() throws SocketException { return delegate.getReuseAddress(); } @Override public int getSoTimeout() throws IOException { return delegate.getSoTimeout(); } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean isBound() { return delegate.isBound(); } @Override public boolean isClosed() { return delegate.isClosed(); } @Override public void setReceiveBufferSize(int size) throws SocketException { delegate.setReceiveBufferSize(size); } @Override public void setReuseAddress(boolean on) throws SocketException { delegate.setReuseAddress(on); } @Override public void setSoTimeout(int timeout) throws SocketException { delegate.setSoTimeout(timeout); } @Override public String toString() { return delegate.toString(); } /** * Starts a thread that will route accepted connections * to the AcceptObserver. */ private void startListening() { if(observer != null) { ThreadExecutor.startThread(new Runnable() { public void run() { while(!isClosed()) { try { Socket s = delegate.accept(); observer.handleAccept(s); } catch(IOException ignored) {} } observer.shutdown(); } }, "BlockingServerSocketEmulator"); } } }