package org.limewire.http.reactor;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.ByteChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.nio.reactor.EventMask;
import org.apache.http.nio.reactor.IOSession;
import org.apache.http.nio.reactor.SessionBufferStatus;
import org.limewire.nio.AbstractNBSocket;
import org.limewire.nio.NIODispatcher;
import org.limewire.nio.Throttle;
import org.limewire.nio.channel.ThrottleWriter;
/**
* Provides an implementation of the <code>IOSession</code> interface that
* connects to LimeWire's NIO layer and has support for throttling.
*/
public class HttpIOSession implements IOSession {
private static final Log LOG = LogFactory.getLog(HttpIOSession.class);
private final Map<String, Object> attributes;
private SessionBufferStatus bufferStatus;
private int socketTimeout;
private AbstractNBSocket socket;
private HttpChannel channel;
private int eventMask;
private ThrottleWriter throttleWriter;
private AtomicBoolean closed = new AtomicBoolean(false);
private final Executor ioExecutor;
public HttpIOSession(AbstractNBSocket socket, Executor ioExecutor) {
if (socket == null) {
throw new IllegalArgumentException();
}
this.attributes = Collections
.synchronizedMap(new HashMap<String, Object>());
this.socketTimeout = 0;
this.socket = socket;
this.ioExecutor = ioExecutor;
}
public void setHttpChannel(HttpChannel channel) {
this.channel = channel;
}
public ByteChannel channel() {
return channel;
}
public void close() {
if (this.closed.getAndSet(true)) {
return;
}
channel.closeWhenBufferedOutputHasBeenFlushed();
}
public Object getAttribute(String name) {
return attributes.get(name);
}
public SessionBufferStatus getBufferStatus() {
return bufferStatus;
}
public SocketAddress getLocalAddress() {
return socket.getLocalSocketAddress();
}
public SocketAddress getRemoteAddress() {
return socket.getRemoteSocketAddress();
}
public int getSocketTimeout() {
return socketTimeout;
}
public boolean isClosed() {
return closed.get();
}
public Object removeAttribute(String name) {
return attributes.remove(name);
}
public void setAttribute(String name, Object obj) {
attributes.put(name, obj);
}
public void setBufferStatus(SessionBufferStatus status) {
this.bufferStatus = status;
}
public synchronized int getEventMask() {
return eventMask;
}
public synchronized void setEventMask(int ops) {
if (isClosed()) {
if (LOG.isErrorEnabled())
LOG.error("Attempted to set event mask to " + ops + " on closed session: " + this);
return;
}
this.eventMask = ops;
channel.requestRead((ops & EventMask.READ) != 0);
channel.requestWrite((ops & EventMask.WRITE) != 0);
// if ((ops & EventMask.READ) != 0) {
// System.err.println("read on");
// } else {
// System.err.println("read off");
// }
// if ((ops & EventMask.WRITE) != 0) {
// System.err.println("write on");
// } else {
// System.err.println("write off");
// }
}
public synchronized void setEvent(int op) {
if (isClosed()) {
if (LOG.isErrorEnabled())
LOG.error("Attempted to set event mask to " + op + " on closed session: " + this);
return;
}
this.eventMask |= op;
if ((op & EventMask.READ) != 0) {
channel.requestRead(true);
}
if ((op & EventMask.WRITE) != 0) {
channel.requestWrite(true);
}
// if ((op & EventMask.READ) != 0) {
// System.err.println("read on");
// }
// if ((op & EventMask.WRITE) != 0) {
// System.err.println("write on");
// }
}
public synchronized void clearEvent(int op) {
if (isClosed()) {
if (LOG.isErrorEnabled())
LOG.error("Attempted to set event mask to " + op + " on closed session: " + this);
return;
}
this.eventMask &= ~op;
if ((op & EventMask.READ) != 0) {
channel.requestRead(false);
}
if ((op & EventMask.WRITE) != 0) {
channel.requestWrite(false);
}
// if ((op & EventMask.READ) != 0) {
// System.err.println("read off");
// }
// if ((op & EventMask.WRITE) != 0) {
// System.err.println("write off");
// }
}
public void setSocketTimeout(int timeout) {
this.socketTimeout = timeout;
try {
socket.setSoTimeout(timeout);
} catch (SocketException e) {
LOG.warn("Could not set socket timeout", e);
}
}
public boolean hasBufferedInput() {
return this.bufferStatus != null
&& this.bufferStatus.hasBufferedInput();
}
public boolean hasBufferedOutput() {
return this.bufferStatus != null
&& this.bufferStatus.hasBufferedOutput();
}
/**
* Throttles the underlying connection using <code>throttle</code>. If
* <code>throttle</code> is null, throttling is disabled.
*/
public void setThrottle(final Throttle throttle) {
assert NIODispatcher.instance().isDispatchThread() :
"wrong thread: "+Thread.currentThread().getName();
this.throttleWriter.setThrottle(throttle);
}
public Socket getSocket() {
return socket;
}
public void shutdown() {
closed.set(true);
socket.close();
}
public void setThrottleChannel(final ThrottleWriter throttleWriter) {
this.throttleWriter = throttleWriter;
}
public int getStatus() {
throw new UnsupportedOperationException();
}
public Executor getIoExecutor() {
return ioExecutor;
}
}