Add cache manager auto-configuration

Add support for cache manager auto-configuration that is triggered when
the `EnableCaching` annotation is added to a Spring Boot application.

A new "spring.cache" set of configuration keys is also provided. The
"spring.cache.mode" allows the user to specify the cache provider that
should be auto-configured. If no explicit configuration is provided,
the environment is checked for the best suited cache implementation,
that is:

- Generic if at least one `Cache` bean is defined in the context.
- Hazelcast if either a default configuration file is present or the
  `spring.cache.config` property is set.
- JCache if one JSR-107 provider is present
- Redis if a `RedisTemplate` is defined in the context
- Guava
- Simple as a fallback option, using concurrent maps
- NoOp (that is, no cache) if the mode is set to "none"

If the provider supports it, it is possible to specify the caches
to create on startup via `spring.cache.cache-names`. If the provider
relies on a configuration file and a custom one needs to be used
`spring.cache.config` can be set to such custom resource.

If more than one JSR-107 provider is present, it is possible to force
the provider to use by setting the mode to `jcache` and specifying the
fully qualified class name of the CachingProvider to use via
`spring.cache.jcache.provider`.

See gh-2633
pull/1784/head
Stephane Nicoll 10 years ago committed by Phillip Webb
parent 10da3d390e
commit 151220f41d

@ -60,11 +60,26 @@
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>com.samskivert</groupId> <groupId>com.samskivert</groupId>
<artifactId>jmustache</artifactId> <artifactId>jmustache</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>

@ -0,0 +1,141 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.CacheConfigurationImportSelector;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheAspectSupport;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.context.annotation.Role;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the cache abstraction. Creates a
* {@link CacheManager} if necessary when caching is enabled via {@link EnableCaching}.
* <p>
* Cache store can be auto-detected or specified explicitly via configuration.
*
* @author Stephane Nicoll
* @since 1.3.0
* @see EnableCaching
*/
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean({ CacheManager.class, CacheResolver.class })
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator";
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static BeanFactoryPostProcessor cacheAutoConfigurationValidatorPostProcessor() {
return new CacheManagerValidatorPostProcessor();
}
@Bean(name = VALIDATOR_BEAN_NAME)
public CacheManagerValidator cacheAutoConfigurationValidator() {
return new CacheManagerValidator();
}
/**
* {@link BeanFactoryPostProcessor} to ensure that the {@link CacheManagerValidator}
* is triggered before {@link CacheAspectSupport} but without causing early
* instantiation.
*/
static class CacheManagerValidatorPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
for (String name : beanFactory.getBeanNamesForType(CacheAspectSupport.class)) {
BeanDefinition definition = beanFactory.getBeanDefinition(name);
definition.setDependsOn(append(definition.getDependsOn(),
VALIDATOR_BEAN_NAME));
}
}
private String[] append(String[] array, String value) {
String[] result = new String[array == null ? 1 : array.length + 1];
if (array != null) {
System.arraycopy(array, 0, result, 0, array.length);
}
result[result.length - 1] = value;
return result;
}
}
/**
* Bean used to validate that a CacheManager exists and provide a more meaningful
* exception.
*/
static class CacheManagerValidator {
@Autowired
private CacheProperties cacheProperties;
@Autowired(required = false)
private CacheManager beanFactory;
@PostConstruct
public void checkHasCacheManager() {
Assert.notNull(this.beanFactory, "No cache manager could "
+ "be auto-configured, check your configuration (caching "
+ "type is '" + this.cacheProperties.getType() + "')");
}
}
/**
* {@link ImportSelector} to add {@link CacheType} configuration classes.
*/
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = types[i].getConfigurationClass().getName();
}
return imports;
}
}
}

@ -0,0 +1,52 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
/**
* General cache condition used with all cache configuration classes.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.3.0
*/
class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.cache.");
if (!resolver.containsProperty("type")) {
return ConditionOutcome.match("Automatic cache type");
}
CacheType cacheType = CacheType
.forConfigurationClass(((AnnotationMetadata) metadata).getClassName());
String value = resolver.getProperty("type").replace("-", "_").toUpperCase();
if (value.equals(cacheType.name())) {
return ConditionOutcome.match("Cache type " + cacheType);
}
return ConditionOutcome.noMatch("Cache type " + value);
}
}

@ -0,0 +1,68 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* {@link SpringBootCondition} used to check if a cache configuration file can be found.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.3.0
*/
abstract class CacheConfigFileCondition extends SpringBootCondition {
private final String name;
private final String[] resourceLocations;
public CacheConfigFileCondition(String name, String... resourceLocations) {
this.name = name;
this.resourceLocations = resourceLocations;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.cache.");
if (resolver.containsProperty("config")) {
return ConditionOutcome.match("A spring.cache.config property is specified");
}
return getResourceOutcome(context, metadata);
}
protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
for (String location : this.resourceLocations) {
Resource resource = context.getResourceLoader().getResource(location);
if (resource != null && resource.exists()) {
return ConditionOutcome.match("Found " + this.name + " config in "
+ resource);
}
}
return ConditionOutcome.noMatch("No specific " + this.name
+ " configuration found");
}
}

@ -0,0 +1,141 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* Configuration properties for the cache abstraction.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "spring.cache")
public class CacheProperties {
/**
* Cache type, auto-detected according to the environment by default.
*/
private CacheType type;
/**
* The location of the configuration file to use to initialize the cache library.
*/
private Resource config;
/**
* Comma-separated list of cache names to create if supported by the underlying cache
* manager. Usually, this disables the ability to create additional caches on-the-fly.
*/
private final List<String> cacheNames = new ArrayList<String>();
private final JCache jcache = new JCache();
private final Guava guava = new Guava();
public CacheType getType() {
return this.type;
}
public void setType(CacheType mode) {
this.type = mode;
}
public Resource getConfig() {
return this.config;
}
public void setConfig(Resource config) {
this.config = config;
}
public List<String> getCacheNames() {
return this.cacheNames;
}
public JCache getJcache() {
return this.jcache;
}
public Guava getGuava() {
return this.guava;
}
/**
* Resolve the config location if set.
* @return the location or {@code null} if it is not set
* @throws IllegalArgumentException if the config attribute is set to a unknown
* location
*/
public Resource resolveConfigLocation() {
if (this.config != null) {
Assert.isTrue(this.config.exists(), "Cache configuration field defined by "
+ "'spring.cache.config' does not exist " + this.config);
return this.config;
}
return null;
}
/**
* JCache (JSR-107) specific cache properties.
*/
public static class JCache {
/**
* Fully qualified name of the CachingProvider implementation to use to retrieve
* the JSR-107 compliant cache manager. Only needed if more than one JSR-107
* implementation is available on the classpath.
*/
private String provider;
public String getProvider() {
return this.provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
}
/**
* Guava specific cache properties.
*/
public static class Guava {
/**
* The spec to use to create caches. Check CacheBuilderSpec for more details on
* the spec format.
*/
private String spec;
public String getSpec() {
return this.spec;
}
public void setSpec(String spec) {
this.spec = spec;
}
}
}

@ -0,0 +1,82 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
/**
* Supported cache types (defined in order of precedence).
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.3.0
*/
public enum CacheType {
/**
* Generic caching using 'Cache 'beans from the context.
*/
GENERIC(GenericCacheConfiguration.class),
/**
* Haezelcast backed caching
*/
HAZELCAST(HazelcastCacheConfiguration.class),
/**
* JCache (JSR-107) backed caching.
*/
JCACHE(JCacheCacheConfiguration.class),
/**
* Redis backed caching.
*/
REDIS(RedisCacheConfiguration.class),
/**
* Guava backed caching.
*/
GUAVA(GuavaCacheConfiguration.class),
/**
* Simple in-memory caching.
*/
SIMPLE(SimpleCacheConfiguration.class),
/**
* No caching.
*/
NONE(NoOpCacheConfiguration.class);
private final Class<?> configurationClass;
CacheType(Class<?> configurationClass) {
this.configurationClass = configurationClass;
}
Class<?> getConfigurationClass() {
return this.configurationClass;
}
static CacheType forConfigurationClass(String configurationClass) {
for (CacheType type : values()) {
if (type.getConfigurationClass().getName().equals(configurationClass)) {
return type;
}
}
throw new IllegalArgumentException("Unsupported class " + configurationClass);
}
}

@ -0,0 +1,50 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.Collection;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Generic cache configuration based on arbitrary {@link Cache} instances defined in the
* context.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GenericCacheConfiguration {
@Bean
public SimpleCacheManager cacheManager(Collection<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return cacheManager;
}
}

@ -0,0 +1,92 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheBuilderSpec;
import com.google.common.cache.CacheLoader;
/**
* Guava cache configuration.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(CacheBuilder.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GuavaCacheConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Autowired(required = false)
private CacheBuilder<Object, Object> cacheBuilder;
@Autowired(required = false)
private CacheBuilderSpec cacheBuilderSpec;
@Autowired(required = false)
private CacheLoader<Object, Object> cacheLoader;
@Bean
public GuavaCacheManager cacheManager() {
GuavaCacheManager cacheManager = createCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManager;
}
private GuavaCacheManager createCacheManager() {
GuavaCacheManager cacheManager = new GuavaCacheManager();
setCacheBuilder(cacheManager);
if (this.cacheLoader != null) {
cacheManager.setCacheLoader(this.cacheLoader);
}
return cacheManager;
}
private void setCacheBuilder(GuavaCacheManager cacheManager) {
String specification = this.cacheProperties.getGuava().getSpec();
if (StringUtils.hasText(specification)) {
cacheManager.setCacheSpecification(specification);
}
else if (this.cacheBuilderSpec != null) {
cacheManager.setCacheBuilderSpec(this.cacheBuilderSpec);
}
else if (this.cacheBuilder != null) {
cacheManager.setCacheBuilder(this.cacheBuilder);
}
}
}

@ -0,0 +1,96 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import com.hazelcast.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
/**
* Hazelcast cache configuration. Only kick in if a configuration file location is set or
* if a default configuration file exists (either placed in the default location or set
* via the {@value #CONFIG_SYSTEM_PROPERTY} system property).
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass({ HazelcastInstance.class, HazelcastCacheManager.class })
@ConditionalOnMissingBean(CacheManager.class)
@Conditional({ CacheCondition.class,
HazelcastCacheConfiguration.ConfigAvailableCondition.class })
class HazelcastCacheConfiguration {
static final String CONFIG_SYSTEM_PROPERTY = "hazelcast.config";
@Autowired
private CacheProperties cacheProperties;
@Bean
public HazelcastCacheManager cacheManager() throws IOException {
return new HazelcastCacheManager(createHazelcastInstance());
}
private HazelcastInstance createHazelcastInstance() throws IOException {
Resource location = this.cacheProperties.resolveConfigLocation();
if (location != null) {
Config cfg = new XmlConfigBuilder(location.getURL()).build();
return Hazelcast.newHazelcastInstance(cfg);
}
return Hazelcast.newHazelcastInstance();
}
/**
* Determines if the Hazelcast configuration is available. This either kick in if a
* default configuration has been found or if property referring to the file to use
* has been set.
*/
static class ConfigAvailableCondition extends CacheConfigFileCondition {
public ConfigAvailableCondition() {
super("Hazelcast", "file:./hazelcast.xml", "classpath:/hazelcast.xml");
}
@Override
protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) {
return ConditionOutcome.match("System property '"
+ CONFIG_SYSTEM_PROPERTY + "' is set.");
}
return super.getResourceOutcome(context, metadata);
}
}
}

@ -0,0 +1,136 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.Iterator;
import java.util.List;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.StringUtils;
/**
* Cache configuration for JSR-107 compliant providers.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass(Caching.class)
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
@Conditional({ CacheCondition.class,
JCacheCacheConfiguration.JCacheAvailableCondition.class })
class JCacheCacheConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Autowired(required = false)
private javax.cache.configuration.Configuration<?, ?> defaultCacheConfiguration;
@Autowired(required = false)
private List<JCacheManagerCustomizer> cacheManagerCustomizers;
@Bean
public JCacheCacheManager cacheManager() {
CacheManager cacheManager = createCacheManager(this.cacheProperties.getJcache()
.getProvider());
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!CollectionUtils.isEmpty(cacheNames)) {
for (String cacheName : cacheNames) {
cacheManager.createCache(cacheName, getDefaultCacheConfiguration());
}
}
customize(cacheManager);
return new JCacheCacheManager(cacheManager);
}
private CacheManager createCacheManager(String cachingProvider) {
if (StringUtils.hasText(cachingProvider)) {
return Caching.getCachingProvider(cachingProvider).getCacheManager();
}
return Caching.getCachingProvider().getCacheManager();
}
private javax.cache.configuration.Configuration<?, ?> getDefaultCacheConfiguration() {
if (this.defaultCacheConfiguration != null) {
return this.defaultCacheConfiguration;
}
return new MutableConfiguration<Object, Object>();
}
private void customize(CacheManager cacheManager) {
if (this.cacheManagerCustomizers != null) {
AnnotationAwareOrderComparator.sort(this.cacheManagerCustomizers);
for (JCacheManagerCustomizer customizer : this.cacheManagerCustomizers) {
customizer.customize(cacheManager);
}
}
}
/**
* Determines if JCache is available. This either kick in if a default
* {@link CachingProvider} has been found or if the property referring to the provider
* to use has been set.
*/
@Order(Ordered.LOWEST_PRECEDENCE)
static class JCacheAvailableCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.cache.jcache.");
if (resolver.containsProperty("provider")) {
return ConditionOutcome.match("JCache provider specified");
}
Iterator<CachingProvider> providers = Caching.getCachingProviders()
.iterator();
if (!providers.hasNext()) {
return ConditionOutcome.noMatch("No JSR-107 compliant providers");
}
providers.next();
if (providers.hasNext()) {
return ConditionOutcome.noMatch("Multiple default JSR-107 compliant "
+ "providers found");
}
return ConditionOutcome.match("Default JSR-107 compliant provider found.");
}
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import javax.cache.CacheManager;
import javax.cache.configuration.Configuration;
/**
* Callback interface that can be implemented by beans wishing to customize the cache
* manager before it is used, in particular to create additional caches.
*
* @author Stephane Nicoll
* @since 1.3.0
* @see CacheManager#createCache(String, Configuration)
*/
public interface JCacheManagerCustomizer {
/**
* Customize the cache manager.
* @param cacheManager the {@link CacheManager} to customize
*/
void customize(CacheManager cacheManager);
}

@ -0,0 +1,42 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* No-op cache configuration used to disable caching via configuration.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class NoOpCacheConfiguration {
@Bean
public NoOpCacheManager cacheManager() {
return new NoOpCacheManager();
}
}

@ -0,0 +1,56 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Redis cache configuration.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnBean(RedisTemplate.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Bean
public RedisCacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManager;
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Simplest cache configuration, usually used as a fallback.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManager;
}
}

@ -0,0 +1,20 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for the cache abstraction.
*/
package org.springframework.boot.autoconfigure.cache;

@ -9,6 +9,7 @@ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\

@ -0,0 +1,502 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache;
import java.util.Collection;
import java.util.Collections;
import javax.cache.configuration.CompleteConfiguration;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.guava.GuavaCache;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import com.google.common.cache.CacheBuilder;
import com.hazelcast.cache.HazelcastCachingProvider;
import com.hazelcast.spring.cache.HazelcastCacheManager;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link CacheAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class CacheAutoConfigurationTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
@After
public void tearDown() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void noEnableCaching() {
load(EmptyConfiguration.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(CacheManager.class);
}
@Test
public void cacheManagerBackOff() {
load(CustomCacheManagerConfiguration.class);
ConcurrentMapCacheManager cacheManager = validateCacheManager(ConcurrentMapCacheManager.class);
assertThat(cacheManager.getCacheNames(), contains("custom1"));
assertThat(cacheManager.getCacheNames(), hasSize(1));
}
@Test
public void cacheManagerFromSupportBackOff() {
load(CustomCacheManagerFromSupportConfiguration.class);
ConcurrentMapCacheManager cacheManager = validateCacheManager(ConcurrentMapCacheManager.class);
assertThat(cacheManager.getCacheNames(), contains("custom1"));
assertThat(cacheManager.getCacheNames(), hasSize(1));
}
@Test
public void cacheResolverBackOff() throws Exception {
load(CustomCacheResolverConfiguration.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(CacheManager.class);
}
@Test
public void notSupportedCachingMode() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("cache");
this.thrown.expectMessage("foobar");
load(DefaultCacheConfiguration.class, "spring.cache.type=foobar");
}
@Test
public void simpleCacheExplicit() {
load(DefaultCacheConfiguration.class, "spring.cache.type=simple");
ConcurrentMapCacheManager cacheManager = validateCacheManager(ConcurrentMapCacheManager.class);
assertThat(cacheManager.getCacheNames(), is(empty()));
}
@Test
public void simpleCacheExplicitWithCacheNames() {
load(DefaultCacheConfiguration.class, "spring.cache.type=simple",
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
ConcurrentMapCacheManager cacheManager = validateCacheManager(ConcurrentMapCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
@Test
public void genericCacheWithCaches() {
load(GenericCacheConfiguration.class);
SimpleCacheManager cacheManager = validateCacheManager(SimpleCacheManager.class);
assertThat(cacheManager.getCache("first"),
equalTo(this.context.getBean("firstCache")));
assertThat(cacheManager.getCache("second"),
equalTo(this.context.getBean("secondCache")));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
@Test
public void genericCacheExplicit() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("No cache manager could be auto-configured");
this.thrown.expectMessage("GENERIC");
load(DefaultCacheConfiguration.class, "spring.cache.type=generic");
}
@Test
public void genericCacheExplicitWithCaches() {
load(GenericCacheConfiguration.class, "spring.cache.type=generic");
SimpleCacheManager cacheManager = validateCacheManager(SimpleCacheManager.class);
assertThat(cacheManager.getCache("first"),
equalTo(this.context.getBean("firstCache")));
assertThat(cacheManager.getCache("second"),
equalTo(this.context.getBean("secondCache")));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
@Test
public void redisCacheExplicit() {
load(RedisCacheConfiguration.class, "spring.cache.type=redis");
RedisCacheManager cacheManager = validateCacheManager(RedisCacheManager.class);
assertThat(cacheManager.getCacheNames(), is(empty()));
}
@Test
public void redisCacheExplicitWithCaches() {
load(RedisCacheConfiguration.class, "spring.cache.type=redis",
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
RedisCacheManager cacheManager = validateCacheManager(RedisCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
@Test
public void noOpCacheExplicit() {
load(DefaultCacheConfiguration.class, "spring.cache.type=none");
NoOpCacheManager cacheManager = validateCacheManager(NoOpCacheManager.class);
assertThat(cacheManager.getCacheNames(), is(empty()));
}
@Test
public void jCacheCacheNoProviderExplicit() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("No cache manager could be auto-configured");
this.thrown.expectMessage("JCACHE");
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache");
}
@Test
public void jCacheCacheWithProvider() {
String cachingProviderFqn = MockCachingProvider.class.getName();
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + cachingProviderFqn);
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames(), is(empty()));
}
@Test
public void jCacheCacheWithCaches() {
String cachingProviderFqn = MockCachingProvider.class.getName();
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + cachingProviderFqn,
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
@Test
public void jCacheCacheWithCachesAndCustomConfig() {
String cachingProviderFqn = MockCachingProvider.class.getName();
load(JCacheCustomConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + cachingProviderFqn,
"spring.cache.cacheNames[0]=one", "spring.cache.cacheNames[1]=two");
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("one", "two"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
CompleteConfiguration<?, ?> defaultCacheConfiguration = this.context
.getBean(CompleteConfiguration.class);
verify(cacheManager.getCacheManager()).createCache("one",
defaultCacheConfiguration);
verify(cacheManager.getCacheManager()).createCache("two",
defaultCacheConfiguration);
}
@Test
public void jCacheCacheWithUnknownProvider() {
String wrongCachingProviderFqn = "org.acme.FooBar";
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage(wrongCachingProviderFqn);
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + wrongCachingProviderFqn);
}
@Test
public void hazelcastCacheExplicit() {
load(DefaultCacheConfiguration.class, "spring.cache.type=hazelcast");
HazelcastCacheManager cacheManager = validateCacheManager(HazelcastCacheManager.class);
// NOTE: the hazelcast implementation know about a cache in a lazy manner.
cacheManager.getCache("defaultCache");
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("defaultCache"));
assertThat(cacheManager.getCacheNames(), hasSize(1));
}
@Test
public void hazelcastCacheWithLocation() {
load(DefaultCacheConfiguration.class, "spring.cache.type=hazelcast",
"spring.cache.config=org/springframework/boot/autoconfigure/cache/hazelcast-specific.xml");
HazelcastCacheManager cacheManager = validateCacheManager(HazelcastCacheManager.class);
cacheManager.getCache("foobar");
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foobar"));
assertThat(cacheManager.getCacheNames(), hasSize(1));
}
@Test
public void hazelcastWithWrongLocation() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("foo/bar/unknown.xml");
load(DefaultCacheConfiguration.class, "spring.cache.type=hazelcast",
"spring.cache.config=foo/bar/unknown.xml");
System.out.println(this.context.getBean(CacheManager.class).getClass());
}
@Test
public void hazelcastAsJCacheWithCaches() {
String cachingProviderFqn = HazelcastCachingProvider.class.getName();
JCacheCacheManager cacheManager = null;
try {
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + cachingProviderFqn,
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
cacheManager = validateCacheManager(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
finally {
if (cacheManager != null) {
cacheManager.getCacheManager().close();
}
}
}
@Test
public void jCacheCacheWithCachesAndCustomizer() {
String cachingProviderFqn = HazelcastCachingProvider.class.getName();
JCacheCacheManager cacheManager = null;
try {
load(JCacheWithCustomizerConfiguration.class, "spring.cache.type=jcache",
"spring.cache.jcache.provider=" + cachingProviderFqn,
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
cacheManager = validateCacheManager(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "custom1")); // see
// customizer
assertThat(cacheManager.getCacheNames(), hasSize(2));
}
finally {
if (cacheManager != null) {
cacheManager.getCacheManager().close();
}
}
}
@Test
public void guavaCacheExplicitWithCaches() {
load(DefaultCacheConfiguration.class, "spring.cache.type=guava",
"spring.cache.cacheNames=foo");
GuavaCacheManager cacheManager = validateCacheManager(GuavaCacheManager.class);
Cache foo = cacheManager.getCache("foo");
foo.get("1");
// See next tests: no spec given so stats should be disabled
assertThat(((GuavaCache) foo).getNativeCache().stats().missCount(), equalTo(0L));
}
@Test
public void guavaCacheExplicitWithSpec() {
load(DefaultCacheConfiguration.class, "spring.cache.type=guava",
"spring.cache.guava.spec=recordStats", "spring.cache.cacheNames[0]=foo",
"spring.cache.cacheNames[1]=bar");
validateGuavaCacheWithStats();
}
@Test
public void guavaCacheExplicitWithCacheBuilder() {
load(GuavaCacheBuilderConfiguration.class, "spring.cache.type=guava",
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
validateGuavaCacheWithStats();
}
private void validateGuavaCacheWithStats() {
GuavaCacheManager cacheManager = validateCacheManager(GuavaCacheManager.class);
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
assertThat(cacheManager.getCacheNames(), hasSize(2));
Cache foo = cacheManager.getCache("foo");
foo.get("1");
assertThat(((GuavaCache) foo).getNativeCache().stats().missCount(), equalTo(1L));
}
private <T extends CacheManager> T validateCacheManager(Class<T> type) {
CacheManager cacheManager = this.context.getBean(CacheManager.class);
assertThat("Wrong cache manager type", cacheManager, is(instanceOf(type)));
return type.cast(cacheManager);
}
private void load(Class<?> config, String... environment) {
this.context = doLoad(config, environment);
}
private AnnotationConfigApplicationContext doLoad(Class<?> config,
String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.register(config);
applicationContext.register(CacheAutoConfiguration.class);
applicationContext.refresh();
return applicationContext;
}
@Configuration
static class EmptyConfiguration {
}
@Configuration
@EnableCaching
static class DefaultCacheConfiguration {
}
@Configuration
@EnableCaching
static class GenericCacheConfiguration {
@Bean
public Cache firstCache() {
return new ConcurrentMapCache("first");
}
@Bean
public Cache secondCache() {
return new ConcurrentMapCache("second");
}
}
@Configuration
@EnableCaching
static class RedisCacheConfiguration {
@Bean
public RedisTemplate<?, ?> redisTemplate() {
return mock(RedisTemplate.class);
}
}
@Configuration
@EnableCaching
static class JCacheCustomConfiguration {
@Bean
public CompleteConfiguration<?, ?> defaultCacheConfiguration() {
return mock(CompleteConfiguration.class);
}
}
@Configuration
@EnableCaching
static class JCacheWithCustomizerConfiguration {
@Bean
JCacheManagerCustomizer myCustomizer() {
return new JCacheManagerCustomizer() {
@Override
public void customize(javax.cache.CacheManager cacheManager) {
MutableConfiguration<?, ?> config = new MutableConfiguration<Object, Object>();
config.setExpiryPolicyFactory(CreatedExpiryPolicy
.factoryOf(Duration.TEN_MINUTES));
config.setStatisticsEnabled(true);
cacheManager.createCache("custom1", config);
cacheManager.destroyCache("bar");
}
};
}
}
@Configuration
@Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class })
static class CustomCacheManagerConfiguration {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("custom1");
}
}
@Configuration
@Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class })
static class CustomCacheManagerFromSupportConfiguration extends
CachingConfigurerSupport {
@Override
@Bean
// The @Bean annotation is important, see CachingConfigurerSupport Javadoc
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("custom1");
}
}
@Configuration
@EnableCaching
static class GuavaCacheBuilderConfiguration {
@Bean
CacheBuilder<Object, Object> cacheBuilder() {
return CacheBuilder.newBuilder().recordStats();
}
}
@Configuration
@Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class })
static class CustomCacheResolverConfiguration extends CachingConfigurerSupport {
@Override
@Bean
// The @Bean annotation is important, see CachingConfigurerSupport Javadoc
public CacheResolver cacheResolver() {
return new CacheResolver() {
@Override
public Collection<? extends Cache> resolveCaches(
CacheOperationInvocationContext<?> context) {
return Collections.singleton(mock(Cache.class));
}
};
}
}
}

@ -0,0 +1,117 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.cache.support;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.Configuration;
import javax.cache.configuration.OptionalFeature;
import javax.cache.spi.CachingProvider;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
/**
* A mock {@link CachingProvider} that exposes a JSR-107 cache manager for testing
* purposes.
*
* @author Stephane Nicoll
*/
public class MockCachingProvider implements CachingProvider {
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public CacheManager getCacheManager(URI uri, ClassLoader classLoader,
Properties properties) {
CacheManager cacheManager = mock(CacheManager.class);
final Map<String, Cache> caches = new HashMap<String, Cache>();
given(cacheManager.getCacheNames()).willReturn(caches.keySet());
given(cacheManager.getCache(anyString())).willAnswer(new Answer<Cache>() {
@Override
public Cache answer(InvocationOnMock invocationOnMock) throws Throwable {
String cacheName = (String) invocationOnMock.getArguments()[0];
return caches.get(cacheName);
}
});
given(cacheManager.createCache(anyString(), any(Configuration.class))).will(
new Answer<Cache>() {
@Override
public Cache answer(InvocationOnMock invocationOnMock)
throws Throwable {
String cacheName = (String) invocationOnMock.getArguments()[0];
Cache cache = mock(Cache.class);
given(cache.getName()).willReturn(cacheName);
caches.put(cacheName, cache);
return cache;
}
});
return cacheManager;
}
@Override
public ClassLoader getDefaultClassLoader() {
return null;
}
@Override
public URI getDefaultURI() {
return null;
}
@Override
public Properties getDefaultProperties() {
return new Properties();
}
@Override
public CacheManager getCacheManager(URI uri, ClassLoader classLoader) {
return getCacheManager(uri, classLoader, getDefaultProperties());
}
@Override
public CacheManager getCacheManager() {
return getCacheManager(getDefaultURI(), getDefaultClassLoader());
}
@Override
public void close() {
}
@Override
public void close(ClassLoader classLoader) {
}
@Override
public void close(URI uri, ClassLoader classLoader) {
}
@Override
public boolean isSupported(OptionalFeature optionalFeature) {
return false;
}
}

@ -0,0 +1,4 @@
#
# Test JSR 107 provider for testing purposes only.
#
org.springframework.boot.autoconfigure.cache.support.MockCachingProvider

@ -0,0 +1,8 @@
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.4.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map name="defaultCache" />
</hazelcast>

@ -0,0 +1,10 @@
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.4.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<map name="foobar">
<time-to-live-seconds>3600</time-to-live-seconds>
<max-idle-seconds>600</max-idle-seconds>
</map>
</hazelcast>

@ -69,6 +69,7 @@
<gson.version>2.3.1</gson.version> <gson.version>2.3.1</gson.version>
<h2.version>1.4.186</h2.version> <h2.version>1.4.186</h2.version>
<hamcrest.version>1.3</hamcrest.version> <hamcrest.version>1.3</hamcrest.version>
<hazelcast.version>3.4.1</hazelcast.version>
<hibernate.version>4.3.8.Final</hibernate.version> <hibernate.version>4.3.8.Final</hibernate.version>
<hibernate-entitymanager.version>${hibernate.version}</hibernate-entitymanager.version> <hibernate-entitymanager.version>${hibernate.version}</hibernate-entitymanager.version>
<hibernate-validator.version>5.1.3.Final</hibernate-validator.version> <hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
@ -503,6 +504,16 @@
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<version>${h2.version}</version> <version>${h2.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>${hazelcast.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.jayway.jsonpath</groupId> <groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId> <artifactId>json-path</artifactId>

Loading…
Cancel
Save