Created
October 14, 2011 21:49
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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