package org.limewire.http.reactor;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.IOReactorStatus;
import org.apache.http.nio.reactor.IOSession;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.ExecutionContext;
import org.limewire.io.IOUtils;
import org.limewire.nio.AbstractNBSocket;
import org.limewire.nio.channel.ThrottleWriter;
/**
* An implementation of {@link DispatchedIOReactor} interface that
* establishes connections through LimeWire's NIO layer.
* <p>
* This is analogous to {@link ListeningIOReactor}.
*/
public class DefaultDispatchedIOReactor implements DispatchedIOReactor {
public static final String IO_SESSION_KEY = "org.limewire.iosession";
private static final Log LOG = LogFactory.getLog(DefaultDispatchedIOReactor.class);
private final HttpParams params;
protected volatile IOEventDispatch eventDispatch = null;
private final Executor ioExecutor;
public DefaultDispatchedIOReactor(final HttpParams params, final Executor ioExecutor) {
if (params == null) {
throw new IllegalArgumentException();
}
this.params = params;
this.ioExecutor = ioExecutor;
}
public void execute(IOEventDispatch eventDispatch) throws IOException {
if (!(eventDispatch instanceof DefaultServerIOEventDispatch)) {
throw new IllegalArgumentException("Event dispatch must be of type DefaultServerIOEventDispatch");
}
this.eventDispatch = eventDispatch;
}
/**
* Sets parameters of <code>socket</code> based on default {@link HttpParams}.
*/
protected void prepareSocket(final Socket socket) throws IOException {
socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(this.params));
int linger = HttpConnectionParams.getLinger(this.params);
if (linger >= 0) {
socket.setSoLinger(linger > 0, linger);
}
}
/**
* Connects <code>socket</code> to LimeWire's NIO layer.
*/
protected NHttpConnection connectSocket(AbstractNBSocket socket, Object attachment, String word) {
final HttpIOSession session = new HttpIOSession(socket, ioExecutor);
session.setAttribute(IOSession.ATTACHMENT_KEY, attachment);
session.setSocketTimeout(HttpConnectionParams.getSoTimeout(this.params));
HttpChannel channel = new HttpChannel(session, eventDispatch, word);
session.setHttpChannel(channel);
ThrottleWriter throttleWriter = new ThrottleWriter(null);
session.setThrottleChannel(throttleWriter);
channel.setWriteChannel(throttleWriter);
this.eventDispatch.connected(session);
// need to enable access to the channel for throttling support
NHttpConnection conn = (NHttpConnection) session.getAttribute(ExecutionContext.HTTP_CONNECTION);
assert conn != null;
conn.getContext().setAttribute(IO_SESSION_KEY, session);
socket.setReadObserver(channel);
socket.setWriteObserver(channel);
return conn;
}
/**
* Processes an established connection.
*
* @param word the text that was send on connect, this is injected back when
* the socket's channel is read
* @param socket the socket
* @return the HttpCore connection object
*/
public NHttpConnection acceptConnection(String word, Socket socket) {
try {
prepareSocket(socket);
return connectSocket((AbstractNBSocket) socket, null, word);
} catch (IOException e) {
LOG.info("Closing socket due to unexpected exception", e);
IOUtils.close(socket);
return null;
}
}
/**
* Throws {@link UnsupportedOperationException}.
*/
public IOReactorStatus getStatus() {
throw new UnsupportedOperationException();
}
/**
* Throws {@link UnsupportedOperationException}.
*/
public void shutdown() throws IOException {
throw new UnsupportedOperationException();
}
/**
* Throws {@link UnsupportedOperationException}.
*/
public void shutdown(long gracePeriod) throws IOException {
throw new UnsupportedOperationException();
}
}