/* * 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.config; import java.io.IOException; import java.util.Collection; import java.util.UUID; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.context.ConfigurableApplicationContext; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; /** * Abstract cache annotation tests (containing several reusable methods). * * @author Costin Leau * @author Chris Beams * @author Phillip Webb * @author Stephane Nicoll */ public abstract class AbstractCacheAnnotationTests { protected ConfigurableApplicationContext ctx; protected CacheableService<?> cs; protected CacheableService<?> ccs; protected CacheManager cm; /** * @return a refreshed application context */ protected abstract ConfigurableApplicationContext getApplicationContext(); @Before public void setup() { this.ctx = getApplicationContext(); this.cs = ctx.getBean("service", CacheableService.class); this.ccs = ctx.getBean("classService", CacheableService.class); this.cm = ctx.getBean("cacheManager", CacheManager.class); Collection<String> cn = this.cm.getCacheNames(); assertTrue(cn.contains("testCache")); assertTrue(cn.contains("secondary")); assertTrue(cn.contains("primary")); } @After public void close() { if (this.ctx != null) { this.ctx.close(); } } public void testCacheable(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); Object r3 = service.cache(o1); assertSame(r1, r2); assertSame(r1, r3); } public void testCacheableNull(CacheableService<?> service) throws Exception { Object o1 = new Object(); assertNull(this.cm.getCache("testCache").get(o1)); Object r1 = service.cacheNull(o1); Object r2 = service.cacheNull(o1); Object r3 = service.cacheNull(o1); assertSame(r1, r2); assertSame(r1, r3); assertEquals(r3, this.cm.getCache("testCache").get(o1).get()); assertNull("Cached value should be null", r3); } public void testCacheableSync(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cacheSync(o1); Object r2 = service.cacheSync(o1); Object r3 = service.cacheSync(o1); assertSame(r1, r2); assertSame(r1, r3); } public void testCacheableSyncNull(CacheableService<?> service) throws Exception { Object o1 = new Object(); assertNull(this.cm.getCache("testCache").get(o1)); Object r1 = service.cacheSyncNull(o1); Object r2 = service.cacheSyncNull(o1); Object r3 = service.cacheSyncNull(o1); assertSame(r1, r2); assertSame(r1, r3); assertEquals(r3, this.cm.getCache("testCache").get(o1).get()); assertNull("Cached value should be null", r3); } public void testEvict(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); assertSame(r1, r2); service.invalidate(o1); Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); assertSame(r3, r4); } public void testEvictEarly(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); assertSame(r1, r2); try { service.evictEarly(o1); } catch (RuntimeException ex) { // expected } Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); assertSame(r3, r4); } public void testEvictException(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); assertSame(r1, r2); try { service.evictWithException(o1); } catch (RuntimeException ex) { // expected } // exception occurred, eviction skipped, data should still be in the cache Object r3 = service.cache(o1); assertSame(r1, r3); } public void testEvictWKey(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); assertSame(r1, r2); service.evict(o1, null); Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); assertSame(r3, r4); } public void testEvictWKeyEarly(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); assertSame(r1, r2); try { service.invalidateEarly(o1, null); } catch (Exception ex) { // expected } Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); assertSame(r3, r4); } public void testEvictAll(CacheableService<?> service) throws Exception { Object o1 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); Object o2 = new Object(); Object r10 = service.cache(o2); assertSame(r1, r2); assertNotSame(r1, r10); service.evictAll(new Object()); Cache cache = this.cm.getCache("testCache"); assertNull(cache.get(o1)); assertNull(cache.get(o2)); Object r3 = service.cache(o1); Object r4 = service.cache(o1); assertNotSame(r1, r3); assertSame(r3, r4); } public void testConditionalExpression(CacheableService<?> service) throws Exception { Object r1 = service.conditional(4); Object r2 = service.conditional(4); assertNotSame(r1, r2); Object r3 = service.conditional(3); Object r4 = service.conditional(3); assertSame(r3, r4); } public void testConditionalExpressionSync(CacheableService<?> service) throws Exception { Object r1 = service.conditionalSync(4); Object r2 = service.conditionalSync(4); assertNotSame(r1, r2); Object r3 = service.conditionalSync(3); Object r4 = service.conditionalSync(3); assertSame(r3, r4); } public void testUnlessExpression(CacheableService<?> service) throws Exception { Cache cache = this.cm.getCache("testCache"); cache.clear(); service.unless(10); service.unless(11); assertThat(cache.get(10).get(), equalTo(10L)); assertThat(cache.get(11), nullValue()); } public void testKeyExpression(CacheableService<?> service) throws Exception { Object r1 = service.key(5, 1); Object r2 = service.key(5, 2); assertSame(r1, r2); Object r3 = service.key(1, 5); Object r4 = service.key(2, 5); assertNotSame(r3, r4); } public void testVarArgsKey(CacheableService<?> service) throws Exception { Object r1 = service.varArgsKey(1, 2, 3); Object r2 = service.varArgsKey(1, 2, 3); assertSame(r1, r2); Object r3 = service.varArgsKey(1, 2, 3); Object r4 = service.varArgsKey(1, 2); assertNotSame(r3, r4); } public void testNullValue(CacheableService<?> service) throws Exception { Object key = new Object(); assertNull(service.nullValue(key)); int nr = service.nullInvocations().intValue(); assertNull(service.nullValue(key)); assertEquals(nr, service.nullInvocations().intValue()); assertNull(service.nullValue(new Object())); assertEquals(nr + 1, service.nullInvocations().intValue()); } public void testMethodName(CacheableService<?> service, String keyName) throws Exception { Object key = new Object(); Object r1 = service.name(key); assertSame(r1, service.name(key)); Cache cache = this.cm.getCache("testCache"); // assert the method name is used assertNotNull(cache.get(keyName)); } public void testRootVars(CacheableService<?> service) { Object key = new Object(); Object r1 = service.rootVars(key); assertSame(r1, service.rootVars(key)); Cache cache = this.cm.getCache("testCache"); // assert the method name is used String expectedKey = "rootVarsrootVars" + AopProxyUtils.ultimateTargetClass(service) + service; assertNotNull(cache.get(expectedKey)); } public void testCheckedThrowable(CacheableService<?> service) throws Exception { String arg = UUID.randomUUID().toString(); try { service.throwChecked(arg); fail("Excepted exception"); } catch (Exception ex) { assertEquals("Wrong exception type", IOException.class, ex.getClass()); assertEquals(arg, ex.getMessage()); } } public void testUncheckedThrowable(CacheableService<?> service) throws Exception { try { service.throwUnchecked(1L); fail("Excepted exception"); } catch (RuntimeException ex) { assertEquals("Wrong exception type", UnsupportedOperationException.class, ex.getClass()); assertEquals("1", ex.getMessage()); } } public void testCheckedThrowableSync(CacheableService<?> service) throws Exception { String arg = UUID.randomUUID().toString(); try { service.throwCheckedSync(arg); fail("Excepted exception"); } catch (Exception ex) { ex.printStackTrace(); assertEquals("Wrong exception type", IOException.class, ex.getClass()); assertEquals(arg, ex.getMessage()); } } public void testUncheckedThrowableSync(CacheableService<?> service) throws Exception { try { service.throwUncheckedSync(1L); fail("Excepted exception"); } catch (RuntimeException ex) { assertEquals("Wrong exception type", UnsupportedOperationException.class, ex.getClass()); assertEquals("1", ex.getMessage()); } } public void testNullArg(CacheableService<?> service) { Object r1 = service.cache(null); assertSame(r1, service.cache(null)); } public void testCacheUpdate(CacheableService<?> service) { Object o = new Object(); Cache cache = this.cm.getCache("testCache"); assertNull(cache.get(o)); Object r1 = service.update(o); assertSame(r1, cache.get(o).get()); o = new Object(); assertNull(cache.get(o)); Object r2 = service.update(o); assertSame(r2, cache.get(o).get()); } public void testConditionalCacheUpdate(CacheableService<?> service) { Integer one = 1; Integer three = 3; Cache cache = this.cm.getCache("testCache"); assertEquals(one, Integer.valueOf(service.conditionalUpdate(one).toString())); assertNull(cache.get(one)); assertEquals(three, Integer.valueOf(service.conditionalUpdate(three).toString())); assertEquals(three, Integer.valueOf(cache.get(three).get().toString())); } public void testMultiCache(CacheableService<?> service) { Object o1 = new Object(); Object o2 = new Object(); Cache primary = this.cm.getCache("primary"); Cache secondary = this.cm.getCache("secondary"); assertNull(primary.get(o1)); assertNull(secondary.get(o1)); Object r1 = service.multiCache(o1); assertSame(r1, primary.get(o1).get()); assertSame(r1, secondary.get(o1).get()); Object r2 = service.multiCache(o1); Object r3 = service.multiCache(o1); assertSame(r1, r2); assertSame(r1, r3); assertNull(primary.get(o2)); assertNull(secondary.get(o2)); Object r4 = service.multiCache(o2); assertSame(r4, primary.get(o2).get()); assertSame(r4, secondary.get(o2).get()); } public void testMultiEvict(CacheableService<?> service) { Object o1 = new Object(); Object o2 = o1.toString() + "A"; Object r1 = service.multiCache(o1); Object r2 = service.multiCache(o1); Cache primary = this.cm.getCache("primary"); Cache secondary = this.cm.getCache("secondary"); primary.put(o2, o2); assertSame(r1, r2); assertSame(r1, primary.get(o1).get()); assertSame(r1, secondary.get(o1).get()); service.multiEvict(o1); assertNull(primary.get(o1)); assertNull(secondary.get(o1)); assertNull(primary.get(o2)); Object r3 = service.multiCache(o1); Object r4 = service.multiCache(o1); assertNotSame(r1, r3); assertSame(r3, r4); assertSame(r3, primary.get(o1).get()); assertSame(r4, secondary.get(o1).get()); } public void testMultiPut(CacheableService<?> service) { Object o = 1; Cache primary = this.cm.getCache("primary"); Cache secondary = this.cm.getCache("secondary"); assertNull(primary.get(o)); assertNull(secondary.get(o)); Object r1 = service.multiUpdate(o); assertSame(r1, primary.get(o).get()); assertSame(r1, secondary.get(o).get()); o = 2; assertNull(primary.get(o)); assertNull(secondary.get(o)); Object r2 = service.multiUpdate(o); assertSame(r2, primary.get(o).get()); assertSame(r2, secondary.get(o).get()); } public void testPutRefersToResult(CacheableService<?> service) throws Exception { Long id = Long.MIN_VALUE; TestEntity entity = new TestEntity(); Cache primary = this.cm.getCache("primary"); assertNull(primary.get(id)); assertNull(entity.getId()); service.putRefersToResult(entity); assertSame(entity, primary.get(id).get()); } public void testMultiCacheAndEvict(CacheableService<?> service) { String methodName = "multiCacheAndEvict"; Cache primary = this.cm.getCache("primary"); Cache secondary = this.cm.getCache("secondary"); Object key = 1; secondary.put(key, key); assertNull(secondary.get(methodName)); assertSame(key, secondary.get(key).get()); Object r1 = service.multiCacheAndEvict(key); assertSame(r1, service.multiCacheAndEvict(key)); // assert the method name is used assertSame(r1, primary.get(methodName).get()); assertNull(secondary.get(methodName)); assertNull(secondary.get(key)); } public void testMultiConditionalCacheAndEvict(CacheableService<?> service) { Cache primary = this.cm.getCache("primary"); Cache secondary = this.cm.getCache("secondary"); Object key = 1; secondary.put(key, key); assertNull(primary.get(key)); assertSame(key, secondary.get(key).get()); Object r1 = service.multiConditionalCacheAndEvict(key); Object r3 = service.multiConditionalCacheAndEvict(key); assertTrue(!r1.equals(r3)); assertNull(primary.get(key)); Object key2 = 3; Object r2 = service.multiConditionalCacheAndEvict(key2); assertSame(r2, service.multiConditionalCacheAndEvict(key2)); // assert the method name is used assertSame(r2, primary.get(key2).get()); assertNull(secondary.get(key2)); } @Test public void testCacheable() throws Exception { testCacheable(this.cs); } @Test public void testCacheableNull() throws Exception { testCacheableNull(this.cs); } @Test public void testCacheableSync() throws Exception { testCacheableSync(this.cs); } @Test public void testCacheableSyncNull() throws Exception { testCacheableSyncNull(this.cs); } @Test public void testInvalidate() throws Exception { testEvict(this.cs); } @Test public void testEarlyInvalidate() throws Exception { testEvictEarly(this.cs); } @Test public void testEvictWithException() throws Exception { testEvictException(this.cs); } @Test public void testEvictAll() throws Exception { testEvictAll(this.cs); } @Test public void testInvalidateWithKey() throws Exception { testEvictWKey(this.cs); } @Test public void testEarlyInvalidateWithKey() throws Exception { testEvictWKeyEarly(this.cs); } @Test public void testConditionalExpression() throws Exception { testConditionalExpression(this.cs); } @Test public void testConditionalExpressionSync() throws Exception { testConditionalExpressionSync(this.cs); } @Test public void testUnlessExpression() throws Exception { testUnlessExpression(this.cs); } @Test public void testClassCacheUnlessExpression() throws Exception { testUnlessExpression(this.cs); } @Test public void testKeyExpression() throws Exception { testKeyExpression(this.cs); } @Test public void testVarArgsKey() throws Exception { testVarArgsKey(this.cs); } @Test public void testClassCacheCacheable() throws Exception { testCacheable(this.ccs); } @Test public void testClassCacheInvalidate() throws Exception { testEvict(this.ccs); } @Test public void testClassEarlyInvalidate() throws Exception { testEvictEarly(this.ccs); } @Test public void testClassEvictAll() throws Exception { testEvictAll(this.ccs); } @Test public void testClassEvictWithException() throws Exception { testEvictException(this.ccs); } @Test public void testClassCacheInvalidateWKey() throws Exception { testEvictWKey(this.ccs); } @Test public void testClassEarlyInvalidateWithKey() throws Exception { testEvictWKeyEarly(this.ccs); } @Test public void testNullValue() throws Exception { testNullValue(this.cs); } @Test public void testClassNullValue() throws Exception { Object key = new Object(); assertNull(this.ccs.nullValue(key)); int nr = this.ccs.nullInvocations().intValue(); assertNull(this.ccs.nullValue(key)); assertEquals(nr, this.ccs.nullInvocations().intValue()); assertNull(this.ccs.nullValue(new Object())); // the check method is also cached assertEquals(nr, this.ccs.nullInvocations().intValue()); assertEquals(nr + 1, AnnotatedClassCacheableService.nullInvocations.intValue()); } @Test public void testMethodName() throws Exception { testMethodName(this.cs, "name"); } @Test public void testClassMethodName() throws Exception { testMethodName(this.ccs, "nametestCache"); } @Test public void testRootVars() throws Exception { testRootVars(this.cs); } @Test public void testClassRootVars() throws Exception { testRootVars(this.ccs); } @Test public void testCustomKeyGenerator() { Object param = new Object(); Object r1 = this.cs.customKeyGenerator(param); assertSame(r1, this.cs.customKeyGenerator(param)); Cache cache = this.cm.getCache("testCache"); // Checks that the custom keyGenerator was used Object expectedKey = SomeCustomKeyGenerator.generateKey("customKeyGenerator", param); assertNotNull(cache.get(expectedKey)); } @Test public void testUnknownCustomKeyGenerator() { try { Object param = new Object(); this.cs.unknownCustomKeyGenerator(param); fail("should have failed with NoSuchBeanDefinitionException"); } catch (NoSuchBeanDefinitionException ex) { // expected } } @Test public void testCustomCacheManager() { CacheManager customCm = this.ctx.getBean("customCacheManager", CacheManager.class); Object key = new Object(); Object r1 = this.cs.customCacheManager(key); assertSame(r1, this.cs.customCacheManager(key)); Cache cache = customCm.getCache("testCache"); assertNotNull(cache.get(key)); } @Test public void testUnknownCustomCacheManager() { try { Object param = new Object(); this.cs.unknownCustomCacheManager(param); fail("should have failed with NoSuchBeanDefinitionException"); } catch (NoSuchBeanDefinitionException ex) { // expected } } @Test public void testNullArg() throws Exception { testNullArg(this.cs); } @Test public void testClassNullArg() throws Exception { testNullArg(this.ccs); } @Test public void testCheckedException() throws Exception { testCheckedThrowable(this.cs); } @Test public void testClassCheckedException() throws Exception { testCheckedThrowable(this.ccs); } @Test public void testCheckedExceptionSync() throws Exception { testCheckedThrowableSync(this.cs); } @Test public void testClassCheckedExceptionSync() throws Exception { testCheckedThrowableSync(this.ccs); } @Test public void testUncheckedException() throws Exception { testUncheckedThrowable(this.cs); } @Test public void testClassUncheckedException() throws Exception { testUncheckedThrowable(this.ccs); } @Test public void testUncheckedExceptionSync() throws Exception { testUncheckedThrowableSync(this.cs); } @Test public void testClassUncheckedExceptionSync() throws Exception { testUncheckedThrowableSync(this.ccs); } @Test public void testUpdate() { testCacheUpdate(this.cs); } @Test public void testClassUpdate() { testCacheUpdate(this.ccs); } @Test public void testConditionalUpdate() { testConditionalCacheUpdate(this.cs); } @Test public void testClassConditionalUpdate() { testConditionalCacheUpdate(this.ccs); } @Test public void testMultiCache() { testMultiCache(this.cs); } @Test public void testClassMultiCache() { testMultiCache(this.ccs); } @Test public void testMultiEvict() { testMultiEvict(this.cs); } @Test public void testClassMultiEvict() { testMultiEvict(this.ccs); } @Test public void testMultiPut() { testMultiPut(this.cs); } @Test public void testClassMultiPut() { testMultiPut(this.ccs); } @Test public void testPutRefersToResult() throws Exception { testPutRefersToResult(this.cs); } @Test public void testClassPutRefersToResult() throws Exception { testPutRefersToResult(this.ccs); } @Test public void testMultiCacheAndEvict() { testMultiCacheAndEvict(this.cs); } @Test public void testClassMultiCacheAndEvict() { testMultiCacheAndEvict(this.ccs); } @Test public void testMultiConditionalCacheAndEvict() { testMultiConditionalCacheAndEvict(this.cs); } @Test public void testClassMultiConditionalCacheAndEvict() { testMultiConditionalCacheAndEvict(this.ccs); } }