Java tutorial
/* Copyright (c) 2013 OpenPlans. All rights reserved. * This code is licensed under the BSD New License, available at the root * application directory. */ package org.geogit.di.caching; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.geogit.api.ObjectId; import org.geogit.api.RevObject; import org.geogit.api.porcelain.ConfigException; import org.geogit.storage.ConfigDatabase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheStats; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.inject.Provider; abstract class CacheFactory { private static final Logger LOGGER = LoggerFactory.getLogger(CacheFactory.class); private volatile Cache<ObjectId, RevObject> cache; private final Provider<ConfigDatabase> configDb; private final String configKeywordPrefix; public CacheFactory(final String configKeywordPrefix, final Provider<ConfigDatabase> configDb) { this.configKeywordPrefix = configKeywordPrefix; this.configDb = configDb; } public Cache<ObjectId, RevObject> get() { if (cache == null) { createCache(); } return cache; } protected synchronized void createCache() { if (cache != null) { return; } if (!cacheIsEnabled()) { this.cache = NO_CACHE; return; } final int maxSize = getConfig("maxSize", 50 * 1000); final int concurrencyLevel = getConfig("concurrencyLevel", 0); if (concurrencyLevel == 0) { this.cache = new SimpleCache<ObjectId, RevObject>(maxSize); LOGGER.info("Cache '{}' configured with maxSize: {}", configKeywordPrefix, maxSize); return; } final int expireSeconds = getConfig("expireSeconds", 30); final int initialCapacity = getConfig("initialCapacity", 10 * 1000); CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder(); cacheBuilder = cacheBuilder.maximumSize(maxSize); cacheBuilder.expireAfterAccess(expireSeconds, TimeUnit.SECONDS); cacheBuilder.initialCapacity(initialCapacity); cacheBuilder.concurrencyLevel(concurrencyLevel); try { this.cache = cacheBuilder.build(); } catch (RuntimeException e) { LOGGER.error( "Error configuring cache '{}' with maxSize: {}, expireSeconds: {}, initialCapacity: {}, concurrencyLevel: {}", configKeywordPrefix, maxSize, expireSeconds, initialCapacity, concurrencyLevel, e); throw e; } LOGGER.debug( "Cache '{}' configured with maxSize: {}, expireSeconds: {}, initialCapacity: {}, concurrencyLevel: {}", configKeywordPrefix, maxSize, expireSeconds, initialCapacity, concurrencyLevel); } private boolean cacheIsEnabled() { LOGGER.debug("checking if cache {} is enabled...", configKeywordPrefix); final boolean enabled = getConfig("enabled", Boolean.TRUE); if (!enabled) { LOGGER.info("Cache {} is disabled", configKeywordPrefix); } return enabled; } @SuppressWarnings("unchecked") private <T> T getConfig(final String keyword, final T defaultValue) { final String kw = configKeywordPrefix + "." + keyword; ConfigDatabase configDatabase = configDb.get(); try { Optional<? extends Object> value = configDatabase.get(kw, defaultValue.getClass()); if (value.isPresent()) { LOGGER.trace("Got cache config property {} = {}", kw, value.get()); return (T) value.get(); } } catch (ConfigException e) { return defaultValue; } return defaultValue; } private static final Cache<ObjectId, RevObject> NO_CACHE = new Cache<ObjectId, RevObject>() { @Override public RevObject getIfPresent(Object key) { return null; } @Override public RevObject get(ObjectId key, Callable<? extends RevObject> valueLoader) throws ExecutionException { return null; } @Override public ImmutableMap<ObjectId, RevObject> getAllPresent(Iterable<?> keys) { return ImmutableMap.of(); } @Override public void put(ObjectId key, RevObject value) { // do nothing } @Override public void putAll(Map<? extends ObjectId, ? extends RevObject> m) { // do nothing } @Override public void invalidate(Object key) { // do nothing } @Override public void invalidateAll(Iterable<?> keys) { // do nothing } @Override public void invalidateAll() { // do nothing } @Override public long size() { return 0; } @Override public CacheStats stats() { return new CacheStats(0, 0, 0, 0, 0, 0); } @Override public ConcurrentMap<ObjectId, RevObject> asMap() { return Maps.newConcurrentMap(); } @Override public void cleanUp() { // do nothing } }; public static class SimpleCache<K, V> implements Cache<K, V> { private static class LinkedCache<K, V> extends LinkedHashMap<K, V> { private static final long serialVersionUID = 1L; private final int maxEntries; public LinkedCache(int maxEntries) { this.maxEntries = maxEntries; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxEntries; } }; private Map<K, V> map; public SimpleCache(int maxEntries) { map = Collections.synchronizedMap(new LinkedCache<K, V>(maxEntries)); } @Override public V getIfPresent(Object key) { return map.get(key); } @Override public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException { throw new UnsupportedOperationException("not in use"); } @Override public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) { throw new UnsupportedOperationException("not in use"); } @Override public void put(K key, V value) { map.put(key, value); } @Override public void putAll(Map<? extends K, ? extends V> m) { map.putAll(m); } @Override public void invalidate(Object key) { map.remove(key); } @Override public void invalidateAll(Iterable<?> keys) { for (Object k : keys) { map.remove(k); } } @Override public void invalidateAll() { map.clear(); } @Override public long size() { return map.size(); } @Override public CacheStats stats() { return new CacheStats(0, 0, 0, 0, 0, 0); } @Override public ConcurrentMap<K, V> asMap() { throw new UnsupportedOperationException(); } @Override public void cleanUp() { map.clear(); } } }