package com.datascience.datastoring.storages; import java.util.concurrent.ExecutionException; import com.datascience.core.base.Project; import com.datascience.datastoring.jobs.IJobStorage; import com.google.common.util.concurrent.UncheckedExecutionException; import org.apache.log4j.Logger; import com.datascience.datastoring.jobs.Job; import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; /** * * @author konrad */ public class CachedJobStorage extends WrappedJobStorage{ private static Logger logger = Logger.getLogger(CachedJobStorage.class); protected LoadingCache<String, Optional<Job>> cache; @SuppressWarnings("OverridableMethodCallInConstructor") public CachedJobStorage(final IJobStorage cachedJobStorage, int cacheSize){ super(cachedJobStorage); cache = CacheBuilder.newBuilder() .maximumSize(cacheSize) .removalListener(getRemovalListener()) .build(getLoader()); } protected CacheLoader<String, Optional<Job>> getLoader() { return new CacheLoader<String, Optional<Job>>(){ @Override public Optional<Job> load(String id) throws Exception { // This is tricky - if absent we throw // exception to avoid putting this value into cache Job job = wrappedJobStorage.get(id); if (job == null) { throw new NotInCachedException(); } return Optional.of(job); } }; } protected RemovalListener<String, Optional<Job>> getRemovalListener(){ return new RemovalListener<String, Optional<Job>>() { @Override public void onRemoval(RemovalNotification<String, Optional<Job>> rn){ try { wrappedJobStorage.add(rn.getValue().get()); } catch (Exception ex) { logger.error("CachedJobStorage on eviction", ex); } } }; } @Override public <T extends Project> Job<T> get(String id) throws Exception { logger.debug("CACHED_JS: get " + id); try { return cache.get(id).get(); } catch (ExecutionException ex){ if (ex.getCause() instanceof NotInCachedException) { return null; } else { throw ex; } } catch (UncheckedExecutionException ex) { logger.fatal("CachedJobStorage", ex.getCause()); throw ex; } } /** * @param job * @throws Exception */ @Override public void add(Job job) throws Exception { logger.debug("CACHED_JS: add " + job.getId()); cache.put(job.getId(), Optional.of(job)); } /** * TODO: think whether this should be synchronized * @param id * @throws Exception */ @Override public void remove(Job job) throws Exception { logger.debug("CACHED_JS: rm " + job.getId()); String id = job.getId(); cache.invalidate(id); wrappedJobStorage.remove(job); } @Override public void test() throws Exception { wrappedJobStorage.test(); } @Override public void stop() throws Exception { cache.invalidateAll(); cache.cleanUp(); wrappedJobStorage.stop(); } @Override public void clear() throws Exception { cache.invalidateAll(); cache.cleanUp(); wrappedJobStorage.clear(); } @Override protected void finalize() throws Throwable { super.finalize(); stop(); } @Override public void flush(Job job){ } @Override public String toString() { return "Cached" + wrappedJobStorage.toString(); } static protected class NotInCachedException extends Exception{ } }