diff --git a/org.springframework.aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj b/org.springframework.aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj index 826ab6bdc7..ffea5ff1c5 100644 --- a/org.springframework.aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj +++ b/org.springframework.aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj @@ -18,7 +18,9 @@ package org.springframework.cache.aspectj; import org.springframework.cache.annotation.AnnotationCacheOperationSource; import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; /** * Concrete AspectJ cache aspect using Spring's @{@link Cacheable} annotation. @@ -59,6 +61,20 @@ public aspect AnnotationCacheAspect extends AbstractCacheAspect { private pointcut executionOfAnyPublicMethodInAtCacheEvictType() : execution(public * ((@CacheEvict *)+).*(..)) && @this(CacheEvict); + /** + * Matches the execution of any public method in a type with the @{@link CachePut} + * annotation, or any subtype of a type with the {@code CachePut} annotation. + */ + private pointcut executionOfAnyPublicMethodInAtCachePutType() : + execution(public * ((@CachePut *)+).*(..)) && @this(CachePut); + + /** + * Matches the execution of any public method in a type with the @{@link Caching} + * annotation, or any subtype of a type with the {@code Caching} annotation. + */ + private pointcut executionOfAnyPublicMethodInAtCachingType() : + execution(public * ((@Caching *)+).*(..)) && @this(Caching); + /** * Matches the execution of any method with the @{@link Cacheable} annotation. */ @@ -71,6 +87,18 @@ public aspect AnnotationCacheAspect extends AbstractCacheAspect { private pointcut executionOfCacheEvictMethod() : execution(* *(..)) && @annotation(CacheEvict); + /** + * Matches the execution of any method with the @{@link CachePut} annotation. + */ + private pointcut executionOfCachePutMethod() : + execution(* *(..)) && @annotation(CachePut); + + /** + * Matches the execution of any method with the @{@link Caching} annotation. + */ + private pointcut executionOfCachingMethod() : + execution(* *(..)) && @annotation(Caching); + /** * Definition of pointcut from super aspect - matched join points will have Spring * cache management applied. @@ -78,8 +106,11 @@ public aspect AnnotationCacheAspect extends AbstractCacheAspect { protected pointcut cacheMethodExecution(Object cachedObject) : (executionOfAnyPublicMethodInAtCacheableType() || executionOfAnyPublicMethodInAtCacheEvictType() + || executionOfAnyPublicMethodInAtCachePutType() + || executionOfAnyPublicMethodInAtCachingType() || executionOfCacheableMethod() - || executionOfCacheEvictMethod()) + || executionOfCacheEvictMethod() + || executionOfCachePutMethod() + || executionOfCachingMethod()) && this(cachedObject); - -} +} \ No newline at end of file diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AbstractAnnotationTest.java b/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AbstractAnnotationTest.java index 8650d5b116..e30127e516 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AbstractAnnotationTest.java +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AbstractAnnotationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 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. @@ -18,38 +18,48 @@ package org.springframework.cache.aspectj; import static org.junit.Assert.*; +import java.util.Collection; import java.util.UUID; import org.junit.Before; import org.junit.Test; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.config.AnnotatedClassCacheableService; import org.springframework.cache.config.CacheableService; import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Abstract annotation test (containing several reusable methods). + * * @author Costin Leau + * @author Chris Beams */ public abstract class AbstractAnnotationTest { protected ApplicationContext ctx; - protected CacheableService cs; + protected CacheableService cs; - protected CacheableService ccs; + protected CacheableService ccs; - protected abstract String getConfig(); + protected CacheManager cm; + + /** @return a refreshed application context */ + protected abstract ApplicationContext getApplicationContext(); @Before public void setup() { - ctx = new ClassPathXmlApplicationContext(getConfig()); + ctx = getApplicationContext(); cs = ctx.getBean("service", CacheableService.class); ccs = ctx.getBean("classService", CacheableService.class); + cm = ctx.getBean(CacheManager.class); + Collection cn = cm.getCacheNames(); + assertTrue(cn.contains("default")); } - public void testCacheable(CacheableService service) throws Exception { + public void testCacheable(CacheableService service) throws Exception { Object o1 = new Object(); - Object o2 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); @@ -59,9 +69,8 @@ public abstract class AbstractAnnotationTest { assertSame(r1, r3); } - public void testInvalidate(CacheableService service) throws Exception { + public void testEvict(CacheableService service) throws Exception { Object o1 = new Object(); - Object o2 = new Object(); Object r1 = service.cache(o1); Object r2 = service.cache(o1); @@ -74,7 +83,98 @@ public abstract class AbstractAnnotationTest { assertSame(r3, r4); } - public void testConditionalExpression(CacheableService service) throws Exception { + 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 = cm.getCache("default"); + 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); @@ -86,7 +186,7 @@ public abstract class AbstractAnnotationTest { assertSame(r3, r4); } - public void testKeyExpression(CacheableService service) throws Exception { + public void testKeyExpression(CacheableService service) throws Exception { Object r1 = service.key(5, 1); Object r2 = service.key(5, 2); @@ -98,7 +198,26 @@ public abstract class AbstractAnnotationTest { assertNotSame(r3, r4); } - public void testCheckedThrowable(CacheableService service) throws Exception { + 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 = cm.getCache("default"); + // assert the method name is used + assertNotNull(cache.get(keyName)); + } + + public void testCheckedThrowable(CacheableService service) throws Exception { String arg = UUID.randomUUID().toString(); try { service.throwChecked(arg); @@ -108,16 +227,165 @@ public abstract class AbstractAnnotationTest { } } - public void testUncheckedThrowable(CacheableService service) throws Exception { + public void testUncheckedThrowable(CacheableService service) throws Exception { try { service.throwUnchecked(Long.valueOf(1)); fail("Excepted exception"); } catch (RuntimeException ex) { - assertTrue(ex instanceof UnsupportedOperationException); + assertTrue("Excepted different exception type and got " + ex.getClass(), + ex instanceof UnsupportedOperationException); // expected } } + 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 = cm.getCache("default"); + 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 = Integer.valueOf(1); + Integer three = Integer.valueOf(3); + + Cache cache = cm.getCache("default"); + 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 = cm.getCache("primary"); + Cache secondary = 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 r1 = service.multiCache(o1); + Object r2 = service.multiCache(o1); + + Cache primary = cm.getCache("primary"); + Cache secondary = cm.getCache("secondary"); + + 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)); + + 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 = Integer.valueOf(1); + + Cache primary = cm.getCache("primary"); + Cache secondary = 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 = Integer.valueOf(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 testMultiCacheAndEvict(CacheableService service) { + String methodName = "multiCacheAndEvict"; + + Cache primary = cm.getCache("primary"); + Cache secondary = cm.getCache("secondary"); + Object key = Integer.valueOf(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 = cm.getCache("primary"); + Cache secondary = cm.getCache("secondary"); + Object key = Integer.valueOf(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 = Integer.valueOf(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(cs); @@ -125,7 +393,32 @@ public abstract class AbstractAnnotationTest { @Test public void testInvalidate() throws Exception { - testInvalidate(cs); + testEvict(cs); + } + + @Test + public void testEarlyInvalidate() throws Exception { + testEvictEarly(cs); + } + + @Test + public void testEvictWithException() throws Exception { + testEvictException(cs); + } + + @Test + public void testEvictAll() throws Exception { + testEvictAll(cs); + } + + @Test + public void testInvalidateWithKey() throws Exception { + testEvictWKey(cs); + } + + @Test + public void testEarlyInvalidateWithKey() throws Exception { + testEvictWKeyEarly(cs); } @Test @@ -145,7 +438,70 @@ public abstract class AbstractAnnotationTest { @Test public void testClassCacheInvalidate() throws Exception { - testInvalidate(ccs); + testEvict(ccs); + } + + @Test + public void testClassEarlyInvalidate() throws Exception { + testEvictEarly(ccs); + } + + @Test + public void testClassEvictAll() throws Exception { + testEvictAll(ccs); + } + + @Test + public void testClassEvictWithException() throws Exception { + testEvictException(ccs); + } + + @Test + public void testClassCacheInvalidateWKey() throws Exception { + testEvictWKey(ccs); + } + + @Test + public void testClassEarlyInvalidateWithKey() throws Exception { + testEvictWKeyEarly(ccs); + } + + @Test + public void testNullValue() throws Exception { + testNullValue(cs); + } + + @Test + public void testClassNullValue() throws Exception { + Object key = new Object(); + assertNull(ccs.nullValue(key)); + int nr = ccs.nullInvocations().intValue(); + assertNull(ccs.nullValue(key)); + assertEquals(nr, ccs.nullInvocations().intValue()); + assertNull(ccs.nullValue(new Object())); + // the check method is also cached + assertEquals(nr, ccs.nullInvocations().intValue()); + assertEquals(nr + 1, AnnotatedClassCacheableService.nullInvocations.intValue()); + } + + @Test + public void testMethodName() throws Exception { + testMethodName(cs, "name"); + } + + @Test + public void testClassMethodName() throws Exception { + testMethodName(ccs, "namedefault"); + } + + @Test + public void testNullArg() throws Exception { + testNullArg(cs); + } + + @Test + public void testClassNullArg() throws Exception { + testNullArg(ccs); } @Test @@ -168,4 +524,73 @@ public abstract class AbstractAnnotationTest { testUncheckedThrowable(ccs); } + @Test + public void testUpdate() { + testCacheUpdate(cs); + } + + @Test + public void testClassUpdate() { + testCacheUpdate(ccs); + } + + @Test + public void testConditionalUpdate() { + testConditionalCacheUpdate(cs); + } + + @Test + public void testClassConditionalUpdate() { + testConditionalCacheUpdate(ccs); + } + + @Test + public void testMultiCache() { + testMultiCache(cs); + } + + @Test + public void testClassMultiCache() { + testMultiCache(ccs); + } + + @Test + public void testMultiEvict() { + testMultiEvict(cs); + } + + @Test + public void testClassMultiEvict() { + testMultiEvict(ccs); + } + + @Test + public void testMultiPut() { + testMultiPut(cs); + } + + @Test + public void testClassMultiPut() { + testMultiPut(ccs); + } + + @Test + public void testMultiCacheAndEvict() { + testMultiCacheAndEvict(cs); + } + + @Test + public void testClassMultiCacheAndEvict() { + testMultiCacheAndEvict(ccs); + } + + @Test + public void testMultiConditionalCacheAndEvict() { + testMultiConditionalCacheAndEvict(cs); + } + + @Test + public void testClassMultiConditionalCacheAndEvict() { + testMultiConditionalCacheAndEvict(ccs); + } } \ No newline at end of file diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AspectJAnnotationTest.java b/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AspectJAnnotationTest.java new file mode 100644 index 0000000000..cd46258317 --- /dev/null +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/aspectj/AspectJAnnotationTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2010 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.aspectj; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.GenericXmlApplicationContext; + + +/** + * @author Costin Leau + */ +public class AspectJAnnotationTest extends AbstractAnnotationTest { + + + @Override + protected ApplicationContext getApplicationContext() { + return new GenericXmlApplicationContext("/org/springframework/cache/config/annotation-cache-aspectj.xml"); + } +} diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/org.springframework.aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 96f2410bd2..2db724f6f1 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 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. @@ -19,15 +19,18 @@ package org.springframework.cache.config; import java.util.concurrent.atomic.AtomicLong; import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; /** * @author Costin Leau */ @Cacheable("default") -public class AnnotatedClassCacheableService implements CacheableService { +public class AnnotatedClassCacheableService implements CacheableService { - private AtomicLong counter = new AtomicLong(); + private final AtomicLong counter = new AtomicLong(); + public static final AtomicLong nullInvocations = new AtomicLong(); public Object cache(Object arg1) { return counter.getAndIncrement(); @@ -41,11 +44,63 @@ public class AnnotatedClassCacheableService implements CacheableService { public void invalidate(Object arg1) { } + @CacheEvict("default") + public void evictWithException(Object arg1) { + throw new RuntimeException("exception thrown - evict should NOT occur"); + } + + @CacheEvict(value = "default", allEntries = true) + public void evictAll(Object arg1) { + } + + @CacheEvict(value = "default", afterInvocation = false) + public void evictEarly(Object arg1) { + throw new RuntimeException("exception thrown - evict should still occur"); + } + + @CacheEvict(value = "default", key = "#p0") + public void evict(Object arg1, Object arg2) { + } + + @CacheEvict(value = "default", key = "#p0", afterInvocation = false) + public void invalidateEarly(Object arg1, Object arg2) { + throw new RuntimeException("exception thrown - evict should still occur"); + } + @Cacheable(value = "default", key = "#p0") public Object key(Object arg1, Object arg2) { return counter.getAndIncrement(); } + @Cacheable(value = "default", key = "#root.methodName + #root.caches[0].name") + public Object name(Object arg1) { + return counter.getAndIncrement(); + } + + @Cacheable(value = "default", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target") + public Object rootVars(Object arg1) { + return counter.getAndIncrement(); + } + + @CachePut("default") + public Object update(Object arg1) { + return counter.getAndIncrement(); + } + + @CachePut(value = "default", condition = "#arg.equals(3)") + public Object conditionalUpdate(Object arg) { + return arg; + } + + public Object nullValue(Object arg1) { + nullInvocations.incrementAndGet(); + return null; + } + + public Number nullInvocations() { + return nullInvocations.get(); + } + public Long throwChecked(Object arg1) throws Exception { throw new UnsupportedOperationException(arg1.toString()); } @@ -53,4 +108,31 @@ public class AnnotatedClassCacheableService implements CacheableService { public Long throwUnchecked(Object arg1) { throw new UnsupportedOperationException(); } -} + + // multi annotations + + @Caching(cacheable = { @Cacheable("primary"), @Cacheable("secondary") }) + public Object multiCache(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") }) + public Object multiEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(cacheable = { @Cacheable(value = "primary", key = "#root.methodName") }, evict = { @CacheEvict("secondary") }) + public Object multiCacheAndEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(cacheable = { @Cacheable(value = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") }) + public Object multiConditionalCacheAndEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(put = { @CachePut("primary"), @CachePut("secondary") }) + public Object multiUpdate(Object arg1) { + return arg1; + } +} \ No newline at end of file diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java b/org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java index 80338a6de0..6c3b61169d 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 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. @@ -16,7 +16,6 @@ package org.springframework.cache.config; - /** * Basic service interface. * @@ -28,11 +27,44 @@ public interface CacheableService { void invalidate(Object arg1); + void evictEarly(Object arg1); + + void evictAll(Object arg1); + + void evictWithException(Object arg1); + + void evict(Object arg1, Object arg2); + + void invalidateEarly(Object arg1, Object arg2); + T conditional(int field); T key(Object arg1, Object arg2); + T name(Object arg1); + + T nullValue(Object arg1); + + T update(Object arg1); + + T conditionalUpdate(Object arg2); + + Number nullInvocations(); + + T rootVars(Object arg1); + T throwChecked(Object arg1) throws Exception; T throwUnchecked(Object arg1); + + // multi annotations + T multiCache(Object arg1); + + T multiEvict(Object arg1); + + T multiCacheAndEvict(Object arg1); + + T multiConditionalCacheAndEvict(Object arg1); + + T multiUpdate(Object arg1); } \ No newline at end of file diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/org.springframework.aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index 45a2c2c4df..aeabc9a046 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2010 the original author or authors. + * Copyright 2010-2011 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. @@ -19,8 +19,9 @@ package org.springframework.cache.config; import java.util.concurrent.atomic.AtomicLong; import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; - +import org.springframework.cache.annotation.Caching; /** * Simple cacheable service @@ -29,7 +30,8 @@ import org.springframework.cache.annotation.Cacheable; */ public class DefaultCacheableService implements CacheableService { - private AtomicLong counter = new AtomicLong(); + private final AtomicLong counter = new AtomicLong(); + private final AtomicLong nullInvocations = new AtomicLong(); @Cacheable("default") public Long cache(Object arg1) { @@ -40,6 +42,29 @@ public class DefaultCacheableService implements CacheableService { public void invalidate(Object arg1) { } + @CacheEvict("default") + public void evictWithException(Object arg1) { + throw new RuntimeException("exception thrown - evict should NOT occur"); + } + + @CacheEvict(value = "default", allEntries = true) + public void evictAll(Object arg1) { + } + + @CacheEvict(value = "default", afterInvocation = false) + public void evictEarly(Object arg1) { + throw new RuntimeException("exception thrown - evict should still occur"); + } + + @CacheEvict(value = "default", key = "#p0") + public void evict(Object arg1, Object arg2) { + } + + @CacheEvict(value = "default", key = "#p0", afterInvocation = false) + public void invalidateEarly(Object arg1, Object arg2) { + throw new RuntimeException("exception thrown - evict should still occur"); + } + @Cacheable(value = "default", condition = "#classField == 3") public Long conditional(int classField) { return counter.getAndIncrement(); @@ -50,6 +75,36 @@ public class DefaultCacheableService implements CacheableService { return counter.getAndIncrement(); } + @Cacheable(value = "default", key = "#root.methodName") + public Long name(Object arg1) { + return counter.getAndIncrement(); + } + + @Cacheable(value = "default", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target") + public Long rootVars(Object arg1) { + return counter.getAndIncrement(); + } + + @CachePut("default") + public Long update(Object arg1) { + return counter.getAndIncrement(); + } + + @CachePut(value = "default", condition = "#arg.equals(3)") + public Long conditionalUpdate(Object arg) { + return Long.valueOf(arg.toString()); + } + + @Cacheable("default") + public Long nullValue(Object arg1) { + nullInvocations.incrementAndGet(); + return null; + } + + public Number nullInvocations() { + return nullInvocations.get(); + } + @Cacheable("default") public Long throwChecked(Object arg1) throws Exception { throw new Exception(arg1.toString()); @@ -59,4 +114,31 @@ public class DefaultCacheableService implements CacheableService { public Long throwUnchecked(Object arg1) { throw new UnsupportedOperationException(arg1.toString()); } + + // multi annotations + + @Caching(cacheable = { @Cacheable("primary"), @Cacheable("secondary") }) + public Long multiCache(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") }) + public Long multiEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(cacheable = { @Cacheable(value = "primary", key = "#root.methodName") }, evict = { @CacheEvict("secondary") }) + public Long multiCacheAndEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(cacheable = { @Cacheable(value = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") }) + public Long multiConditionalCacheAndEvict(Object arg1) { + return counter.getAndIncrement(); + } + + @Caching(put = { @CachePut("primary"), @CachePut("secondary") }) + public Long multiUpdate(Object arg1) { + return Long.valueOf(arg1.toString()); + } } \ No newline at end of file diff --git a/org.springframework.aspects/src/test/java/org/springframework/cache/config/annotation-cache-aspectj.xml b/org.springframework.aspects/src/test/java/org/springframework/cache/config/annotation-cache-aspectj.xml index e962a2a654..5ef8152e47 100644 --- a/org.springframework.aspects/src/test/java/org/springframework/cache/config/annotation-cache-aspectj.xml +++ b/org.springframework.aspects/src/test/java/org/springframework/cache/config/annotation-cache-aspectj.xml @@ -13,13 +13,15 @@ - + + +