package edu.uw.zookeeper.netty; import static com.google.common.base.Preconditions.*; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.logging.log4j.Logger; import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.ListenableFuture; import edu.uw.zookeeper.common.Actors.ExecutedQueuedActor; import edu.uw.zookeeper.common.LoggingFutureListener; import edu.uw.zookeeper.common.Promise; import edu.uw.zookeeper.common.PromiseTask; import edu.uw.zookeeper.common.SettableFuturePromise; import edu.uw.zookeeper.net.Connection; public abstract class AbstractChannelConnection<I,O,C extends AbstractChannelConnection<I,O,C>> implements Connection<I,O,C> { protected final Logger logger; protected final EventfulHandler<? extends O> eventful; protected final ConnectionStateHandler state; protected final Channel channel; protected final OutboundActor outbound; protected AbstractChannelConnection( Channel channel, EventfulHandler<? extends O> eventful, ConnectionStateHandler state, Collection<? extends Connection.Listener<? super O>> listeners, Logger logger) { this.logger = checkNotNull(logger); this.eventful = checkNotNull(eventful); this.channel = checkNotNull(channel); this.state = checkNotNull(state); this.outbound = new OutboundActor(); // subscribe any listeners before creating pipeline state.subscribe(eventful); for (Connection.Listener<? super O> listener: listeners) { eventful.subscribe(listener); } // finally, create pipeline ConnectionStateHandler.toPipeline( state, EventfulHandler.toPipeline( eventful, channel.pipeline())); } @Override public void execute(Runnable runnable) { if (channel.isRegistered()) { channel.eventLoop().execute(runnable); } else { executeNow(runnable); } } protected synchronized void executeNow(Runnable runnable) { runnable.run(); } public Channel channel() { return channel; } @Override public State state() { return state.state(); } @Override public SocketAddress localAddress() { return channel.localAddress(); } @Override public SocketAddress remoteAddress() { return channel.remoteAddress(); } @Override public C read() { channel.read(); return self(); } @Override public <T extends I> ListenableFuture<T> write(T message) { PromiseTask<T,T> task = PromiseTask.of( message, SettableFuturePromise.<T>create()); LoggingFutureListener.listen(logger, task); if (! outbound.send(task)) { task.setException(new ClosedChannelException()); } return task; } @Override public C flush() { channel.flush(); return self(); } @Override public ListenableFuture<? extends C> close() { return ChannelFutureWrapper.of(channel.close(), self()); } @Override public void subscribe(Listener<? super O> listener) { eventful.subscribe(listener); } @Override public boolean unsubscribe(Listener<? super O> listener) { return eventful.unsubscribe(listener); } @Override public String toString() { return MoreObjects.toStringHelper(this) .addValue(channel).toString(); } @SuppressWarnings("unchecked") protected C self() { return (C) this; } protected final class OutboundActor extends ExecutedQueuedActor<PromiseTask<? extends I, ? extends I>> implements ChannelFutureListener { public OutboundActor() { super(AbstractChannelConnection.this, new ConcurrentLinkedQueue<PromiseTask<? extends I, ? extends I>>(), AbstractChannelConnection.this.logger); channel.closeFuture().addListener(this); } @Override public void operationComplete(ChannelFuture future) { stop(); } @Override public String toString() { return AbstractChannelConnection.this.toString(); } @SuppressWarnings("unchecked") @Override protected boolean apply(PromiseTask<? extends I, ? extends I> input) { if (! input.isDone()) { Connection.State state = AbstractChannelConnection.this.state(); switch (state) { case CONNECTION_CLOSING: case CONNECTION_CLOSED: { input.setException(new ClosedChannelException()); break; } default: { I task = input.task(); ChannelFutureWrapper.of( channel.write(task), task, (Promise<I>) input); break; } } } return true; } @Override protected void runExit() { channel.flush(); super.runExit(); } @Override protected void doStop() { Exception e = new ClosedChannelException(); PromiseTask<? extends I, ? extends I> next; while ((next = mailbox.poll()) != null) { if (! next.isDone()) { next.setException(e); } } } } }