18
18
19
19
import java .util .List ;
20
20
import java .util .concurrent .CompletableFuture ;
21
+ import java .util .concurrent .CompletionException ;
21
22
import java .util .concurrent .atomic .AtomicLong ;
22
23
24
+ import org .junit .jupiter .api .Test ;
23
25
import org .junit .jupiter .params .ParameterizedTest ;
24
26
import org .junit .jupiter .params .provider .ValueSource ;
25
27
import reactor .core .publisher .Flux ;
29
31
import org .springframework .cache .CacheManager ;
30
32
import org .springframework .cache .concurrent .ConcurrentMapCache ;
31
33
import org .springframework .cache .concurrent .ConcurrentMapCacheManager ;
34
+ import org .springframework .cache .interceptor .CacheErrorHandler ;
35
+ import org .springframework .cache .interceptor .LoggingCacheErrorHandler ;
32
36
import org .springframework .context .annotation .AnnotationConfigApplicationContext ;
33
37
import org .springframework .context .annotation .Bean ;
34
38
import org .springframework .context .annotation .Configuration ;
35
39
import org .springframework .lang .Nullable ;
36
40
37
41
import static org .assertj .core .api .Assertions .assertThat ;
42
+ import static org .assertj .core .api .AssertionsForClassTypes .catchThrowable ;
38
43
39
44
/**
40
45
* Tests for annotation-based caching methods that use reactive operators.
@@ -113,6 +118,51 @@ void cacheHitDetermination(Class<?> configClass) {
113
118
ctx .close ();
114
119
}
115
120
121
+ @ Test
122
+ void cacheErrorHandlerWithLoggingCacheErrorHandler () {
123
+ AnnotationConfigApplicationContext ctx =
124
+ new AnnotationConfigApplicationContext (ExceptionCacheManager .class , ReactiveCacheableService .class , ErrorHandlerCachingConfiguration .class );
125
+ ReactiveCacheableService service = ctx .getBean (ReactiveCacheableService .class );
126
+
127
+ Object key = new Object ();
128
+ Long r1 = service .cacheFuture (key ).join ();
129
+
130
+ assertThat (r1 ).isNotNull ();
131
+ assertThat (r1 ).as ("cacheFuture" ).isEqualTo (0L );
132
+
133
+ key = new Object ();
134
+
135
+ r1 = service .cacheMono (key ).block ();
136
+
137
+ assertThat (r1 ).isNotNull ();
138
+ assertThat (r1 ).as ("cacheMono" ).isEqualTo (1L );
139
+
140
+ key = new Object ();
141
+
142
+ r1 = service .cacheFlux (key ).blockFirst ();
143
+
144
+ assertThat (r1 ).isNotNull ();
145
+ assertThat (r1 ).as ("cacheFlux blockFirst" ).isEqualTo (2L );
146
+ }
147
+
148
+ @ Test
149
+ void cacheErrorHandlerWithSimpleCacheErrorHandler () {
150
+ AnnotationConfigApplicationContext ctx =
151
+ new AnnotationConfigApplicationContext (ExceptionCacheManager .class , ReactiveCacheableService .class );
152
+ ReactiveCacheableService service = ctx .getBean (ReactiveCacheableService .class );
153
+
154
+ Throwable completableFuturThrowable = catchThrowable (() -> service .cacheFuture (new Object ()).join ());
155
+ assertThat (completableFuturThrowable ).isInstanceOf (CompletionException .class )
156
+ .extracting (Throwable ::getCause )
157
+ .isInstanceOf (UnsupportedOperationException .class );
158
+
159
+ Throwable monoThrowable = catchThrowable (() -> service .cacheMono (new Object ()).block ());
160
+ assertThat (monoThrowable ).isInstanceOf (UnsupportedOperationException .class );
161
+
162
+ Throwable fluxThrowable = catchThrowable (() -> service .cacheFlux (new Object ()).blockFirst ());
163
+ assertThat (fluxThrowable ).isInstanceOf (UnsupportedOperationException .class );
164
+ }
165
+
116
166
@ ParameterizedTest
117
167
@ ValueSource (classes = {EarlyCacheHitDeterminationConfig .class ,
118
168
EarlyCacheHitDeterminationWithoutNullValuesConfig .class ,
@@ -139,7 +189,6 @@ void fluxCacheDoesntDependOnFirstRequest(Class<?> configClass) {
139
189
ctx .close ();
140
190
}
141
191
142
-
143
192
@ CacheConfig (cacheNames = "first" )
144
193
static class ReactiveCacheableService {
145
194
@@ -242,4 +291,41 @@ public void put(Object key, @Nullable Object value) {
242
291
}
243
292
}
244
293
294
+ @ Configuration
295
+ static class ErrorHandlerCachingConfiguration implements CachingConfigurer {
296
+
297
+ @ Bean
298
+ @ Override
299
+ public CacheErrorHandler errorHandler () {
300
+ return new LoggingCacheErrorHandler ();
301
+ }
302
+ }
303
+
304
+ @ Configuration (proxyBeanMethods = false )
305
+ @ EnableCaching
306
+ static class ExceptionCacheManager {
307
+
308
+ @ Bean
309
+ CacheManager cacheManager () {
310
+ return new ConcurrentMapCacheManager ("first" ) {
311
+ @ Override
312
+ protected Cache createConcurrentMapCache (String name ) {
313
+ return new ConcurrentMapCache (name , isAllowNullValues ()) {
314
+ @ Override
315
+ public CompletableFuture <?> retrieve (Object key ) {
316
+ return CompletableFuture .supplyAsync (() -> {
317
+ throw new UnsupportedOperationException ("Test exception on retrieve" );
318
+ });
319
+ }
320
+
321
+ @ Override
322
+ public void put (Object key , @ Nullable Object value ) {
323
+ throw new UnsupportedOperationException ("Test exception on put" );
324
+ }
325
+ };
326
+ }
327
+ };
328
+ }
329
+ }
330
+
245
331
}
0 commit comments