package edu.uw.zookeeper.protocol; import java.lang.ref.WeakReference; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import org.apache.logging.log4j.Logger; import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import edu.uw.zookeeper.common.Automaton; import edu.uw.zookeeper.common.Pair; import edu.uw.zookeeper.common.Promise; import edu.uw.zookeeper.net.Connection; public abstract class PingTask<I,O,T extends Connection<? super I, ? extends O,?>> extends TimeOutActor<I,Void> implements Connection.Listener<O>{ protected final I ping; protected final WeakReference<T> connection; protected PingTask( I ping, T connection, TimeOutParameters parameters, ScheduledExecutorService scheduler, Set<Pair<Runnable,Executor>> listeners, Promise<Void> promise, Logger logger) { super(parameters, scheduler, listeners, promise, logger); this.connection = new WeakReference<T>(connection); this.ping = ping; connection.subscribe(this); } @Override public void handleConnectionState( Automaton.Transition<Connection.State> state) { switch (state.to()) { case CONNECTION_CLOSING: case CONNECTION_CLOSED: stop(); break; default: break; } } @Override protected synchronized void doRun() { // should ping now, or ok to wait a while? if ((parameters.getTimeOut() != NO_TIMEOUT) && (parameters.getRemaining() < parameters.getTimeOut()*0.75)) { ping(); } } @Override protected synchronized void doStop() { T connection = this.connection.get(); if (connection != null) { connection.unsubscribe(this); } super.doStop(); } @Override protected synchronized long nextTick() { // somewhat arbitrary... return Math.max(parameters.getRemaining() / 2L, 0L); } @Override protected synchronized MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper().add("connection", connection.get()); } protected synchronized void ping() { T connection = this.connection.get(); if ((connection == null) || (connection.state().compareTo(Connection.State.CONNECTION_CLOSING) >= 0)) { stop(); } else { logger.trace(LoggingMarker.PING_MARKER.get(), "PING: {}", this); parameters.setTouch(); ListenableFuture<I> future = connection.write((I) ping); future.addListener(new PingListener(future), MoreExecutors.directExecutor()); } } protected class PingListener implements Runnable { private final ListenableFuture<I> future; public PingListener(ListenableFuture<I> future) { this.future = future; } @Override public void run() { if (future.isDone()) { if (!future.isCancelled()) { try { future.get(); } catch (InterruptedException e) { throw new AssertionError(e); } catch (ExecutionException e) { promise.setException(e.getCause()); } } else { stop(); } } } } }