|
16 | 16 |
|
17 | 17 | package org.springframework.core; |
18 | 18 |
|
| 19 | +import java.lang.reflect.Method; |
19 | 20 | import java.util.ArrayList; |
20 | 21 | import java.util.List; |
21 | 22 | import java.util.Optional; |
|
29 | 30 | import rx.RxReactiveStreams; |
30 | 31 |
|
31 | 32 | import org.springframework.lang.Nullable; |
| 33 | +import org.springframework.util.ClassUtils; |
| 34 | +import org.springframework.util.ReflectionUtils; |
32 | 35 |
|
33 | | -import static org.springframework.core.ReactiveTypeDescriptor.*; |
| 36 | +import static org.springframework.core.ReactiveTypeDescriptor.multiValue; |
| 37 | +import static org.springframework.core.ReactiveTypeDescriptor.noValue; |
| 38 | +import static org.springframework.core.ReactiveTypeDescriptor.singleOptionalValue; |
| 39 | +import static org.springframework.core.ReactiveTypeDescriptor.singleRequiredValue; |
34 | 40 |
|
35 | 41 | /** |
36 | 42 | * A registry of adapters to adapt a Reactive Streams {@link Publisher} to/from |
37 | 43 | * various async/reactive types such as {@code CompletableFuture}, RxJava |
38 | 44 | * {@code Observable}, and others. |
39 | 45 | * |
40 | 46 | * <p>By default, depending on classpath availability, adapters are registered |
41 | | - * for Reactor, RxJava 1, RxJava 2 types, and {@link CompletableFuture}. |
| 47 | + * for Reactor, RxJava 1, RxJava 2 types, {@link CompletableFuture}, and Java 9+ |
| 48 | + * Flow.Publisher. |
42 | 49 | * |
43 | 50 | * @author Rossen Stoyanchev |
44 | 51 | * @author Sebastien Deleuze |
@@ -82,6 +89,18 @@ public ReactiveAdapterRegistry() { |
82 | 89 | catch (Throwable ex) { |
83 | 90 | // Ignore |
84 | 91 | } |
| 92 | + |
| 93 | + // Java 9+ Flow.Publisher |
| 94 | + try { |
| 95 | + new ReactorJdkFlowAdapterRegistrar().registerAdapter(this); |
| 96 | + } |
| 97 | + catch (NoSuchMethodException ex) { |
| 98 | + throw new IllegalStateException("Failed to find JdkFlowAdapter methods", ex); |
| 99 | + } |
| 100 | + catch (Throwable ex) { |
| 101 | + // Ignore |
| 102 | + // We can fall back on "reactive-streams-flow-bridge" (once released) |
| 103 | + } |
85 | 104 | } |
86 | 105 |
|
87 | 106 |
|
@@ -232,6 +251,34 @@ void registerAdapters(ReactiveAdapterRegistry registry) { |
232 | 251 | } |
233 | 252 |
|
234 | 253 |
|
| 254 | + private static class ReactorJdkFlowAdapterRegistrar { |
| 255 | + |
| 256 | + // TODO: remove reflection when build requires JDK 9+ |
| 257 | + |
| 258 | + void registerAdapter(ReactiveAdapterRegistry registry) |
| 259 | + throws NoSuchMethodException, ClassNotFoundException { |
| 260 | + |
| 261 | + String name = "java.util.concurrent.Flow.Publisher"; |
| 262 | + Class<?> type = ClassUtils.forName(name, getClass().getClassLoader()); |
| 263 | + |
| 264 | + Method toFlowMethod = getMethod("publisherToFlowPublisher", Publisher.class); |
| 265 | + Method toFluxMethod = getMethod("flowPublisherToFlux", type); |
| 266 | + |
| 267 | + Object emptyFlow = ReflectionUtils.invokeMethod(toFlowMethod, null, Flux.empty()); |
| 268 | + |
| 269 | + registry.registerReactiveType( |
| 270 | + multiValue(type, () -> emptyFlow), |
| 271 | + source -> (Publisher<?>) ReflectionUtils.invokeMethod(toFluxMethod, null, source), |
| 272 | + publisher -> ReflectionUtils.invokeMethod(toFlowMethod, null, publisher) |
| 273 | + ); |
| 274 | + } |
| 275 | + |
| 276 | + private static Method getMethod(String name, Class<?> argumentType) throws NoSuchMethodException { |
| 277 | + return reactor.adapter.JdkFlowAdapter.class.getMethod(name, argumentType); |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + |
235 | 282 | /** |
236 | 283 | * Extension of ReactiveAdapter that wraps adapted (raw) Publisher's as |
237 | 284 | * {@link Flux} or {@link Mono} depending on the underlying reactive type's |
|
0 commit comments