Skip to content

Instantly share code, notes, and snippets.

@magro
Created October 14, 2011 21:49
Show Gist options
  • Save magro/1288458 to your computer and use it in GitHub Desktop.
Save magro/1288458 to your computer and use it in GitHub Desktop.
An ehcache backed cache that holds all entries for a given type and updates itself in the background to keep the cache up to date.
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.PinningConfiguration;
import net.sf.ehcache.config.PinningConfiguration.Store;
import net.sf.ehcache.loader.CacheLoader;
/**
* An ehcache backed cache that holds all entries for a given type and updates itself in the
* background to keep the cache up to date.
*
* @author Martin Grotzke
*
* @param <K>
* the type of cache keys.
* @param <V>
* the type of cached values.
*/
public class FullObjectEhCache<K, V> {
public static void main(String[] args) throws InterruptedException {
final KeyGenerator<String, String> keyGenerator = new KeyGenerator<String, String>() {
public String getKey(String a) {
return a.toLowerCase(Locale.ENGLISH);
}
};
final List<String> sor = new ArrayList<String>(Arrays.asList("Foo"));
final ExtendedCacheLoader<String, String> cacheLoader = new ExtendedCacheLoader(sor, keyGenerator);
final FullObjectEhCache<String, String> cache = new FullObjectEhCache<String, String>(
cacheLoader, 100);
System.out.println("Found value for foo: " + cache.get("foo"));
System.out.println("Found value for bar: " + cache.get("bar"));
sor.add("Bar");
Thread.sleep(200);
System.out.println("Found value for foo: " + cache.get("foo"));
System.out.println("Found value for bar: " + cache.get("bar"));
sor.remove("Foo");
Thread.sleep(1200); // wait more than 1 seconds for expiration
System.out.println("Found value for foo: " + cache.get("foo"));
System.out.println("Found value for bar: " + cache.get("bar"));
cache.shutdown();
}
public static interface KeyGenerator<A, B> {
/**
* Transforms an A to a B.
*/
B getKey(A a);
}
/**
* An extended version of {@link CacheLoader} that provides access to all items
* to cache via {@link #getItems()}.s
*/
public static class ExtendedCacheLoader<K, V> implements CacheLoader {
private final List<V> items;
private final KeyGenerator<V, K> keyGenerator;
public ExtendedCacheLoader(List<V> items, KeyGenerator<V, K> keyGenerator) {
this.items = items;
this.keyGenerator = keyGenerator;
}
public List<?> getItems() {
return items;
}
/**
* Actually invoked with values, not with keys.
*/
public Map loadAll(Collection values, Object argument) {
final Map<K,V> map = new HashMap<K, V>();
for (Object value : values) {
final V v = (V)value;
map.put(keyGenerator.getKey(v), v);
}
return map;
}
public Map loadAll(Collection keys) {
return loadAll(keys, null);
}
public Object load(Object key, Object argument) {
throw new UnsupportedOperationException();
}
public Object load(Object key) throws CacheException {
throw new UnsupportedOperationException();
}
public void init() {
}
public Status getStatus() {
return Status.STATUS_ALIVE;
}
public String getName() {
return getClass().getSimpleName();
}
public void dispose() throws CacheException {
}
public CacheLoader clone(Ehcache cache) throws CloneNotSupportedException {
return new ExtendedCacheLoader(items, keyGenerator);
}
}
private final Cache cache;
private final Timer timer;
public FullObjectEhCache(final ExtendedCacheLoader<K, V> cacheLoader, final long reloadInterval) {
final CacheConfiguration config = new CacheConfiguration().name("foc")
// set the TTL to the reloadInterval (and a bit more) to enable expiration of outdated items
.timeToLiveSeconds(MILLISECONDS.toSeconds(reloadInterval) + 1)
.copyOnRead(false).copyOnWrite(false)
.pinning(new PinningConfiguration().store(Store.LOCALHEAP));
config.validateConfiguration();
cache = new Cache(config);
cache.registerCacheLoader(cacheLoader);
cache.initialise();
// populates the cache initially
cache.getAllWithLoader(cacheLoader.getItems(), null);
timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
cache.loadAll(cacheLoader.getItems(), null);
}
}, reloadInterval, reloadInterval);
}
/**
* Shutdown this cache and free acquired resources.
*/
public void shutdown() {
timer.cancel();
cache.dispose();
}
/**
* Get a cached value by the given cache key or <code>null</code> if not existing.
*
* @param key
* the cache key, must not be <code>null</code>.
*
* @return the cached value if existing, otherwise <code>null</code>.
*/
public V get(final K key) {
final Element element = cache.get(key);
return (V) (element != null ? element.getValue() : null);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment