package hudson.plugins.sfee; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Memoizer<A, V> implements Computable<A, V> { private final ConcurrentMap<A, ExpiringFutureTask<V>> cache = new ConcurrentHashMap<A, ExpiringFutureTask<V>>(); private final Computable<A, V> c; public Memoizer(Computable<A, V> c) { this.c = c; } public V compute(final A arg) throws InterruptedException { while (true) { ExpiringFutureTask<V> f = cache.get(arg); if (f != null && f.hasExpired()) { cache.remove(arg); f = null; } if (f == null) { Callable<V> eval = new Callable<V>() { public V call() throws InterruptedException { return c.compute(arg); } }; ExpiringFutureTask<V> ft = new ExpiringFutureTask<V>(eval); f = cache.putIfAbsent(arg, ft); if (f == null) { f = ft; ft.run(); } } try { return f.get(); } catch (CancellationException e) { cache.remove(arg, f); } catch (ExecutionException e) { // Kabutz: this is my addition to the code... try { cache.remove(arg, f); throw e.getCause(); } catch (RuntimeException ex) { throw ex; } catch (Error ex) { throw ex; } catch (Throwable t) { throw new IllegalStateException("Not unchecked", t); } } } } private static class ExpiringFutureTask<V> extends FutureTask<V> { private long creationTime = System.currentTimeMillis(); public ExpiringFutureTask(Callable<V> callable) { super(callable); } public boolean hasExpired() { return System.currentTimeMillis() - creationTime > 60000; } } }