Skip to content

Commit ed0035d

Browse files
committed
Improved ReactiveAdapter support
1 parent 11f1e85 commit ed0035d

File tree

5 files changed

+186
-170
lines changed

5 files changed

+186
-170
lines changed

spring-context/src/main/java/org/springframework/cache/interceptor/AsyncWrapResult.java

-24
This file was deleted.

spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java

+4-12
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.util.ObjectUtils;
5151
import org.springframework.util.StringUtils;
5252

53+
5354
/**
5455
* Base class for caching aspects, such as the {@link CacheInterceptor}
5556
* or an AspectJ aspect.
@@ -381,24 +382,15 @@ public Object call() throws Exception {
381382
if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
382383
// If there are no put requests, just use the cache hit
383384
cacheValue = cacheHit.get();
384-
returnValue = cacheResultWrapperManager.wrap(method.getReturnType(), cacheValue);
385+
returnValue = cacheResultWrapperManager.wrap(cacheValue, method.getReturnType());
385386
updateCache(cacheValue, contexts, cachePutRequests);
386387
}
387388
else {
388389
// Invoke the method if we don't have a cache hit
389390
Object originalReturnValue = invokeOperation(invoker);
390391

391-
returnValue = cacheResultWrapperManager.asyncUnwrap(originalReturnValue, method.getReturnType(), new AsyncWrapResult(new AsyncWrapResult.CallBack() {
392-
@Override
393-
public void onValue(Object cacheValue) {
394-
updateCache(cacheValue, contexts, cachePutRequests);
395-
}
396-
397-
@Override
398-
public void onError(Throwable throwable) {
399-
// Exceptions are not cached
400-
}
401-
}));
392+
returnValue = cacheResultWrapperManager.asyncUnwrap(originalReturnValue, method.getReturnType(),
393+
unwrappedValue -> updateCache(unwrappedValue, contexts, cachePutRequests));
402394
}
403395

404396
return returnValue;

spring-context/src/main/java/org/springframework/cache/interceptor/CacheResultWrapper.java

-41
This file was deleted.

spring-context/src/main/java/org/springframework/cache/interceptor/CacheResultWrapperManager.java

+81-92
Original file line numberDiff line numberDiff line change
@@ -15,162 +15,151 @@
1515
*/
1616
package org.springframework.cache.interceptor;
1717

18-
import java.util.HashMap;
19-
import java.util.Map;
20-
import java.util.Optional;
21-
import java.util.function.Supplier;
2218

23-
import reactor.core.publisher.Mono;
19+
import java.util.*;
20+
import java.util.function.Consumer;
2421

25-
import rx.Single;
22+
import org.springframework.util.ObjectUtils;
23+
import reactor.core.publisher.Mono;
2624

2725
import org.springframework.core.ReactiveAdapter;
2826
import org.springframework.core.ReactiveAdapterRegistry;
2927
import org.springframework.util.ClassUtils;
30-
import org.springframework.util.ObjectUtils;
3128

3229

3330
/**
34-
* Manages the {@link CacheResultWrapper} instances to apply only the appropriate.
31+
* Manages the {@link ReactiveResultWrapper} instances to apply only the appropriate.
3532
* @author Pablo Diaz-Lopez
3633
*/
3734
public class CacheResultWrapperManager {
38-
private Map<Class<?>, CacheResultWrapper> unwrapperByClass;
35+
private ReactiveResultWrapper reactiveResultWrapper;
3936

4037
public CacheResultWrapperManager() {
41-
unwrapperByClass = new HashMap<>();
42-
43-
unwrapperByClass.put(Optional.class, new OptionalUnWrapper());
44-
registerReactiveWrappers();
38+
tryRegisterReactiveAdapterResultWrapper();
4539
}
4640

47-
private void registerReactiveWrappers() {
48-
if(tryRegisterResultWrapper("reactor.core.publisher.Mono", MonoReactiveWrapper::new)) {
49-
ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry();
41+
private void tryRegisterReactiveAdapterResultWrapper() {
42+
boolean monoIsPresent = ClassUtils.isPresent("reactor.core.publisher.Mono", CacheAspectSupport.class.getClassLoader());
5043

51-
tryRegisterResultWrapper("rx.Single", () -> new SingleReactiveWrapper(adapterRegistry));
52-
}
53-
}
54-
55-
private boolean tryRegisterResultWrapper(String className, Supplier<CacheResultWrapper> cacheResultWrapperSupplier) {
56-
try {
57-
Class<?> clazz = ClassUtils.forName(className, CacheAspectSupport.class.getClassLoader());
58-
CacheResultWrapper cacheResultWrapper = cacheResultWrapperSupplier.get();
59-
unwrapperByClass.put(clazz, cacheResultWrapper);
60-
return true;
61-
} catch (ClassNotFoundException e) {
62-
// Cannot register wrapper
63-
return false;
44+
if (monoIsPresent) {
45+
reactiveResultWrapper = new ReactiveAdapterResultWrapper();
46+
} else {
47+
reactiveResultWrapper = new DoNothingReactiveResultWrapper();
6448
}
6549
}
6650

6751
/**
6852
* Wraps a value
6953
*
70-
* @param clazz the target class wanted
7154
* @param value the value to be wrapped
55+
* @param clazz the target class wanted
7256
* @return the value wrapped if it can, or the same value if it cannot handle it
7357
*/
74-
public Object wrap(Class<?> clazz, Object value) {
75-
if (value != null) {
76-
CacheResultWrapper unwrapper = unwrapperByClass.get(clazz);
77-
78-
if (unwrapper != null) {
79-
return unwrapper.wrap(value);
80-
}
58+
public Object wrap(Object value, Class<?> clazz) {
59+
if (clazz.equals(Optional.class)) {
60+
return Optional.ofNullable(value);
8161
}
8262

83-
return value;
63+
return reactiveResultWrapper.tryToWrap(value, clazz);
8464
}
8565

8666
/**
87-
* Unwraps the value asynchronously
67+
* Unwraps the value asynchronously, if it can (or needs to) be unwrapped
8868
*
89-
* @param valueWrapped the value wrapped to be unwrapped
69+
* @param value the value wrapped to be unwrapped
9070
* @param asyncResult where the result will be notified
9171
* @return the same value wrapped or decorated in order to notify when it finish.
9272
*/
93-
public Object asyncUnwrap(Object valueWrapped, Class<?> classWrapped, AsyncWrapResult asyncResult) {
94-
if (valueWrapped != null) {
95-
CacheResultWrapper unwrapper = unwrapperByClass.get(classWrapped);
73+
public Object asyncUnwrap(Object value, Class<?> clazz, Consumer<Object> asyncResult) {
74+
if (clazz.equals(Optional.class)) {
75+
asyncResult.accept(ObjectUtils.unwrapOptional(value));
9676

97-
if (unwrapper != null) {
98-
return unwrapper.notifyResult(valueWrapped, asyncResult);
99-
}
77+
return value;
10078
}
10179

102-
asyncResult.complete(valueWrapped);
103-
104-
return valueWrapped;
80+
return reactiveResultWrapper.notifyResult(value, clazz, asyncResult);
10581
}
10682

107-
private class SingleReactiveWrapper extends MonoReactiveAdapterWrapper {
108-
public SingleReactiveWrapper(ReactiveAdapterRegistry registry) {
109-
super(registry, Single.class);
110-
}
83+
/**
84+
* Wrapper/Unwrapper, it allows to notifyResult values to be cached and wraps it back
85+
* in order to be consumed.
86+
* @author Pablo Diaz-Lopez
87+
*/
88+
interface ReactiveResultWrapper {
89+
/**
90+
* Wraps to the wrapping target class
91+
* @param value the value to wrap
92+
* @return the value wrapped
93+
*/
94+
Object tryToWrap(Object value, Class<?> clazz);
95+
96+
/**
97+
* Unwraps a value and returns it decorated if it needs in order to
98+
* notify the result, this will be the case if the wrapped value is not
99+
* available at the moment (it is calculated asynchronously)
100+
* @param value the value wrapped
101+
* @param clazz class to be wrapped into
102+
* @param asyncResult it will call it when the value it's available
103+
* @return the same value wrapped or a version decorated.
104+
*/
105+
Object notifyResult(Object value, Class<?> clazz, Consumer<Object> asyncResult);
106+
111107
}
112108

113-
private class MonoReactiveWrapper implements CacheResultWrapper {
109+
private class DoNothingReactiveResultWrapper implements ReactiveResultWrapper {
114110
@Override
115-
public Mono<?> wrap(Object value) {
116-
return Mono.justOrEmpty(value);
111+
public Object tryToWrap(Object value, Class<?> clazz) {
112+
return value;
117113
}
118114

119115
@Override
120-
public Mono<?> notifyResult(Object objectWrapped, AsyncWrapResult asyncResult) {
121-
Mono<?> monoWrapped = (Mono<?>) objectWrapped;
122-
123-
return monoWrapped
124-
.doOnSuccess(asyncResult::complete)
125-
.doOnError(asyncResult::error);
116+
public Object notifyResult(Object value, Class<?> valueClass, Consumer<Object> asyncResult) {
117+
asyncResult.accept(value);
118+
return value;
126119
}
127120
}
128121

122+
private class ReactiveAdapterResultWrapper implements ReactiveResultWrapper {
123+
private ReactiveAdapterRegistry registry;
129124

130-
private abstract class MonoReactiveAdapterWrapper implements CacheResultWrapper {
131-
private ReactiveAdapter adapter;
132-
private MonoReactiveWrapper monoReactiveWrapper;
133-
134-
MonoReactiveAdapterWrapper(ReactiveAdapterRegistry registry, Class<?> wrapperClass) {
135-
this.adapter = registry.getAdapterFrom(wrapperClass);
136-
this.monoReactiveWrapper = new MonoReactiveWrapper();
125+
public ReactiveAdapterResultWrapper() {
126+
this.registry = new ReactiveAdapterRegistry();
137127
}
138128

139129
@SuppressWarnings("unchecked")
140130
@Override
141-
public Object wrap(Object value) {
142-
return adapter.fromPublisher(monoReactiveWrapper.wrap(value));
143-
}
131+
public Object tryToWrap(Object value, Class<?> clazz) {
132+
ReactiveAdapter adapterToWrapped = registry.getAdapterTo(clazz);
144133

145-
@SuppressWarnings("unchecked")
146-
@Override
147-
public Object notifyResult(Object valueWrapped, AsyncWrapResult asyncResult) {
148-
Mono<?> monoWrapped = adapter.toMono(valueWrapped);
149-
Mono<?> monoCacheWrapped = monoReactiveWrapper.notifyResult(monoWrapped, asyncResult);
134+
if (isValid(adapterToWrapped)) {
135+
Mono<?> monoWrapped = Mono.justOrEmpty(value);
136+
return adapterToWrapped.fromPublisher(monoWrapped);
150137

151-
return adapter.fromPublisher(monoCacheWrapped);
138+
} else {
139+
return value;
140+
}
152141
}
153-
}
154-
155-
/**
156-
* Inner class to avoid a hard dependency on Java 8.
157-
*/
158-
private class OptionalUnWrapper implements CacheResultWrapper {
159142

143+
@SuppressWarnings("unchecked")
160144
@Override
161-
public Optional<?> notifyResult(Object optionalObject, AsyncWrapResult asyncResult) {
162-
Optional<?> optional = (Optional<?>) optionalObject;
145+
public Object notifyResult(Object value, Class<?> valueClass, Consumer<Object> asyncResult) {
146+
ReactiveAdapter adapter = registry.getAdapterFrom(valueClass);
163147

164-
Object value = ObjectUtils.unwrapOptional(optional);
148+
if (isValid(adapter)) {
149+
Mono<?> monoWrapped = adapter.toMono(value)
150+
.doOnSuccess(asyncResult);
165151

166-
asyncResult.complete(value);
152+
return adapter.fromPublisher(monoWrapped);
153+
} else {
154+
asyncResult.accept(value);
155+
156+
return value;
157+
}
167158

168-
return optional;
169159
}
170160

171-
@Override
172-
public Optional<?> wrap(Object value) {
173-
return Optional.ofNullable(value);
161+
private boolean isValid(ReactiveAdapter adapter) {
162+
return adapter != null && !adapter.getDescriptor().isMultiValue();
174163
}
175164
}
176165
}

0 commit comments

Comments
 (0)