package edu.uw.zookeeper.common; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; 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.collect.Collections2; import com.google.common.collect.ForwardingCollection; import com.google.common.collect.ForwardingDeque; import com.google.common.collect.ForwardingList; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; /** * Not threadsafe. */ public class ChainedFutures<E extends ListenableFuture<?>, V extends FutureChain<E>, U extends ChainedFutures.ChainedProcessor<E,? super V>> extends ForwardingCollection<E> implements FutureChain<E>, Callable<Optional<V>> { public static interface ChainedProcessor<E extends ListenableFuture<?>, T extends FutureChain<E>> extends Processor<T, Optional<? extends E>> { } public static <V> ChainedFuturesTask<V> run( ChainedResult<V,?,?,?> chain) { return run(chain, SettableFuturePromise.<V>create()); } public static <V> ChainedFuturesTask<V> run( ChainedResult<V,?,?,?> chain, Promise<V> promise) { ChainedFuturesTask<V> task = task(chain, promise); task.run(); return task; } public static <V> ChainedFuturesTask<V> task( ChainedResult<V,?,?,?> chain) { return task(chain, SettableFuturePromise.<V>create()); } public static <V> ChainedFuturesTask<V> task( ChainedResult<V,?,?,?> chain, Promise<V> promise) { return new ChainedFuturesTask<V>(chain, promise); } public static <V, C extends FutureChain<?>, T extends Processor<? super C, ? extends V>, U extends ChainedFutures<?,? extends C,?>> ChainedResult<V,C,T,U> result( T result, U chain) { return ChainedResult.create(result, chain); } public static <V> Processor<FutureChain<?>,V> castLast() { return new Processor<FutureChain<?>,V>() { @SuppressWarnings("unchecked") @Override public V apply(FutureChain<?> input) throws Exception { return (V) input.getLast().get(); } }; } public static <V> ChainedResult<V,?,?,?> castLast(ChainedFutures<?,?,?> chain) { return result(ChainedFutures.<V>castLast(), chain); } public static <E extends ListenableFuture<?>, V extends FutureChain<E>, U extends ChainedFutures.ChainedProcessor<E,? super V>> ChainedFutures<E,V,U> apply( U processor, V chain) { return apply(processor, chain, LogManager.getLogger(ChainedFutures.class)); } public static <E extends ListenableFuture<?>, U extends ChainedFutures.ChainedProcessor<E,? super ListChain<E,LinkedList<E>>>> ChainedFutures<E,ListChain<E,LinkedList<E>>,U> linkedList( U processor) { return list(processor, Lists.<E>newLinkedList()); } public static <E extends ListenableFuture<?>, U extends ChainedFutures.ChainedProcessor<E,? super ListChain<E,ArrayList<E>>>> ChainedFutures<E,ListChain<E,ArrayList<E>>,U> arrayList( U processor) { return list(processor, Lists.<E>newArrayList()); } public static <E extends ListenableFuture<?>, U extends ChainedFutures.ChainedProcessor<E,? super ListChain<E,ArrayList<E>>>> ChainedFutures<E,ListChain<E,ArrayList<E>>,U> arrayList( U processor, int capacity) { return list(processor, Lists.<E>newArrayListWithCapacity(capacity)); } public static <E extends ListenableFuture<?>, T extends List<E>, U extends ChainedFutures.ChainedProcessor<E,? super ListChain<E,T>>> ChainedFutures<E,ListChain<E,T>,U> list( U processor, T chain) { return apply(processor, list(chain)); } public static <E extends ListenableFuture<?>, U extends ChainedFutures.ChainedProcessor<E,? super DequeChain<E,ArrayDeque<E>>>> ChainedFutures<E,DequeChain<E,ArrayDeque<E>>,U> arrayDeque( U processor) { return deque(processor, Queues.<E>newArrayDeque()); } public static <E extends ListenableFuture<?>, T extends Deque<E>, U extends ChainedFutures.ChainedProcessor<E,? super DequeChain<E,T>>> ChainedFutures<E,DequeChain<E,T>,U> deque( U processor, T chain) { return apply(processor, deque(chain)); } public static <E extends ListenableFuture<?>, V extends FutureChain<E>, U extends ChainedFutures.ChainedProcessor<E,? super V>> ChainedFutures<E,V,U> apply( U processor, V chain, Logger logger) { return new ChainedFutures<E,V,U>(processor, chain, logger); } public static <E extends ListenableFuture<?>, T extends List<E>> ListChain<E,T> list(T delegate) { return ListChain.create(delegate); } public static <E extends ListenableFuture<?>, T extends Deque<E>> DequeChain<E,T> deque(T delegate) { return new DequeChain<E,T>(delegate); } protected final Logger logger; protected final U processor; protected final V chain; protected ChainedFutures( U processor, V chain, Logger logger) { this.logger = logger; this.processor = processor; this.chain = chain; } public U processor() { return processor; } public V chain() { return chain; } @Override public Optional<V> call() throws Exception { if (chain().isEmpty() || chain().getLast().isDone()) { Optional<? extends E> next = processor().apply(chain()); if (logger.isDebugEnabled()) { logger.debug("APPLIED {} TO {} => {}", processor(), Collections2.transform(chain(), ToStringListenableFuture.toString3rdParty()), next.orNull()); } if (next.isPresent()) { if (!chain().add(LoggingFutureListener.listen(logger, next.get()))) { throw new IllegalStateException(); } } else { return Optional.of(chain()); } } return Optional.absent(); } @Override public E getLast() { return chain().getLast(); } @Override public String toString() { return toStringHelper(MoreObjects.toStringHelper(this)).toString(); } @Override protected Collection<E> delegate() { return chain(); } protected MoreObjects.ToStringHelper toStringHelper(MoreObjects.ToStringHelper helper) { helper.addValue(processor()); ImmutableList.Builder<String> values = ImmutableList.builder(); for (E future: chain()) { values.add(ToStringListenableFuture.toString3rdParty(future)); } return helper.addValue(values.build()); } public static class ListChain<E extends ListenableFuture<?>, T extends List<E>> extends ForwardingList<E> implements FutureChain.FutureListChain<E> { public static <E extends ListenableFuture<?>, T extends List<E>> ListChain<E,T> create(T delegate) { return new ListChain<E,T>(delegate); } private final T delegate; protected ListChain( T delegate) { this.delegate = delegate; } @Override public E getLast() { return get(size() - 1); } @Override protected T delegate() { return delegate; } } public static class DequeChain<E extends ListenableFuture<?>, T extends Deque<E>> extends ForwardingDeque<E> implements FutureChain.FutureDequeChain<E> { public static <E extends ListenableFuture<?>, T extends Deque<E>> DequeChain<E,T> create(T delegate) { return new DequeChain<E,T>(delegate); } private final T delegate; protected DequeChain( T delegate) { this.delegate = delegate; } @Override protected T delegate() { return delegate; } } /** * Not threadsafe. */ public static final class ChainedResult<V, C extends FutureChain<?>, T extends Processor<? super C, ? extends V>, U extends ChainedFutures<?,? extends C,?>> extends AbstractPair<T,U> implements Callable<Optional<V>> { public static <V, C extends FutureChain<?>, T extends Processor<? super C, ? extends V>, U extends ChainedFutures<?,? extends C,?>> ChainedResult<V,C,T,U> create( T result, U chain) { return new ChainedResult<V,C,T,U>(result, chain); } protected ChainedResult( T result, U chain) { super(result, chain); } public T result() { return first; } public U chain() { return second; } @Override public Optional<V> call() throws Exception { Optional<? extends C> futures = chain().call(); if (futures.isPresent()) { return Optional.<V>of(result().apply(futures.get())); } return Optional.absent(); } } public static final class ChainedFuturesTask<V> extends CallablePromiseTask<ChainedResult<V,?,?,?>,V> implements Runnable { public static <V> ChainedFuturesTask<V> create( ChainedResult<V,?,?,?> chain, Promise<V> promise) { return new ChainedFuturesTask<V>(chain, promise); } protected ChainedFuturesTask( ChainedResult<V,?,?,?> chain, Promise<V> promise) { super(chain, promise); } @Override public synchronized void run() { FutureChain<?> chain = task().chain().chain(); if (!isDone()) { super.run(); chain.getLast().addListener(this, MoreExecutors.directExecutor()); } else if (isCancelled()) { if (!chain.isEmpty()) { chain.getLast().cancel(false); } } } @Override public synchronized String toString() { return super.toString(); } } }