package com.netflix.evcache.operation; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.evcache.EVCacheLatch; import com.netflix.evcache.pool.ServerGroup; import net.spy.memcached.internal.ListenableFuture; import net.spy.memcached.internal.OperationCompletionListener; import net.spy.memcached.internal.OperationFuture; @edu.umd.cs.findbugs.annotations.SuppressFBWarnings({ "DE_MIGHT_IGNORE", "EI_EXPOSE_REP2" }) public class EVCacheFutures implements ListenableFuture<Boolean, OperationCompletionListener>, OperationCompletionListener { private Logger log = LoggerFactory.getLogger(EVCacheFutures.class); private final OperationFuture<Boolean>[] futures; private final String app; private final ServerGroup serverGroup; private final String key; private final AtomicInteger completionCounter; private final EVCacheLatch latch; public EVCacheFutures(OperationFuture<Boolean>[] futures, String key, String app, ServerGroup serverGroup, EVCacheLatch latch) { this.futures = futures; this.app = app; this.serverGroup = serverGroup; this.key = key; this.latch = latch; this.completionCounter = new AtomicInteger(futures.length); if (latch != null && latch instanceof EVCacheLatchImpl) ((EVCacheLatchImpl) latch).addFuture(this); for (int i = 0; i < futures.length; i++) { final OperationFuture<Boolean> of = futures[i]; if (of.isDone()) { try { onComplete(of); } catch (Exception e) { } } else { of.addListener(this); } } } public boolean cancel(boolean mayInterruptIfRunning) { if(log.isDebugEnabled()) log.debug("Operation cancelled", new Exception()); for (OperationFuture<Boolean> future : futures) { future.cancel(); } return true; } @Override public boolean isCancelled() { for (OperationFuture<Boolean> future : futures) { if (future.isCancelled() == false) return false; } return true; } @Override public boolean isDone() { for (OperationFuture<Boolean> future : futures) { if (future.isDone() == false) return false; } return true; } @Override public Boolean get() throws InterruptedException, ExecutionException { for (OperationFuture<Boolean> future : futures) { if (future.get() == false) return false; } return true; } @Override public Boolean get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { for (OperationFuture<Boolean> future : futures) { if (future.get(timeout, unit) == false) return false; } return true; } public String getKey() { return key; } public String getApp() { return app; } public String getZone() { return serverGroup.getZone(); } public String getReplicaSetNamae() { return serverGroup.getName(); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("EVCacheFutures [futures=["); for (OperationFuture<Boolean> future : futures) sb.append(future); sb.append("], app=").append(app).append(", ReplicaSet=").append(serverGroup.toString()).append("]"); return sb.toString(); } @Override public void onComplete(OperationFuture<?> future) throws Exception { int val = completionCounter.decrementAndGet(); if (val == 0) { if (latch != null) latch.onComplete(future);// Pass the last future to get completed } } @Override public Future<Boolean> addListener(OperationCompletionListener listener) { return this; } @Override public Future<Boolean> removeListener(OperationCompletionListener listener) { return this; } }