package org.caudexorigo.ds;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import org.caudexorigo.ErrorAnalyser;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
public class Cache<K, V>
{
// private static Logger log = LoggerFactory.getLogger(Cache.class);
private final ConcurrentMap<K, FutureTask<V>> map = new ConcurrentHashMap<K, FutureTask<V>>();
public V get(final K key, final CacheFiller<K, V> cf) throws InterruptedException
{
FutureTask<V> f = map.get(key);
if (f == null)
{
Callable<V> c = new Callable<V>()
{
public V call() throws InterruptedException
{
// return value associated with key
return cf.populate(key);
}
};
f = new FutureTask<V>(c);
FutureTask<V> old = map.putIfAbsent(key, f);
if (old == null)
{
try
{
// log.debug("Cache miss. Populating cache, key: {}", key.toString());
f.run();
}
catch (Throwable t)
{
map.remove(key);
throw new RuntimeException(t);
}
}
else
{
// log.debug("Cache hit. Retrieve from cache, key: {}", key.toString());
f = old;
}
}
try
{
return f.get();
}
catch (Throwable t)
{
map.remove(key);
throw new RuntimeException(t);
}
}
public boolean containsKey(K key) throws InterruptedException
{
return map.containsKey(key);
}
public void remove(K key) throws InterruptedException
{
map.remove(key);
}
public void removeValue(V value) throws InterruptedException
{
try
{
Set<Entry<K, FutureTask<V>>> items = map.entrySet();
for (Entry<K, FutureTask<V>> entry : items)
{
if (entry.getValue().get().equals(value))
{
items.remove(entry);
}
}
}
catch (ExecutionException e)
{
throw new RuntimeException(e);
}
}
public Collection<V> values() throws InterruptedException
{
Collection<FutureTask<V>> fvalues = map.values();
Collection<V> values = new ArrayList<V>(fvalues.size());
for (FutureTask<V> fv : fvalues)
{
try
{
values.add(fv.get());
}
catch (Throwable t)
{
Throwable r = ErrorAnalyser.findRootCause(t);
System.out.printf("Cache.values() -> %s%n", r.getMessage());
}
}
return values;
}
public Set<K> keys()
{
return map.keySet();
}
public int size()
{
return map.size();
}
}