package com.googlecode.objectify.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>This class maintains a thread local list of all the outstanding Future<?> objects
* that have pending triggers. When a Future<?> is done and its trigger is executed,
* it is removed from the list.</p>
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
public class PendingFutures
{
/** */
private static final Logger log = Logger.getLogger(PendingFutures.class.getName());
/**
* We use ConcurrentHashMap not for concurrency but because it doesn't throw
* ConcurrentModificationException. We need to be able to iterate while Futures remove
* themselves from the set. A Set is just a Map of key to key.
*/
private static ThreadLocal<Map<Future<?>, Future<?>>> pending = new ThreadLocal<Map<Future<?>, Future<?>>>() {
@Override
protected Map<Future<?>, Future<?>> initialValue() {
return new ConcurrentHashMap<>(64, 0.75f, 1);
}
};
/**
* Register a pending Future that has a callback.
* @param future must have at least one callback
*/
public static void addPending(Future<?> future) {
pending.get().put(future, future);
}
/**
* Deregister a pending Future that had a callback.
*/
public static void removePending(Future<?> future) {
pending.get().remove(future);
}
/**
* Iterate through all pending futures and get() them, forcing any callbacks to be called.
* This is used only by the AsyncCacheFilter (if using cache without Objectify) or ObjectifyFilter
* (if using Objectify normally) because we don't have a proper hook otherwise.
*/
public static void completeAllPendingFutures() {
// This will cause done Futures to fire callbacks and remove themselves
for (Future<?> fut: pending.get().keySet()) {
try {
fut.get();
}
catch (Exception e) {
log.log(Level.SEVERE, "Error cleaning up pending Future: " + fut, e);
}
}
}
}