/*
* Copyright 2002-2016 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.cache.interceptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.CacheTestUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import static org.junit.Assert.*;
import static org.springframework.cache.CacheTestUtils.*;
/**
* Provides various {@link CacheResolver} customisations scenario
*
* @author Stephane Nicoll
* @since 4.1
*/
public class CacheResolverCustomizationTests {
private CacheManager cacheManager;
private CacheManager anotherCacheManager;
private SimpleService simpleService;
@Before
public void setUp() {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
this.cacheManager = context.getBean("cacheManager", CacheManager.class);
this.anotherCacheManager = context.getBean("anotherCacheManager", CacheManager.class);
this.simpleService = context.getBean(SimpleService.class);
}
@Test
public void noCustomization() {
Cache cache = this.cacheManager.getCache("default");
Object key = new Object();
assertCacheMiss(key, cache);
Object value = this.simpleService.getSimple(key);
assertCacheHit(key, value, cache);
}
@Test
public void customCacheResolver() {
Cache cache = this.cacheManager.getCache("primary");
Object key = new Object();
assertCacheMiss(key, cache);
Object value = this.simpleService.getWithCustomCacheResolver(key);
assertCacheHit(key, value, cache);
}
@Test
public void customCacheManager() {
Cache cache = this.anotherCacheManager.getCache("default");
Object key = new Object();
assertCacheMiss(key, cache);
Object value = this.simpleService.getWithCustomCacheManager(key);
assertCacheHit(key, value, cache);
}
@Test
public void runtimeResolution() {
Cache defaultCache = this.cacheManager.getCache("default");
Cache primaryCache = this.cacheManager.getCache("primary");
Object key = new Object();
assertCacheMiss(key, defaultCache, primaryCache);
Object value = this.simpleService.getWithRuntimeCacheResolution(key, "default");
assertCacheHit(key, value, defaultCache);
assertCacheMiss(key, primaryCache);
Object key2 = new Object();
assertCacheMiss(key2, defaultCache, primaryCache);
Object value2 = this.simpleService.getWithRuntimeCacheResolution(key2, "primary");
assertCacheHit(key2, value2, primaryCache);
assertCacheMiss(key2, defaultCache);
}
@Test
public void namedResolution() {
Cache cache = this.cacheManager.getCache("secondary");
Object key = new Object();
assertCacheMiss(key, cache);
Object value = this.simpleService.getWithNamedCacheResolution(key);
assertCacheHit(key, value, cache);
}
@Test
public void noCacheResolved() {
Method method = ReflectionUtils.findMethod(SimpleService.class, "noCacheResolved", Object.class);
try {
this.simpleService.noCacheResolved(new Object());
fail("Should have failed, no cache resolved");
}
catch (IllegalStateException ex) {
assertTrue("Reference to the method must be contained in the message", ex.getMessage().contains(method.toString()));
}
}
@Test
public void unknownCacheResolver() {
try {
this.simpleService.unknownCacheResolver(new Object());
fail("Should have failed, no cache resolver with that name");
}
catch (NoSuchBeanDefinitionException ex) {
assertEquals("Wrong bean name in exception", "unknownCacheResolver", ex.getBeanName());
}
}
@Configuration
@EnableCaching
static class Config extends CachingConfigurerSupport {
@Override
@Bean
public CacheManager cacheManager() {
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
}
@Override
@Bean
public KeyGenerator keyGenerator() {
return null;
}
@Bean
public CacheManager anotherCacheManager() {
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
}
@Bean
public CacheResolver primaryCacheResolver() {
return new NamedCacheResolver(cacheManager(), "primary");
}
@Bean
public CacheResolver secondaryCacheResolver() {
return new NamedCacheResolver(cacheManager(), "primary");
}
@Bean
public CacheResolver runtimeCacheResolver() {
return new RuntimeCacheResolver(cacheManager());
}
@Bean
public CacheResolver namedCacheResolver() {
NamedCacheResolver resolver = new NamedCacheResolver();
resolver.setCacheManager(cacheManager());
resolver.setCacheNames(Collections.singleton("secondary"));
return resolver;
}
@Bean
public CacheResolver nullCacheResolver() {
return new NullCacheResolver(cacheManager());
}
@Bean
public SimpleService simpleService() {
return new SimpleService();
}
}
@CacheConfig(cacheNames = "default")
static class SimpleService {
private final AtomicLong counter = new AtomicLong();
@Cacheable
public Object getSimple(Object key) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheResolver = "primaryCacheResolver")
public Object getWithCustomCacheResolver(Object key) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheManager = "anotherCacheManager")
public Object getWithCustomCacheManager(Object key) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheResolver = "runtimeCacheResolver", key = "#p0")
public Object getWithRuntimeCacheResolution(Object key, String cacheName) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheResolver = "namedCacheResolver")
public Object getWithNamedCacheResolution(Object key) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheResolver = "nullCacheResolver") // No cache resolved for the operation
public Object noCacheResolved(Object key) {
return this.counter.getAndIncrement();
}
@Cacheable(cacheResolver = "unknownCacheResolver") // No such bean defined
public Object unknownCacheResolver(Object key) {
return this.counter.getAndIncrement();
}
}
/**
* Example of {@link CacheResolver} that resolve the caches at
* runtime (i.e. based on method invocation parameters).
* <p>Expects the second argument to hold the name of the cache to use
*/
private static class RuntimeCacheResolver extends AbstractCacheResolver {
private RuntimeCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
String cacheName = (String) context.getArgs()[1];
return Collections.singleton(cacheName);
}
}
private static class NullCacheResolver extends AbstractCacheResolver {
private NullCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
return null;
}
}
}