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-2633pull/1784/head
parent
10da3d390e
commit
151220f41d
@ -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;
|
@ -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>
|
Loading…
Reference in New Issue