package edu.uw.zookeeper.net.intravm;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.Callable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import edu.uw.zookeeper.common.Automaton;
import edu.uw.zookeeper.common.LoggingFutureListener;
import edu.uw.zookeeper.common.CallablePromiseTask;
import edu.uw.zookeeper.common.SettableFuturePromise;
import edu.uw.zookeeper.net.Connection;
public abstract class AbstractIntraVmConnection<I,O,U,V, T extends AbstractIntraVmEndpoint<I,O,U,V>, C extends AbstractIntraVmConnection<I,O,U,V,T,C>> implements Connection<I,O,C> {
protected final Logger logger;
protected final T local;
protected final AbstractIntraVmEndpoint<?,?,?,? super U> remote;
protected final CallablePromiseTask<Close,C> close;
protected AbstractIntraVmConnection(
T local,
AbstractIntraVmEndpoint<?,?,?,? super U> remote) {
this.logger = LogManager.getLogger(getClass());
this.local = local;
this.remote = remote;
this.close = CallablePromiseTask.create(
new Close(),
SettableFuturePromise.<C>create());
LoggingFutureListener.listen(logger, close);
}
protected T local() {
return local;
}
protected AbstractIntraVmEndpoint<?,?,?,? super U> remote() {
return remote;
}
@Override
public Connection.State state() {
return local.state();
}
@Override
public SocketAddress localAddress() {
return local.address();
}
@Override
public SocketAddress remoteAddress() {
return remote.address();
}
@Override
public void subscribe(Connection.Listener<? super O> listener) {
local.subscribe(listener);
}
@Override
public boolean unsubscribe(Connection.Listener<? super O> listener) {
return local.unsubscribe(listener);
}
@Override
public C read() {
local.reader().run();
return self();
}
@Override
public <I1 extends I> ListenableFuture<I1> write(I1 message) {
return local.writer().submit(message, remote);
}
@Override
public C flush() {
local.writer().run();
return self();
}
@Override
public ListenableFuture<? extends C> close() {
if (!close.isDone()) {
execute(close);
}
return close;
}
@Override
public void execute(Runnable command) {
local.execute(command);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.addValue(String.format("%s => %s", localAddress(), remoteAddress()))
.toString();
}
@SuppressWarnings("unchecked")
protected C self() {
return (C) this;
}
protected class Close implements Callable<Optional<C>>, Connection.Listener<Object> {
public Close() {
}
@Override
public Optional<C> call() throws IOException {
logger.entry(this);
Optional<C> result = Optional.absent();
switch (local.state()) {
case CONNECTION_CLOSED:
break;
case CONNECTION_CLOSING:
local.subscribe(this);
break;
case CONNECTION_OPENED:
case CONNECTION_OPENING:
local.subscribe(this);
local.close();
break;
default:
break;
}
if (local.state() == Connection.State.CONNECTION_CLOSED) {
local.unsubscribe(this);
result = Optional.of(self());
}
switch (remote.state()) {
case CONNECTION_CLOSED:
break;
case CONNECTION_CLOSING:
case CONNECTION_OPENED:
case CONNECTION_OPENING:
remote.close();
break;
default:
break;
}
return logger.exit(result);
}
@Override
public void handleConnectionState(
Automaton.Transition<Connection.State> state) {
if (state.to() == Connection.State.CONNECTION_CLOSED) {
close();
}
}
@Override
public void handleConnectionRead(Object message) {
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).addValue(state()).toString();
}
}
}