View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.cache.jcache.interceptor;
18  
19  import java.lang.reflect.Method;
20  import java.util.Comparator;
21  import javax.cache.annotation.CacheDefaults;
22  import javax.cache.annotation.CacheKeyGenerator;
23  import javax.cache.annotation.CacheRemove;
24  import javax.cache.annotation.CacheRemoveAll;
25  import javax.cache.annotation.CacheResult;
26  
27  import org.junit.Before;
28  import org.junit.Test;
29  
30  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
31  import org.springframework.cache.interceptor.CacheResolver;
32  import org.springframework.cache.interceptor.KeyGenerator;
33  import org.springframework.cache.jcache.AbstractJCacheTests;
34  import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
35  import org.springframework.cache.jcache.support.TestableCacheResolver;
36  import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
37  import org.springframework.util.Assert;
38  import org.springframework.util.ReflectionUtils;
39  
40  import static org.junit.Assert.*;
41  import static org.mockito.BDDMockito.*;
42  
43  /**
44   * @author Stephane Nicoll
45   */
46  public class AnnotationCacheOperationSourceTests extends AbstractJCacheTests {
47  
48  	private final DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
49  
50  	private final DefaultListableBeanFactory beanFactory =  new DefaultListableBeanFactory();
51  
52  
53  	@Before
54  	public void setUp() {
55  		source.setCacheResolver(defaultCacheResolver);
56  		source.setExceptionCacheResolver(defaultExceptionCacheResolver);
57  		source.setKeyGenerator(defaultKeyGenerator);
58  		source.setBeanFactory(beanFactory);
59  		source.afterPropertiesSet();
60  	}
61  
62  
63  	@Test
64  	public void cache() {
65  		CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class);
66  		assertDefaults(op);
67  		assertNull("Exception caching not enabled so resolver should not be set", op.getExceptionCacheResolver());
68  	}
69  
70  	@Test
71  	public void cacheWithException() {
72  		CacheResultOperation op = getDefaultCacheOperation(CacheResultOperation.class, String.class, boolean.class);
73  		assertDefaults(op);
74  		assertEquals(defaultExceptionCacheResolver, op.getExceptionCacheResolver());
75  		assertEquals("exception", op.getExceptionCacheName());
76  	}
77  
78  	@Test
79  	public void put() {
80  		CachePutOperation op = getDefaultCacheOperation(CachePutOperation.class, String.class, Object.class);
81  		assertDefaults(op);
82  	}
83  
84  	@Test
85  	public void remove() {
86  		CacheRemoveOperation op = getDefaultCacheOperation(CacheRemoveOperation.class, String.class);
87  		assertDefaults(op);
88  	}
89  
90  	@Test
91  	public void removeAll() {
92  		CacheRemoveAllOperation op = getDefaultCacheOperation(CacheRemoveAllOperation.class);
93  		assertEquals(defaultCacheResolver, op.getCacheResolver());
94  	}
95  
96  	@Test
97  	public void noAnnotation() {
98  		assertNull(getCacheOperation(AnnotatedJCacheableService.class, name.getMethodName()));
99  	}
100 
101 	@Test
102 	public void multiAnnotations() {
103 		thrown.expect(IllegalStateException.class);
104 		getCacheOperation(InvalidCases.class, name.getMethodName());
105 	}
106 
107 	@Test
108 	public void defaultCacheNameWithCandidate() {
109 		Method method = ReflectionUtils.findMethod(Object.class, "toString");
110 		assertEquals("foo", source.determineCacheName(method, null, "foo"));
111 	}
112 
113 	@Test
114 	public void defaultCacheNameWithDefaults() {
115 		Method method = ReflectionUtils.findMethod(Object.class, "toString");
116 		CacheDefaults mock = mock(CacheDefaults.class);
117 		given(mock.cacheName()).willReturn("");
118 		assertEquals("java.lang.Object.toString()", source.determineCacheName(method, mock, ""));
119 	}
120 
121 	@Test
122 	public void defaultCacheNameNoDefaults() {
123 		Method method = ReflectionUtils.findMethod(Object.class, "toString");
124 		assertEquals("java.lang.Object.toString()", source.determineCacheName(method, null, ""));
125 	}
126 
127 	@Test
128 	public void defaultCacheNameWithParameters() {
129 		Method method = ReflectionUtils.findMethod(Comparator.class, "compare", Object.class, Object.class);
130 		assertEquals("java.util.Comparator.compare(java.lang.Object,java.lang.Object)",
131 				source.determineCacheName(method, null, ""));
132 	}
133 
134 	@Test
135 	public void customCacheResolver() {
136 		CacheResultOperation operation =
137 				getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
138 		assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
139 		assertJCacheResolver(operation.getExceptionCacheResolver(), null);
140 		assertEquals(KeyGeneratorAdapter.class, operation.getKeyGenerator().getClass());
141 		assertEquals(defaultKeyGenerator, ((KeyGeneratorAdapter) operation.getKeyGenerator()).getTarget());
142 	}
143 
144 	@Test
145 	public void customKeyGenerator() {
146 		CacheResultOperation operation =
147 				getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
148 		assertEquals(defaultCacheResolver, operation.getCacheResolver());
149 		assertNull(operation.getExceptionCacheResolver());
150 		assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
151 	}
152 
153 	@Test
154 	public void customKeyGeneratorSpringBean() {
155 		TestableCacheKeyGenerator bean = new TestableCacheKeyGenerator();
156 		beanFactory.registerSingleton("fooBar", bean);
157 		CacheResultOperation operation =
158 				getCacheOperation(CacheResultOperation.class, CustomService.class, name.getMethodName(), Long.class);
159 		assertEquals(defaultCacheResolver, operation.getCacheResolver());
160 		assertNull(operation.getExceptionCacheResolver());
161 		KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) operation.getKeyGenerator();
162 		assertSame(bean, adapter.getTarget()); // take bean from context
163 	}
164 
165 	@Test
166 	public void customKeyGeneratorAndCacheResolver() {
167 		CacheResultOperation operation = getCacheOperation(CacheResultOperation.class,
168 				CustomServiceWithDefaults.class, name.getMethodName(), Long.class);
169 		assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
170 		assertJCacheResolver(operation.getExceptionCacheResolver(), null);
171 		assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
172 	}
173 
174 	@Test
175 	public void customKeyGeneratorAndCacheResolverWithExceptionName() {
176 		CacheResultOperation operation = getCacheOperation(CacheResultOperation.class,
177 				CustomServiceWithDefaults.class, name.getMethodName(), Long.class);
178 		assertJCacheResolver(operation.getCacheResolver(), TestableCacheResolver.class);
179 		assertJCacheResolver(operation.getExceptionCacheResolver(), TestableCacheResolver.class);
180 		assertCacheKeyGenerator(operation.getKeyGenerator(), TestableCacheKeyGenerator.class);
181 	}
182 
183 	private void assertDefaults(AbstractJCacheKeyOperation<?> operation) {
184 		assertEquals(defaultCacheResolver, operation.getCacheResolver());
185 		assertEquals(KeyGeneratorAdapter.class, operation.getKeyGenerator().getClass());
186 		assertEquals(defaultKeyGenerator, ((KeyGeneratorAdapter) operation.getKeyGenerator()).getTarget());
187 	}
188 
189 	protected <T extends JCacheOperation<?>> T getDefaultCacheOperation(Class<T> operationType, Class<?>... parameterTypes) {
190 		return getCacheOperation(operationType, AnnotatedJCacheableService.class, name.getMethodName(), parameterTypes);
191 	}
192 
193 	protected <T extends JCacheOperation<?>> T getCacheOperation(
194 			Class<T> operationType, Class<?> targetType, String methodName, Class<?>... parameterTypes) {
195 
196 		JCacheOperation<?> result = getCacheOperation(targetType, methodName, parameterTypes);
197 		assertNotNull(result);
198 		assertEquals(operationType, result.getClass());
199 		return operationType.cast(result);
200 	}
201 
202 	private JCacheOperation<?> getCacheOperation(Class<?> targetType, String methodName, Class<?>... parameterTypes) {
203 		Method method = ReflectionUtils.findMethod(targetType, methodName, parameterTypes);
204 		Assert.notNull(method, "requested method '" + methodName + "'does not exist");
205 		return source.getCacheOperation(method, targetType);
206 	}
207 
208 	private void assertJCacheResolver(CacheResolver actual,
209 			Class<? extends javax.cache.annotation.CacheResolver> expectedTargetType) {
210 
211 		if (expectedTargetType == null) {
212 			assertNull(actual);
213 		}
214 		else {
215 			assertEquals("Wrong cache resolver implementation", CacheResolverAdapter.class, actual.getClass());
216 			CacheResolverAdapter adapter = (CacheResolverAdapter) actual;
217 			assertEquals("Wrong target JCache implementation", expectedTargetType, adapter.getTarget().getClass());
218 		}
219 	}
220 
221 	private void assertCacheKeyGenerator(KeyGenerator actual,
222 			Class<? extends CacheKeyGenerator> expectedTargetType) {
223 		assertEquals("Wrong cache resolver implementation", KeyGeneratorAdapter.class, actual.getClass());
224 		KeyGeneratorAdapter adapter = (KeyGeneratorAdapter) actual;
225 		assertEquals("Wrong target CacheKeyGenerator implementation", expectedTargetType, adapter.getTarget().getClass());
226 	}
227 
228 
229 	static class CustomService {
230 
231 		@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
232 		public Object customKeyGenerator(Long id) {
233 			return null;
234 		}
235 
236 		@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
237 		public Object customKeyGeneratorSpringBean(Long id) {
238 			return null;
239 		}
240 
241 		@CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class)
242 		public Object customCacheResolver(Long id) {
243 			return null;
244 		}
245 	}
246 
247 
248 	@CacheDefaults(cacheResolverFactory = TestableCacheResolverFactory.class,
249 			cacheKeyGenerator = TestableCacheKeyGenerator.class)
250 	static class CustomServiceWithDefaults {
251 
252 		@CacheResult
253 		public Object customKeyGeneratorAndCacheResolver(Long id) {
254 			return null;
255 		}
256 
257 		@CacheResult(exceptionCacheName = "exception")
258 		public Object customKeyGeneratorAndCacheResolverWithExceptionName(Long id) {
259 			return null;
260 		}
261 	}
262 
263 
264 	static class InvalidCases {
265 
266 		@CacheRemove
267 		@CacheRemoveAll
268 		public void multiAnnotations() {
269 		}
270 	}
271 
272 }