|
15 | 15 | */
|
16 | 16 | package org.springframework.cache.interceptor;
|
17 | 17 |
|
18 |
| -import java.util.HashMap; |
19 |
| -import java.util.Map; |
20 |
| -import java.util.Optional; |
21 |
| -import java.util.function.Supplier; |
22 | 18 |
|
23 |
| -import reactor.core.publisher.Mono; |
| 19 | +import java.util.*; |
| 20 | +import java.util.function.Consumer; |
24 | 21 |
|
25 |
| -import rx.Single; |
| 22 | +import org.springframework.util.ObjectUtils; |
| 23 | +import reactor.core.publisher.Mono; |
26 | 24 |
|
27 | 25 | import org.springframework.core.ReactiveAdapter;
|
28 | 26 | import org.springframework.core.ReactiveAdapterRegistry;
|
29 | 27 | import org.springframework.util.ClassUtils;
|
30 |
| -import org.springframework.util.ObjectUtils; |
31 | 28 |
|
32 | 29 |
|
33 | 30 | /**
|
34 |
| - * Manages the {@link CacheResultWrapper} instances to apply only the appropriate. |
| 31 | + * Manages the {@link ReactiveResultWrapper} instances to apply only the appropriate. |
35 | 32 | * @author Pablo Diaz-Lopez
|
36 | 33 | */
|
37 | 34 | public class CacheResultWrapperManager {
|
38 |
| - private Map<Class<?>, CacheResultWrapper> unwrapperByClass; |
| 35 | + private ReactiveResultWrapper reactiveResultWrapper; |
39 | 36 |
|
40 | 37 | public CacheResultWrapperManager() {
|
41 |
| - unwrapperByClass = new HashMap<>(); |
42 |
| - |
43 |
| - unwrapperByClass.put(Optional.class, new OptionalUnWrapper()); |
44 |
| - registerReactiveWrappers(); |
| 38 | + tryRegisterReactiveAdapterResultWrapper(); |
45 | 39 | }
|
46 | 40 |
|
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()); |
50 | 43 |
|
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(); |
64 | 48 | }
|
65 | 49 | }
|
66 | 50 |
|
67 | 51 | /**
|
68 | 52 | * Wraps a value
|
69 | 53 | *
|
70 |
| - * @param clazz the target class wanted |
71 | 54 | * @param value the value to be wrapped
|
| 55 | + * @param clazz the target class wanted |
72 | 56 | * @return the value wrapped if it can, or the same value if it cannot handle it
|
73 | 57 | */
|
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); |
81 | 61 | }
|
82 | 62 |
|
83 |
| - return value; |
| 63 | + return reactiveResultWrapper.tryToWrap(value, clazz); |
84 | 64 | }
|
85 | 65 |
|
86 | 66 | /**
|
87 |
| - * Unwraps the value asynchronously |
| 67 | + * Unwraps the value asynchronously, if it can (or needs to) be unwrapped |
88 | 68 | *
|
89 |
| - * @param valueWrapped the value wrapped to be unwrapped |
| 69 | + * @param value the value wrapped to be unwrapped |
90 | 70 | * @param asyncResult where the result will be notified
|
91 | 71 | * @return the same value wrapped or decorated in order to notify when it finish.
|
92 | 72 | */
|
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)); |
96 | 76 |
|
97 |
| - if (unwrapper != null) { |
98 |
| - return unwrapper.notifyResult(valueWrapped, asyncResult); |
99 |
| - } |
| 77 | + return value; |
100 | 78 | }
|
101 | 79 |
|
102 |
| - asyncResult.complete(valueWrapped); |
103 |
| - |
104 |
| - return valueWrapped; |
| 80 | + return reactiveResultWrapper.notifyResult(value, clazz, asyncResult); |
105 | 81 | }
|
106 | 82 |
|
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 | + |
111 | 107 | }
|
112 | 108 |
|
113 |
| - private class MonoReactiveWrapper implements CacheResultWrapper { |
| 109 | + private class DoNothingReactiveResultWrapper implements ReactiveResultWrapper { |
114 | 110 | @Override
|
115 |
| - public Mono<?> wrap(Object value) { |
116 |
| - return Mono.justOrEmpty(value); |
| 111 | + public Object tryToWrap(Object value, Class<?> clazz) { |
| 112 | + return value; |
117 | 113 | }
|
118 | 114 |
|
119 | 115 | @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; |
126 | 119 | }
|
127 | 120 | }
|
128 | 121 |
|
| 122 | + private class ReactiveAdapterResultWrapper implements ReactiveResultWrapper { |
| 123 | + private ReactiveAdapterRegistry registry; |
129 | 124 |
|
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(); |
137 | 127 | }
|
138 | 128 |
|
139 | 129 | @SuppressWarnings("unchecked")
|
140 | 130 | @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); |
144 | 133 |
|
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); |
150 | 137 |
|
151 |
| - return adapter.fromPublisher(monoCacheWrapped); |
| 138 | + } else { |
| 139 | + return value; |
| 140 | + } |
152 | 141 | }
|
153 |
| - } |
154 |
| - |
155 |
| - /** |
156 |
| - * Inner class to avoid a hard dependency on Java 8. |
157 |
| - */ |
158 |
| - private class OptionalUnWrapper implements CacheResultWrapper { |
159 | 142 |
|
| 143 | + @SuppressWarnings("unchecked") |
160 | 144 | @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); |
163 | 147 |
|
164 |
| - Object value = ObjectUtils.unwrapOptional(optional); |
| 148 | + if (isValid(adapter)) { |
| 149 | + Mono<?> monoWrapped = adapter.toMono(value) |
| 150 | + .doOnSuccess(asyncResult); |
165 | 151 |
|
166 |
| - asyncResult.complete(value); |
| 152 | + return adapter.fromPublisher(monoWrapped); |
| 153 | + } else { |
| 154 | + asyncResult.accept(value); |
| 155 | + |
| 156 | + return value; |
| 157 | + } |
167 | 158 |
|
168 |
| - return optional; |
169 | 159 | }
|
170 | 160 |
|
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(); |
174 | 163 | }
|
175 | 164 | }
|
176 | 165 | }
|
0 commit comments