Skip to content

Commit

Permalink
Allow for a custom JCache config source (fixes #312)
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-manes committed Sep 29, 2019
1 parent ee1100e commit 92c8d59
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 34 deletions.
Expand Up @@ -48,57 +48,61 @@
* @author ben.manes@gmail.com (Ben Manes)
*/
final class CacheFactory {
final Config rootConfig;
final CacheManager cacheManager;

public CacheFactory(CacheManager cacheManager, Config rootConfig) {
this.cacheManager = requireNonNull(cacheManager);
this.rootConfig = requireNonNull(rootConfig);
}
private CacheFactory() {}

/**
* Returns a if the cache definition is found in the external settings file.
*
* @param cacheName the name of the cache
* @return {@code true} if a definition exists
*/
public boolean isDefinedExternally(String cacheName) {
return TypesafeConfigurator.cacheNames(rootConfig).contains(cacheName);
public static boolean isDefinedExternally(String cacheName) {
return TypesafeConfigurator.cacheNames(rootConfig()).contains(cacheName);
}

/**
* Returns a newly created cache instance if a definition is found in the external settings file.
*
* @param cacheManager the owner of the cache instance
* @param cacheName the name of the cache
* @return a new cache instance or null if the named cache is not defined in the settings file
*/
public @Nullable <K, V> CacheProxy<K, V> tryToCreateFromExternalSettings(String cacheName) {
Optional<CaffeineConfiguration<K, V>> configuration =
TypesafeConfigurator.from(rootConfig, cacheName);
return configuration.isPresent() ? createCache(cacheName, configuration.get()) : null;
public static @Nullable <K, V> CacheProxy<K, V> tryToCreateFromExternalSettings(
CacheManager cacheManager, String cacheName) {
return TypesafeConfigurator.<K, V>from(rootConfig(), cacheName)
.map(configuration -> createCache(cacheManager, cacheName, configuration))
.orElse(null);
}

/**
* Returns a fully constructed cache based on the cache
*
* @param cacheManager the owner of the cache instance
* @param cacheName the name of the cache
* @param configuration the full cache definition
* @return a newly constructed cache instance
*/
public <K, V> CacheProxy<K, V> createCache(String cacheName, Configuration<K, V> configuration) {
public static <K, V> CacheProxy<K, V> createCache(CacheManager cacheManager,
String cacheName, Configuration<K, V> configuration) {
CaffeineConfiguration<K, V> config = resolveConfigurationFor(configuration);
return new Builder<>(cacheName, config).build();
return new Builder<>(cacheManager, cacheName, config).build();
}

/** Returns the resolved configuration. */
private static Config rootConfig() {
return requireNonNull(TypesafeConfigurator.configSource().get());
}

/** Copies the configuration and overlays it on top of the default settings. */
@SuppressWarnings("PMD.AccessorMethodGeneration")
private <K, V> CaffeineConfiguration<K, V> resolveConfigurationFor(
private static <K, V> CaffeineConfiguration<K, V> resolveConfigurationFor(
Configuration<K, V> configuration) {
if (configuration instanceof CaffeineConfiguration<?, ?>) {
return new CaffeineConfiguration<>((CaffeineConfiguration<K, V>) configuration);
}

CaffeineConfiguration<K, V> template = TypesafeConfigurator.defaults(rootConfig);
CaffeineConfiguration<K, V> template = TypesafeConfigurator.defaults(rootConfig());
if (configuration instanceof CompleteConfiguration<?, ?>) {
CompleteConfiguration<K, V> complete = (CompleteConfiguration<K, V>) configuration;
template.setReadThrough(complete.isReadThrough());
Expand All @@ -120,19 +124,21 @@ private <K, V> CaffeineConfiguration<K, V> resolveConfigurationFor(
}

/** A one-shot builder for creating a cache instance. */
private final class Builder<K, V> {
private static final class Builder<K, V> {
final Ticker ticker;
final String cacheName;
final Executor executor;
final CacheManager cacheManager;
final ExpiryPolicy expiryPolicy;
final EventDispatcher<K, V> dispatcher;
final JCacheStatisticsMXBean statistics;
final Caffeine<Object, Object> caffeine;
final CaffeineConfiguration<K, V> config;

Builder(String cacheName, CaffeineConfiguration<K, V> config) {
Builder(CacheManager cacheManager, String cacheName, CaffeineConfiguration<K, V> config) {
this.config = config;
this.cacheName = cacheName;
this.cacheManager = cacheManager;
this.caffeine = Caffeine.newBuilder();
this.statistics = new JCacheStatisticsMXBean();
this.ticker = config.getTickerFactory().create();
Expand Down
Expand Up @@ -33,9 +33,6 @@

import org.checkerframework.checker.nullness.qual.Nullable;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

/**
* An implementation of JSR-107 {@link CacheManager} that manages Caffeine-based caches.
*
Expand All @@ -46,21 +43,14 @@ public final class CacheManagerImpl implements CacheManager {
private final WeakReference<ClassLoader> classLoaderReference;
private final Map<String, CacheProxy<?, ?>> caches;
private final CachingProvider cacheProvider;
private final CacheFactory cacheFactory;
private final Properties properties;
private final URI uri;

private volatile boolean closed;

public CacheManagerImpl(CachingProvider cacheProvider, URI uri, ClassLoader classLoader,
Properties properties) {
this(cacheProvider, uri, classLoader, properties, ConfigFactory.load());
}

public CacheManagerImpl(CachingProvider cacheProvider, URI uri, ClassLoader classLoader,
Properties properties, Config rootConfig) {
public CacheManagerImpl(CachingProvider cacheProvider,
URI uri, ClassLoader classLoader, Properties properties) {
this.classLoaderReference = new WeakReference<>(requireNonNull(classLoader));
this.cacheFactory = new CacheFactory(this, rootConfig);
this.cacheProvider = requireNonNull(cacheProvider);
this.properties = requireNonNull(properties);
this.caches = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -96,10 +86,10 @@ public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(
CacheProxy<?, ?> cache = caches.compute(cacheName, (name, existing) -> {
if ((existing != null) && !existing.isClosed()) {
throw new CacheException("Cache " + cacheName + " already exists");
} else if (cacheFactory.isDefinedExternally(cacheName)) {
} else if (CacheFactory.isDefinedExternally(cacheName)) {
throw new CacheException("Cache " + cacheName + " is configured externally");
}
return cacheFactory.createCache(cacheName, configuration);
return CacheFactory.createCache(this, cacheName, configuration);
});
enableManagement(cache.getName(), cache.getConfiguration().isManagementEnabled());
enableStatistics(cache.getName(), cache.getConfiguration().isStatisticsEnabled());
Expand Down Expand Up @@ -136,7 +126,7 @@ public <K, V> CacheProxy<K, V> getCache(String cacheName) {
requireNotClosed();

CacheProxy<?, ?> cache = caches.computeIfAbsent(cacheName, name -> {
CacheProxy<?, ?> created = cacheFactory.tryToCreateFromExternalSettings(name);
CacheProxy<?, ?> created = CacheFactory.tryToCreateFromExternalSettings(this, name);
if (created != null) {
created.enableManagement(created.getConfiguration().isManagementEnabled());
created.enableStatistics(created.getConfiguration().isStatisticsEnabled());
Expand Down Expand Up @@ -192,7 +182,7 @@ public void close() {
if (isClosed()) {
return;
}
synchronized (cacheFactory) {
synchronized (this) {
if (!isClosed()) {
cacheProvider.close(uri, classLoaderReference.get());
for (Cache<?, ?> cache : caches.values()) {
Expand Down
Expand Up @@ -24,6 +24,7 @@
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -42,6 +43,7 @@
import com.github.benmanes.caffeine.jcache.expiry.JCacheExpiryPolicy;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigFactory;

/**
* Static utility methods pertaining to externalized {@link CaffeineConfiguration} entries using the
Expand All @@ -54,6 +56,7 @@ public final class TypesafeConfigurator {
static final Logger logger = Logger.getLogger(TypesafeConfigurator.class.getName());

static FactoryCreator factoryCreator = FactoryBuilder::factoryOf;
static Supplier<Config> configSource = ConfigFactory::load;

private TypesafeConfigurator() {}

Expand Down Expand Up @@ -113,6 +116,21 @@ public static void setFactoryCreator(FactoryCreator factoryCreator) {
TypesafeConfigurator.factoryCreator = requireNonNull(factoryCreator);
}

/**
* Specifies how the {@link Config} instance should be loaded. The default strategy uses
* {@link ConfigFactory#load()}.
*
* @param configSource the strategy for loading the configuration
*/
public static void setConfigSource(Supplier<Config> configSource) {
TypesafeConfigurator.configSource = requireNonNull(configSource);
}

/** Returns the strategy for loading the configuration. */
public static Supplier<Config> configSource() {
return TypesafeConfigurator.configSource;
}

/** A one-shot builder for creating a configuration instance. */
private static final class Configurator<K, V> {
final CaffeineConfiguration<K, V> configuration;
Expand Down
Expand Up @@ -26,6 +26,7 @@
import java.util.OptionalLong;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import javax.cache.Cache;
import javax.cache.Caching;
Expand All @@ -37,13 +38,22 @@

import com.github.benmanes.caffeine.jcache.copy.JavaSerializationCopier;
import com.google.common.collect.Iterables;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

/**
* @author ben.manes@gmail.com (Ben Manes)
*/
public final class TypesafeConfigurationTest {

@Test
public void configSource() {
Config config = ConfigFactory.load();
Supplier<Config> configSource = () -> config;
TypesafeConfigurator.setConfigSource(configSource);
assertThat(TypesafeConfigurator.configSource(), is(configSource));
}

@Test
public void defaults() {
CaffeineConfiguration<Integer, Integer> defaults =
Expand Down

0 comments on commit 92c8d59

Please sign in to comment.