From 2a2a8d3f939f2f16a85a5ec6b2d8699b95f20cc0 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 9 Nov 2015 13:03:40 +0100 Subject: [PATCH] Allow null values to be cached with `@CacheResult` Even though the JSR-107 spec forbids to store null values, our cache abstraction allows that behaviour with a special handled (and this is the default behaviour). While this was working fine with our own set of annotations, the JSR-107 interceptor counterpart was interpreting the spec sensu strictu. We now allow for that special case as well. Issue: SPR-13641 --- .../AnnotatedClassCacheableService.java | 5 +++++ .../config/AnnotatedJCacheableService.java | 8 +++++++- .../cache/config/CacheableService.java | 4 +++- .../cache/config/DefaultCacheableService.java | 6 ++++++ .../interceptor/CacheResultInterceptor.java | 6 ++---- .../config/AbstractJCacheAnnotationTests.java | 19 +++++++++++++++++- .../jcache/config/JCacheableService.java | 4 +++- .../AnnotatedJCacheableService.java | 8 +++++++- .../cache/config/AbstractAnnotationTests.java | 20 +++++++++++++++++++ .../AnnotatedClassCacheableService.java | 5 +++++ .../cache/config/CacheableService.java | 4 +++- .../cache/config/DefaultCacheableService.java | 6 ++++++ .../cache/config/cache-advice.xml | 1 + 13 files changed, 86 insertions(+), 10 deletions(-) diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 2da3cce681..9426acc8aa 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -38,6 +38,11 @@ public class AnnotatedClassCacheableService implements CacheableService return counter.getAndIncrement(); } + @Override + public Object cacheNull(Object arg1) { + return null; + } + @Override public Object conditional(int field) { return null; diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java index 247f10f728..30e17ee843 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -58,6 +58,12 @@ public class AnnotatedJCacheableService implements JCacheableService { return counter.getAndIncrement(); } + @Override + @CacheResult + public Long cacheNull(String id) { + return null; + } + @Override @CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class) public Long cacheWithException(@CacheKey String id, boolean matchFilter) { diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java index e77a609f0f..17d299d915 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -26,6 +26,8 @@ public interface CacheableService { T cache(Object arg1); + T cacheNull(Object arg1); + void invalidate(Object arg1); void evictEarly(Object arg1); diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index 670ba3c06e..aeebd53c57 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -40,6 +40,12 @@ public class DefaultCacheableService implements CacheableService { return counter.getAndIncrement(); } + @Override + @Cacheable("testCache") + public Long cacheNull(Object arg1) { + return null; + } + @Override @CacheEvict("testCache") public void invalidate(Object arg1) { diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java index e472729401..7c178edf02 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -59,9 +59,7 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor { T cache(String id); + T cacheNull(String id); + T cacheWithException(String id, boolean matchFilter); T cacheWithCheckedException(String id, boolean matchFilter) throws IOException; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java index cb93b67641..57c699e9b9 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -57,6 +57,12 @@ public class AnnotatedJCacheableService implements JCacheableService { return counter.getAndIncrement(); } + @Override + @CacheResult + public Long cacheNull(String id) { + return null; + } + @Override @CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class) public Long cacheWithException(@CacheKey String id, boolean matchFilter) { diff --git a/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java b/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java index 5ee37593b5..bcc58fa51a 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java @@ -89,6 +89,21 @@ public abstract class AbstractAnnotationTests { assertSame(r1, r3); } + public void testCacheableNull(CacheableService service) throws Exception { + Object o1 = new Object(); + assertNull(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, cm.getCache("testCache").get(o1).get()); + assertNull("Cached value should be null", r3); + } + public void testEvict(CacheableService service) throws Exception { Object o1 = new Object(); @@ -457,6 +472,11 @@ public abstract class AbstractAnnotationTests { testCacheable(cs); } + @Test + public void testCacheableNull() throws Exception { + testCacheableNull(cs); + } + @Test public void testInvalidate() throws Exception { testEvict(cs); diff --git a/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 3060d29a58..c65d15beca 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -40,6 +40,11 @@ public class AnnotatedClassCacheableService implements CacheableService return counter.getAndIncrement(); } + @Override + public Object cacheNull(Object arg1) { + return null; + } + @Override public Object conditional(int field) { return null; diff --git a/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java index 8abd12eee6..a129f6178b 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java +++ b/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -27,6 +27,8 @@ public interface CacheableService { T cache(Object arg1); + T cacheNull(Object arg1); + void invalidate(Object arg1); void evictEarly(Object arg1); diff --git a/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index de3ce9b663..f2b843375c 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -41,6 +41,12 @@ public class DefaultCacheableService implements CacheableService { return counter.getAndIncrement(); } + @Override + @Cacheable("testCache") + public Long cacheNull(Object arg1) { + return null; + } + @Override @CacheEvict("testCache") public void invalidate(Object arg1) { diff --git a/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml b/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml index 8b6e61ec48..86db01a0f3 100644 --- a/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml +++ b/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml @@ -10,6 +10,7 @@ +