15
15
*/
16
16
package org .springframework .data .couchbase .cache ;
17
17
18
+
18
19
import java .lang .reflect .Method ;
19
20
import java .util .Arrays ;
20
21
import java .util .Collection ;
23
24
import java .util .concurrent .Callable ;
24
25
25
26
import org .springframework .cache .support .AbstractValueAdaptingCache ;
26
- import org .springframework .cache .support .SimpleValueWrapper ;
27
27
import org .springframework .core .convert .ConversionFailedException ;
28
28
import org .springframework .core .convert .ConversionService ;
29
29
import org .springframework .core .convert .TypeDescriptor ;
30
30
import org .springframework .util .Assert ;
31
31
import org .springframework .util .ObjectUtils ;
32
32
import org .springframework .util .ReflectionUtils ;
33
33
34
+ /**
35
+ * Couchbase-backed Cache Methods that take a Class return non-wrapped objects - cache-miss cannot be distinguished from
36
+ * cached null - this is what AbstractValueAdaptingCache does Methods that do not take a Class return wrapped objects -
37
+ * the wrapper is null for cache-miss - the exception is T get(final Object key, final Callable<T> valueLoader), which
38
+ * does not return a wrapper because if there is a cache-miss, it gets the value from valueLoader (and caches it). There
39
+ * are anomalies with get(key, ValueLoader) - which returns non-wrapped object.
40
+ */
34
41
public class CouchbaseCache extends AbstractValueAdaptingCache {
35
42
36
43
private final String name ;
@@ -70,11 +77,18 @@ public CouchbaseCacheWriter getNativeCache() {
70
77
return cacheWriter ;
71
78
}
72
79
80
+ /**
81
+ * same as inherited, but passes clazz for transcoder
82
+ */
83
+ protected Object lookup (final Object key , Class <?> clazz ) {
84
+ return cacheWriter .get (cacheConfig .getCollectionName (), createCacheKey (key ), cacheConfig .getValueTranscoder (),
85
+ clazz );
86
+ }
87
+
73
88
@ Override
74
89
protected Object lookup (final Object key ) {
75
- return cacheWriter . get ( cacheConfig . getCollectionName (), createCacheKey ( key ), cacheConfig . getValueTranscoder () );
90
+ return lookup ( key , Object . class );
76
91
}
77
-
78
92
/**
79
93
* Returns the configuration for this {@link CouchbaseCache}.
80
94
*/
@@ -97,33 +111,56 @@ public synchronized <T> T get(final Object key, final Callable<T> valueLoader) {
97
111
}
98
112
99
113
@ Override
100
- public void put (final Object key , final Object value ) {
101
- if (!isAllowNullValues () && value == null ) {
114
+ @ SuppressWarnings ("unchecked" )
115
+ public <T > T get (final Object key , Class <T > type ) {
116
+ Object value = this .fromStoreValue (this .lookup (key , type ));
117
+ if (value != null && type != null && !type .isInstance (value )) {
118
+ throw new IllegalStateException ("Cached value is not of required type [" + type .getName () + "]: " + value );
119
+ } else {
120
+ return (T ) value ;
121
+ }
122
+ }
102
123
103
- throw new IllegalArgumentException (String .format (
104
- "Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\" #result == null\" )' or "
105
- + "configure CouchbaseCache to allow 'null' via CouchbaseCacheConfiguration." ,
106
- name ));
124
+ public synchronized <T > T get (final Object key , final Callable <T > valueLoader , Class <T > type ) {
125
+ T value = get (key , type );
126
+ if (value == null ) { // cannot distinguish between cache miss and cached null
127
+ value = valueFromLoader (key , valueLoader );
128
+ put (key , value );
107
129
}
130
+ return value ;
131
+ }
108
132
133
+ @ Override
134
+ public void put (final Object key , final Object value ) {
109
135
cacheWriter .put (cacheConfig .getCollectionName (), createCacheKey (key ), toStoreValue (value ), cacheConfig .getExpiry (),
110
136
cacheConfig .getValueTranscoder ());
111
137
}
112
138
113
139
@ Override
114
140
public ValueWrapper putIfAbsent (final Object key , final Object value ) {
115
- if (!isAllowNullValues () && value == null ) {
116
- return get (key );
117
- }
118
141
119
142
Object result = cacheWriter .putIfAbsent (cacheConfig .getCollectionName (), createCacheKey (key ), toStoreValue (value ),
120
143
cacheConfig .getExpiry (), cacheConfig .getValueTranscoder ());
121
144
122
- if (result == null ) {
123
- return null ;
124
- }
145
+ return toValueWrapper (result );
146
+ }
125
147
126
- return new SimpleValueWrapper (result );
148
+ /**
149
+ * Not sure why this isn't in AbstractValueAdaptingCache
150
+ *
151
+ * @param key
152
+ * @param value
153
+ * @param clazz
154
+ * @return
155
+ * @param <T>
156
+ */
157
+ @ SuppressWarnings ("unchecked" )
158
+ public <T > T putIfAbsent (final Object key , final Object value , final Class <T > clazz ) {
159
+
160
+ Object result = cacheWriter .putIfAbsent (cacheConfig .getCollectionName (), createCacheKey (key ),
161
+ toStoreValue (value ), cacheConfig .getExpiry (), cacheConfig .getValueTranscoder (), clazz );
162
+
163
+ return (T ) result ;
127
164
}
128
165
129
166
@ Override
@@ -168,6 +205,9 @@ protected String createCacheKey(final Object key) {
168
205
* @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
169
206
*/
170
207
protected String convertKey (final Object key ) {
208
+ if (key == null ) {
209
+ throw new IllegalArgumentException (String .format ("Cache '%s' does not allow 'null' key." , name ));
210
+ }
171
211
if (key instanceof String ) {
172
212
return (String ) key ;
173
213
}
0 commit comments