/* Copyright (c) 2013-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.di.caching;
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.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.api.porcelain.ConfigException;
import org.locationtech.geogig.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_000);
final int concurrencyLevel = getConfig("concurrencyLevel", 4);
final int expireSeconds = getConfig("expireSeconds", 300);
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);
cacheBuilder.softValues();
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.debug("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 {
try {
return valueLoader.call();
} catch (Exception e) {
throw new ExecutionException(e);
}
}
@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
}
};
}