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");
}
}
}