Skip to content

Commit 112781f

Browse files
committed
Reliable null value handling in ConcurrentMapCache, GuavaCache, JCacheCache
The 4.2 variant of this fix includes a common AbstractValueAdaptingCache base class and a common NullValue holder class. Issue: SPR-13553
1 parent 1d59c5f commit 112781f

File tree

6 files changed

+187
-190
lines changed

6 files changed

+187
-190
lines changed

spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ public final Ehcache getNativeCache() {
6363
@Override
6464
public ValueWrapper get(Object key) {
6565
Element element = this.cache.get(key);
66-
return toWrapper(element);
66+
return toValueWrapper(element);
6767
}
6868

6969
@Override
@@ -85,7 +85,7 @@ public void put(Object key, Object value) {
8585
@Override
8686
public ValueWrapper putIfAbsent(Object key, Object value) {
8787
Element existingElement = this.cache.putIfAbsent(new Element(key, value));
88-
return toWrapper(existingElement);
88+
return toValueWrapper(existingElement);
8989
}
9090

9191
@Override
@@ -98,7 +98,7 @@ public void clear() {
9898
this.cache.removeAll();
9999
}
100100

101-
private ValueWrapper toWrapper(Element element) {
101+
private ValueWrapper toValueWrapper(Element element) {
102102
return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
103103
}
104104

spring-context-support/src/main/java/org/springframework/cache/guava/GuavaCache.java

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,31 @@
1616

1717
package org.springframework.cache.guava;
1818

19-
import java.io.Serializable;
2019
import java.util.concurrent.Callable;
2120
import java.util.concurrent.ExecutionException;
2221

2322
import com.google.common.cache.LoadingCache;
2423
import com.google.common.util.concurrent.UncheckedExecutionException;
2524

26-
import org.springframework.cache.Cache;
27-
import org.springframework.cache.support.SimpleValueWrapper;
25+
import org.springframework.cache.support.AbstractValueAdaptingCache;
2826
import org.springframework.util.Assert;
2927

3028
/**
31-
* Spring {@link Cache} adapter implementation on top of a
32-
* Guava {@link com.google.common.cache.Cache} instance.
29+
* Spring {@link org.springframework.cache.Cache} adapter implementation
30+
* on top of a Guava {@link com.google.common.cache.Cache} instance.
3331
*
3432
* <p>Requires Google Guava 12.0 or higher.
3533
*
3634
* @author Juergen Hoeller
3735
* @author Stephane Nicoll
3836
* @since 4.0
3937
*/
40-
public class GuavaCache implements Cache {
41-
42-
private static final Object NULL_HOLDER = new NullHolder();
38+
public class GuavaCache extends AbstractValueAdaptingCache {
4339

4440
private final String name;
4541

4642
private final com.google.common.cache.Cache<Object, Object> cache;
4743

48-
private final boolean allowNullValues;
49-
5044

5145
/**
5246
* Create a {@link GuavaCache} instance with the specified name and the
@@ -67,11 +61,11 @@ public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cac
6761
* values for this cache
6862
*/
6963
public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache, boolean allowNullValues) {
64+
super(allowNullValues);
7065
Assert.notNull(name, "Name must not be null");
7166
Assert.notNull(cache, "Cache must not be null");
7267
this.name = name;
7368
this.cache = cache;
74-
this.allowNullValues = allowNullValues;
7569
}
7670

7771

@@ -85,32 +79,23 @@ public final com.google.common.cache.Cache<Object, Object> getNativeCache() {
8579
return this.cache;
8680
}
8781

88-
public final boolean isAllowNullValues() {
89-
return this.allowNullValues;
90-
}
91-
9282
@Override
9383
public ValueWrapper get(Object key) {
9484
if (this.cache instanceof LoadingCache) {
9585
try {
9686
Object value = ((LoadingCache<Object, Object>) this.cache).get(key);
97-
return toWrapper(value);
87+
return toValueWrapper(value);
9888
}
9989
catch (ExecutionException ex) {
10090
throw new UncheckedExecutionException(ex.getMessage(), ex);
10191
}
10292
}
103-
return toWrapper(this.cache.getIfPresent(key));
93+
return super.get(key);
10494
}
10595

10696
@Override
107-
@SuppressWarnings("unchecked")
108-
public <T> T get(Object key, Class<T> type) {
109-
Object value = fromStoreValue(this.cache.getIfPresent(key));
110-
if (value != null && type != null && !type.isInstance(value)) {
111-
throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
112-
}
113-
return (T) value;
97+
protected Object lookup(Object key) {
98+
return this.cache.getIfPresent(key);
11499
}
115100

116101
@Override
@@ -123,7 +108,7 @@ public ValueWrapper putIfAbsent(Object key, final Object value) {
123108
try {
124109
PutIfAbsentCallable callable = new PutIfAbsentCallable(value);
125110
Object result = this.cache.get(key, callable);
126-
return (callable.called ? null : toWrapper(result));
111+
return (callable.called ? null : toValueWrapper(result));
127112
}
128113
catch (ExecutionException ex) {
129114
throw new IllegalStateException(ex);
@@ -141,42 +126,6 @@ public void clear() {
141126
}
142127

143128

144-
/**
145-
* Convert the given value from the internal store to a user value
146-
* returned from the get method (adapting {@code null}).
147-
* @param storeValue the store value
148-
* @return the value to return to the user
149-
*/
150-
protected Object fromStoreValue(Object storeValue) {
151-
if (this.allowNullValues && storeValue == NULL_HOLDER) {
152-
return null;
153-
}
154-
return storeValue;
155-
}
156-
157-
/**
158-
* Convert the given user value, as passed into the put method,
159-
* to a value in the internal store (adapting {@code null}).
160-
* @param userValue the given user value
161-
* @return the value to store
162-
*/
163-
protected Object toStoreValue(Object userValue) {
164-
if (this.allowNullValues && userValue == null) {
165-
return NULL_HOLDER;
166-
}
167-
return userValue;
168-
}
169-
170-
private ValueWrapper toWrapper(Object value) {
171-
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
172-
}
173-
174-
175-
@SuppressWarnings("serial")
176-
private static class NullHolder implements Serializable {
177-
}
178-
179-
180129
private class PutIfAbsentCallable implements Callable<Object> {
181130

182131
private final Object value;
Lines changed: 6 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,7 @@
1616

1717
package org.springframework.cache.jcache;
1818

19-
import java.io.Serializable;
20-
21-
import org.springframework.cache.Cache;
22-
import org.springframework.cache.support.SimpleValueWrapper;
19+
import org.springframework.cache.support.AbstractValueAdaptingCache;
2320
import org.springframework.util.Assert;
2421

2522
/**
@@ -32,14 +29,10 @@
3229
* @author Stephane Nicoll
3330
* @since 3.2
3431
*/
35-
public class JCacheCache implements Cache {
36-
37-
private static final Object NULL_HOLDER = new NullHolder();
32+
public class JCacheCache extends AbstractValueAdaptingCache {
3833

3934
private final javax.cache.Cache<Object, Object> cache;
4035

41-
private final boolean allowNullValues;
42-
4336

4437
/**
4538
* Create an {@link org.springframework.cache.jcache.JCacheCache} instance.
@@ -55,9 +48,9 @@ public JCacheCache(javax.cache.Cache<Object, Object> jcache) {
5548
* @param allowNullValues whether to accept and convert null values for this cache
5649
*/
5750
public JCacheCache(javax.cache.Cache<Object, Object> jcache, boolean allowNullValues) {
51+
super(allowNullValues);
5852
Assert.notNull(jcache, "Cache must not be null");
5953
this.cache = jcache;
60-
this.allowNullValues = allowNullValues;
6154
}
6255

6356

@@ -71,24 +64,9 @@ public final javax.cache.Cache<Object, Object> getNativeCache() {
7164
return this.cache;
7265
}
7366

74-
public final boolean isAllowNullValues() {
75-
return this.allowNullValues;
76-
}
77-
7867
@Override
79-
public ValueWrapper get(Object key) {
80-
Object value = this.cache.get(key);
81-
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
82-
}
83-
84-
@Override
85-
@SuppressWarnings("unchecked")
86-
public <T> T get(Object key, Class<T> type) {
87-
Object value = fromStoreValue(this.cache.get(key));
88-
if (value != null && type != null && !type.isInstance(value)) {
89-
throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
90-
}
91-
return (T) value;
68+
protected Object lookup(Object key) {
69+
return this.cache.get(key);
9270
}
9371

9472
@Override
@@ -112,36 +90,4 @@ public void clear() {
11290
this.cache.removeAll();
11391
}
11492

115-
116-
/**
117-
* Convert the given value from the internal store to a user value
118-
* returned from the get method (adapting {@code null}).
119-
* @param storeValue the store value
120-
* @return the value to return to the user
121-
*/
122-
protected Object fromStoreValue(Object storeValue) {
123-
if (this.allowNullValues && storeValue == NULL_HOLDER) {
124-
return null;
125-
}
126-
return storeValue;
127-
}
128-
129-
/**
130-
* Convert the given user value, as passed into the put method,
131-
* to a value in the internal store (adapting {@code null}).
132-
* @param userValue the given user value
133-
* @return the value to store
134-
*/
135-
protected Object toStoreValue(Object userValue) {
136-
if (this.allowNullValues && userValue == null) {
137-
return NULL_HOLDER;
138-
}
139-
return userValue;
140-
}
141-
142-
143-
@SuppressWarnings("serial")
144-
private static class NullHolder implements Serializable {
145-
}
146-
14793
}

0 commit comments

Comments
 (0)