/* * Copyright 2002-2014 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.jcache.interceptor; import java.lang.reflect.Method; import java.util.Comparator; import javax.cache.annotation.CacheDefaults; import javax.cache.annotation.CacheKeyGenerator; import javax.cache.annotation.CacheRemove; import javax.cache.annotation.CacheRemoveAll; import javax.cache.annotation.CacheResult; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.jcache.AbstractJCacheTests; import org.springframework.cache.jcache.support.TestableCacheKeyGenerator; import org.springframework.cache.jcache.support.TestableCacheResolver; import org.springframework.cache.jcache.support.TestableCacheResolverFactory; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * @author Stephane Nicoll */ public class AnnotationCacheOperationSourceTests extends AbstractJCacheTests { private final DefaultJCacheOperationSource source = new DefaultJCacheOperationSource(); private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); @Before public void setUp() { source.setCacheResolver(defaultCacheResolver); source.setExceptionCacheResolver(defaultExceptionCacheResolver); source.setKeyGenerator(defaultKeyGenerator); source.setBeanFactory(beanFactory); source.afterPropertiesSet(); } @Test public void cache() { CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class); assertDefaults(op); assertNull("Exception caching not enabled so resolver should not be set", op.getExceptionCacheResolver()); } @Test public void cacheWithException() { CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class, boolean.class); assertDefaults(op); assertEquals(defaultExceptionCacheResolver, op.getExceptionCacheResolver()); assertEquals("exception", op.getExceptionCacheName()); } @Test public void put() { CachePutOperation op = getDefaultCacheOperation(CachePutOperation.class, String.class, Object.class); assertDefaults(op); } @Test public void remove() { CacheRemoveOperation op = getDefaultCacheOperation(CacheRemoveOperation.class, String.class); assertDefaults(op); } @Test public void removeAll() { CacheRemoveAllOperation op = getDefaultCacheOperation(CacheRemoveAllOperation.class); assertEquals(defaultCacheResolver, op.getCacheResolver()); } @Test public void noAnnotation() { assertNull(getCacheOperation(AnnotatedJCacheableService.class, name.getMethodName())); } @Test public void multiAnnotations() { thrown.expect(IllegalStateException.class); getCacheOperation(InvalidCases.class, name.getMethodName()); } @Test public void defaultCacheNameWithCandidate() { Method method = ReflectionUtils.findMethod(Object.class, "toString"); assertEquals("foo", source.determineCacheName(method, null, "foo")); } @Test public void defaultCacheNameWithDefaults() { Method method = ReflectionUtils.findMethod(Object.class, "toString"); CacheDefaults mock = mock(CacheDefaults.class); given(mock.cacheName()).willReturn(""); assertEquals("java.lang.Object.toString()", source.determineCacheName(method, mock, "")); } @Test public void defaultCacheNameNoDefaults() { Method method = ReflectionUtils.findMethod(Object.class, "toString"); assertEquals("java.lang.Object.toString()", source.determineCacheName(method, null, "")); } @Test public void defaultCacheNameWithParameters() { Method method = ReflectionUtils.findMethod(Comparator.class, "compare", Object.class, Object.class); assertEquals("java.util.Comparator.compare(java.lang.Object,java.lang.Object)", source.determineCacheName(method, null, "")); } @Test public void customCacheResolver() { CacheResultOperation operation = getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class); assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class); assertJCacheResolver(operation.getExceptionCacheResolver(), null); assertEquals(KeyGeneratorAdapter.class, operation.getKeyGenerator().getClass()); assertEquals(defaultKeyGenerator, ((KeyGeneratorAdapter) operation.getKeyGenerator()).getTarget()); } @Test public void customKeyGenerator() { CacheResultOperation operation = getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class); assertEquals(defaultCacheResolver, operation.getCacheResolver()); assertNull(operation.getExceptionCacheResolver()); assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class); } @Test public void customKeyGeneratorSpringBean() { TestableCacheKeyGenerator bean = new TestableCacheKeyGenerator(); beanFactory.registerSingleton("fooBar", bean); CacheResultOperation operation = getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class); assertEquals(defaultCacheResolver, operation.getCacheResolver()); assertNull(operation.getExceptionCacheResolver()); KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) operation.getKeyGenerator(); assertSame(bean, adapter.getTarget()); // take bean from context } @Test public void customKeyGeneratorAndCacheResolver() { CacheResultOperation operation = getCacheOperation(CacheResultOperation.class, CustomServiceWithDefaults.class, name.getMethodName(), Long.class); assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class); assertJCacheResolver(operation.getExceptionCacheResolver(), null); assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class); } @Test public void customKeyGeneratorAndCacheResolverWithExceptionName() { CacheResultOperation operation = getCacheOperation(CacheResultOperation.class, CustomServiceWithDefaults.class, name.getMethodName(), Long.class); assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class); assertJCacheResolver(operation.getExceptionCacheResolver(), TestableCacheResolver.class); assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class); } private void assertDefaults(AbstractJCacheKeyOperation<?> operation) { assertEquals(defaultCacheResolver, operation.getCacheResolver()); assertEquals(KeyGeneratorAdapter.class, operation.getKeyGenerator().getClass()); assertEquals(defaultKeyGenerator, ((KeyGeneratorAdapter) operation.getKeyGenerator()).getTarget()); } protected <T extends JCacheOperation<?>> T getDefaultCacheOperation(Class<T> operationType, Class<?>... parameterTypes) { return getCacheOperation(operationType, AnnotatedJCacheableService.class, name.getMethodName(), parameterTypes); } protected <T extends JCacheOperation<?>> T getCacheOperation( Class<T> operationType, Class<?> targetType, String methodName, Class<?>... parameterTypes) { JCacheOperation<?> result = getCacheOperation(targetType, methodName, parameterTypes); assertNotNull(result); assertEquals(operationType, result.getClass()); return operationType.cast(result); } private JCacheOperation<?> getCacheOperation(Class<?> targetType, String methodName, Class<?>... parameterTypes) { Method method = ReflectionUtils.findMethod(targetType, methodName, parameterTypes); Assert.notNull(method, "requested method '" + methodName + "'does not exist"); return source.getCacheOperation(method, targetType); } private void assertJCacheResolver(CacheResolver actual, Class<? extends javax.cache.annotation.CacheResolver> expectedTargetType) { if (expectedTargetType == null) { assertNull(actual); } else { assertEquals("Wrong cache resolver implementation", CacheResolverAdapter.class, actual.getClass()); CacheResolverAdapter adapter = (CacheResolverAdapter) actual; assertEquals("Wrong target JCache implementation", expectedTargetType, adapter.getTarget().getClass()); } } private void assertCacheKeyGenerator(KeyGenerator actual, Class<? extends CacheKeyGenerator> expectedTargetType) { assertEquals("Wrong cache resolver implementation", KeyGeneratorAdapter.class, actual.getClass()); KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) actual; assertEquals("Wrong target CacheKeyGenerator implementation", expectedTargetType, adapter.getTarget().getClass()); } static class CustomService { @CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class) public Object customKeyGenerator(Long id) { return null; } @CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class) public Object customKeyGeneratorSpringBean(Long id) { return null; } @CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class) public Object customCacheResolver(Long id) { return null; } } @CacheDefaults(cacheResolverFactory = TestableCacheResolverFactory.class, cacheKeyGenerator = TestableCacheKeyGenerator.class) static class CustomServiceWithDefaults { @CacheResult public Object customKeyGeneratorAndCacheResolver(Long id) { return null; } @CacheResult(exceptionCacheName = "exception") public Object customKeyGeneratorAndCacheResolverWithExceptionName(Long id) { return null; } } static class InvalidCases { @CacheRemove @CacheRemoveAll public void multiAnnotations() { } } }