package edu.uw.zookeeper.client;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.ListenableFuture;
import edu.uw.zookeeper.common.Pair;
import edu.uw.zookeeper.common.Promise;
import edu.uw.zookeeper.common.PromiseTask;
import edu.uw.zookeeper.protocol.Operation;
import edu.uw.zookeeper.protocol.proto.Records;
public final class IteratingClient extends PromiseTask<Iterator<? extends Pair<? extends Records.Request, ? extends ListenableFuture<? extends Operation.ProtocolResponse<?>>>>, Void> implements Runnable {
public static IteratingClient create(
Executor executor,
Iterator<? extends Pair<? extends Records.Request, ? extends ListenableFuture<? extends Operation.ProtocolResponse<?>>>> operations,
Promise<Void> promise) {
return new IteratingClient(executor, operations, promise);
}
private final Executor executor;
private final AtomicInteger pending;
protected IteratingClient(
Executor executor,
Iterator<? extends Pair<? extends Records.Request, ? extends ListenableFuture<? extends Operation.ProtocolResponse<?>>>> operations,
Promise<Void> promise) {
super(operations, promise);
this.executor = executor;
this.pending = new AtomicInteger(0);
}
@Override
public synchronized void run() {
if (!isDone()) {
// note that iterator calls are potentially blocking
if (task().hasNext()) {
Pair<? extends Records.Request, ? extends ListenableFuture<? extends Operation.ProtocolResponse<?>>> operation;
try {
operation = task().next();
} catch (Exception e) {
setException(e);
return;
}
pending.incrementAndGet();
new PendingOperation(operation.second());
executor.execute(this);
} else if (pending.get() == 0) {
set(null);
}
}
}
protected final class PendingOperation implements Runnable {
private final ListenableFuture<? extends Operation.ProtocolResponse<?>> future;
public PendingOperation(ListenableFuture<? extends Operation.ProtocolResponse<?>> future) {
this.future = future;
future.addListener(this, executor);
}
@Override
public void run() {
if (future.isDone()) {
try {
if (!future.isCancelled()) {
try {
future.get();
} catch (InterruptedException e) {
throw Throwables.propagate(e);
} catch (ExecutionException e) {
setException(e);
}
}
} finally {
if (pending.decrementAndGet() == 0) {
executor.execute(IteratingClient.this);
}
}
}
}
}
}