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
+ import org .springframework .lang .NonNull ;
31
+ import org .springframework .lang .Nullable ;
30
32
import org .springframework .util .Assert ;
31
33
import org .springframework .util .ObjectUtils ;
32
34
import org .springframework .util .ReflectionUtils ;
33
35
36
+ /**
37
+ * Couchbase-backed Cache Methods that take a Class return non-wrapped objects - cache-miss cannot be distinguished from
38
+ * cached null - this is what AbstractValueAdaptingCache does Methods that do not take a Class return wrapped objects -
39
+ * the wrapper is null for cache-miss - the exception is T get(final Object key, final Callable<T> valueLoader), which
40
+ * does not return a wrapper because if there is a cache-miss, it gets the value from valueLoader (and caches it). There
41
+ * are anomalies with get(key, ValueLoader) - which returns non-wrapped object.
42
+ */
34
43
public class CouchbaseCache extends AbstractValueAdaptingCache {
35
44
36
45
private final String name ;
@@ -60,31 +69,42 @@ private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {
60
69
}
61
70
}
62
71
72
+ @ NonNull
63
73
@ Override
64
74
public String getName () {
65
75
return name ;
66
76
}
67
77
78
+ @ NonNull
68
79
@ Override
69
80
public CouchbaseCacheWriter getNativeCache () {
70
81
return cacheWriter ;
71
82
}
72
83
73
- @ Override
74
- protected Object lookup (final Object key ) {
75
- return cacheWriter .get (cacheConfig .getCollectionName (), createCacheKey (key ), cacheConfig .getValueTranscoder ());
84
+ /**
85
+ * same as inherited, but passes clazz for transcoder
86
+ */
87
+ protected Object lookup (final Object key , @ Nullable Class <?> clazz ) {
88
+ return cacheWriter .get (cacheConfig .getCollectionName (), createCacheKey (key ), cacheConfig .getValueTranscoder (),
89
+ clazz );
76
90
}
77
91
92
+ @ NonNull
93
+ @ Override
94
+ protected Object lookup (@ NonNull final Object key ) {
95
+ return lookup (key , Object .class );
96
+ }
78
97
/**
79
98
* Returns the configuration for this {@link CouchbaseCache}.
80
99
*/
81
100
public CouchbaseCacheConfiguration getCacheConfiguration () {
82
101
return cacheConfig ;
83
102
}
84
103
104
+ @ NonNull
85
105
@ Override
86
106
@ SuppressWarnings ("unchecked" )
87
- public synchronized <T > T get (final Object key , final Callable <T > valueLoader ) {
107
+ public synchronized <T > T get (@ NonNull final Object key , @ NonNull final Callable <T > valueLoader ) {
88
108
ValueWrapper result = get (key );
89
109
90
110
if (result != null ) {
@@ -96,43 +116,68 @@ public synchronized <T> T get(final Object key, final Callable<T> valueLoader) {
96
116
return value ;
97
117
}
98
118
119
+ @ NonNull
99
120
@ Override
100
- public void put (final Object key , final Object value ) {
101
- if (!isAllowNullValues () && value == null ) {
121
+ @ SuppressWarnings ("unchecked" )
122
+ public <T > T get (@ NonNull final Object key , @ Nullable Class <T > type ) {
123
+ Object value = this .fromStoreValue (this .lookup (key , type ));
124
+ if (value != null && type != null && !type .isInstance (value )) {
125
+ throw new IllegalStateException ("Cached value is not of required type [" + type .getName () + "]: " + value );
126
+ } else {
127
+ return (T ) value ;
128
+ }
129
+ }
102
130
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 ));
131
+ @ Nullable
132
+ public synchronized <T > T get (@ NonNull final Object key , final Callable <T > valueLoader , @ Nullable Class <T > type ) {
133
+ T value = get (key , type );
134
+ if (value == null ) { // cannot distinguish between cache miss and cached null
135
+ value = valueFromLoader (key , valueLoader );
136
+ put (key , value );
107
137
}
138
+ return value ;
139
+ }
108
140
141
+ @ Override
142
+ public void put (@ NonNull final Object key , final Object value ) {
109
143
cacheWriter .put (cacheConfig .getCollectionName (), createCacheKey (key ), toStoreValue (value ), cacheConfig .getExpiry (),
110
144
cacheConfig .getValueTranscoder ());
111
145
}
112
146
113
147
@ Override
114
- public ValueWrapper putIfAbsent (final Object key , final Object value ) {
115
- if (!isAllowNullValues () && value == null ) {
116
- return get (key );
117
- }
148
+ public ValueWrapper putIfAbsent (@ NonNull final Object key , final Object value ) {
118
149
119
150
Object result = cacheWriter .putIfAbsent (cacheConfig .getCollectionName (), createCacheKey (key ), toStoreValue (value ),
120
151
cacheConfig .getExpiry (), cacheConfig .getValueTranscoder ());
121
152
122
- if (result == null ) {
123
- return null ;
124
- }
153
+ return toValueWrapper (result );
154
+ }
125
155
126
- return new SimpleValueWrapper (result );
156
+ /**
157
+ * Not sure why this isn't in AbstractValueAdaptingCache
158
+ *
159
+ * @param key
160
+ * @param value
161
+ * @param clazz
162
+ * @return
163
+ * @param <T>
164
+ */
165
+ @ SuppressWarnings ("unchecked" )
166
+ public <T > T putIfAbsent (@ NonNull final Object key , final Object value , final Class <T > clazz ) {
167
+
168
+ Object result = cacheWriter .putIfAbsent (cacheConfig .getCollectionName (), createCacheKey (key ),
169
+ toStoreValue (value ), cacheConfig .getExpiry (), cacheConfig .getValueTranscoder (), clazz );
170
+
171
+ return (T ) result ;
127
172
}
128
173
129
174
@ Override
130
- public void evict (final Object key ) {
175
+ public void evict (@ NonNull final Object key ) {
131
176
cacheWriter .remove (cacheConfig .getCollectionName (), createCacheKey (key ));
132
177
}
133
178
134
179
@ Override
135
- public boolean evictIfPresent (final Object key ) {
180
+ public boolean evictIfPresent (@ NonNull final Object key ) {
136
181
return cacheWriter .remove (cacheConfig .getCollectionName (), createCacheKey (key ));
137
182
}
138
183
@@ -152,7 +197,7 @@ public void clear() {
152
197
* @param key will never be {@literal null}.
153
198
* @return never {@literal null}.
154
199
*/
155
- protected String createCacheKey (final Object key ) {
200
+ protected String createCacheKey (@ NonNull final Object key ) {
156
201
String convertedKey = convertKey (key );
157
202
if (!cacheConfig .usePrefix ()) {
158
203
return convertedKey ;
@@ -167,7 +212,10 @@ protected String createCacheKey(final Object key) {
167
212
* @return never {@literal null}.
168
213
* @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
169
214
*/
170
- protected String convertKey (final Object key ) {
215
+ protected String convertKey (@ NonNull final Object key ) {
216
+ if (key == null ) {
217
+ throw new IllegalArgumentException (String .format ("Cache '%s' does not allow 'null' key." , name ));
218
+ }
171
219
if (key instanceof String ) {
172
220
return (String ) key ;
173
221
}
0 commit comments