package org.openslx.util; import java.util.concurrent.atomic.AtomicReference; import org.apache.log4j.Logger; public abstract class GenericDataCache { private static final Logger LOGGER = Logger.getLogger(GenericDataCache.class); /** * How long the cached data is valid after fetching */ private final int validMs; /** * Deadline when the cache goes invalid */ private long validUntil = 0; /** * The data being held */ private final AtomicReference item = new AtomicReference<>(); public GenericDataCache(int validMs) { this.validMs = validMs; } /** * Get the cached object, but refresh the cache first if * the cached instance is too old. * * @return */ public T get() { return get(CacheMode.DEFAULT); } /** * Get the cached object, using the given cache access strategy. * ALWAYS_CACHED: Never refresh the cache, except if it has never been fetched before * DEFAULT: Only fetch from remote if the cached value is too old * NEVER_CACHED: Always fetch from remote. If it fails, return null * * @param mode Cache access strategy as described above * @return T */ public T get(CacheMode mode) { switch (mode) { case ALWAYS_CACHED: if (validUntil == 0) ensureUpToDate(true); break; case DEFAULT: ensureUpToDate(false); break; case NEVER_CACHED: if (!ensureUpToDate(true)) return null; break; } return item.get(); } private synchronized boolean ensureUpToDate(boolean force) { final long now = System.currentTimeMillis(); if (!force && now < validUntil) return true; T fetched; try { fetched = update(); if (fetched == null) return false; } catch (Exception e) { LOGGER.warn("Could not fetch fresh data", e); return false; } item.set(fetched); validUntil = now + validMs; return true; } protected abstract T update() throws Exception; // public static enum CacheMode { ALWAYS_CACHED, DEFAULT, NEVER_CACHED } }