Skip to content

Instantly share code, notes, and snippets.

@magro
Created October 14, 2011 21:45
Show Gist options
  • Save magro/1288448 to your computer and use it in GitHub Desktop.
Save magro/1288448 to your computer and use it in GitHub Desktop.
A simple cache that holds all entries for a given type and updates itself in the background to keep the cache up to date.
import java.util.ArrayList;
import java.util.Arrays;
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 java.util.concurrent.atomic.AtomicReference;
/**
* A simple 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 FullObjectCache<K, V> {
public static void main(String[] args) throws InterruptedException {
final List<String> sor = new ArrayList<String>(Arrays.asList("Foo"));
final CacheLoader<String> cacheLoader = new CacheLoader<String>() {
public Iterable<String> loadAll() {
return sor;
}
};
final KeyGenerator<String, String> keyGenerator = new KeyGenerator<String, String>() {
public String getKey(String a) {
return a.toLowerCase(Locale.ENGLISH);
}
};
final FullObjectCache<String, String> cache = new FullObjectCache<String, String>(
cacheLoader, keyGenerator, 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 bar: " + cache.get("bar"));
cache.shutdown();
}
public static interface CacheLoader<V> {
/**
* Loads all items that shall be cached.
*/
Iterable<V> loadAll();
}
public static interface KeyGenerator<A, B> {
/**
* Transforms an A to a B.
*/
B getKey(A a);
}
private final CacheLoader<V> cacheLoader;
private final KeyGenerator<V, K> keyGenerator;
private final AtomicReference<Map<K, V>> entries;
private final Timer timer;
/**
* Creates a new cache that's updated in the specified reloadInterval.
*
* @param cacheLoader
* used to load all items to cache.
* @param keyGenerator
* used to generate the cache key for a cached value.
* @param reloadInterval
* interval in milliseconds to reload the cache.
*/
public FullObjectCache(final CacheLoader<V> cacheLoader,
final KeyGenerator<V, K> keyGenerator,
final long reloadInterval) {
this.cacheLoader = cacheLoader;
this.keyGenerator = keyGenerator;
entries = new AtomicReference<Map<K, V>>();
// as it takes some time until the timer task / thread is started and run
// we update the cache in this thread and use the reloadInterval as initial delay
updateCache();
timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateCache();
}
}, reloadInterval, reloadInterval);
}
/**
* Shutdown this cache and free acquired resources.
*/
public void shutdown() {
timer.cancel();
}
/**
* Triggers an update of the cache, returns when the update is finished.
*/
public void updateCache() {
final Iterable<V> items = cacheLoader.loadAll();
final Map<K, V> entries = new HashMap<K, V>();
for (final V v : items) {
entries.put(keyGenerator.getKey(v), v);
}
this.entries.set(entries);
}
/**
* 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) {
return entries.get().get(key);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment