From 101220bad1ac3b4082a08aff4e9c8254e71febaf Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 22 Jul 2016 00:17:13 -0400 Subject: [PATCH] Add ReactiveAdapterRegistry Issue: SPR-14159 --- .../springframework/core/ReactiveAdapter.java | 116 +++++++ .../core/ReactiveAdapterRegistry.java | 282 ++++++++++++++++++ .../MonoToCompletableFutureConverter.java | 62 ---- .../support/ReactorToRxJava1Converter.java | 81 ----- ...MonoToCompletableFutureConverterTests.java | 56 ---- .../support/ReactiveAdapterRegistryTests.java | 80 +++++ .../ReactorToRxJava1ConverterTests.java | 69 ----- .../config/WebReactiveConfiguration.java | 21 +- ...ontentNegotiatingResultHandlerSupport.java | 24 +- .../reactive/result/SimpleResultHandler.java | 58 ++-- ...AbstractMessageReaderArgumentResolver.java | 71 ++--- .../AbstractMessageWriterResultHandler.java | 65 ++-- .../HttpEntityArgumentResolver.java | 43 +-- .../RequestBodyArgumentResolver.java | 26 +- .../RequestMappingHandlerAdapter.java | 18 +- .../annotation/ResponseBodyResultHandler.java | 61 ++-- .../ResponseEntityResultHandler.java | 84 +++--- .../view/ViewResolutionResultHandler.java | 36 ++- .../reactive/DispatcherHandlerErrorTests.java | 4 +- ...tNegotiatingResultHandlerSupportTests.java | 3 +- .../result/SimpleResultHandlerTests.java | 9 +- ...mpleUrlHandlerMappingIntegrationTests.java | 3 +- .../HttpEntityArgumentResolverTests.java | 21 +- .../MessageReaderArgumentResolverTests.java | 20 +- .../MessageWriterResultHandlerTests.java | 33 +- .../RequestBodyArgumentResolverTests.java | 14 +- .../ResponseBodyResultHandlerTests.java | 11 +- .../ResponseEntityResultHandlerTests.java | 19 +- .../ViewResolutionResultHandlerTests.java | 20 +- 29 files changed, 775 insertions(+), 635 deletions(-) create mode 100644 spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java create mode 100644 spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java delete mode 100644 spring-core/src/main/java/org/springframework/core/convert/support/MonoToCompletableFutureConverter.java delete mode 100644 spring-core/src/main/java/org/springframework/core/convert/support/ReactorToRxJava1Converter.java delete mode 100644 spring-core/src/test/java/org/springframework/core/convert/support/MonoToCompletableFutureConverterTests.java create mode 100644 spring-core/src/test/java/org/springframework/core/convert/support/ReactiveAdapterRegistryTests.java delete mode 100644 spring-core/src/test/java/org/springframework/core/convert/support/ReactorToRxJava1ConverterTests.java diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java new file mode 100644 index 0000000000..bb0b7ad72a --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.core; + +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Contract for adapting to and from {@link Flux} and {@link Mono}. + * + *

An adapter supports a specific adaptee type whose stream semantics can be + * checked via {@link #getDescriptor()}. + * + *

Use the {@link ReactiveAdapterRegistry} to obtain an adapter for a + * supported adaptee type or to register additional adapters. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public interface ReactiveAdapter { + + /** + * Return a descriptor with further information about the adaptee. + */ + Descriptor getDescriptor(); + + /** + * Adapt the given Object to a {@link Mono} + * @param source the source object to adapt + * @return the resulting {@link Mono} possibly empty + */ + Mono toMono(Object source); + + /** + * Adapt the given Object to a {@link Flux}. + * @param source the source object to adapt + * @return the resulting {@link Flux} possibly empty + */ + Flux toFlux(Object source); + + /** + * Adapt the given Object to a Publisher. + * @param source the source object to adapt + * @return the resulting {@link Mono} or {@link Flux} possibly empty + */ + Publisher toPublisher(Object source); + + /** + * Adapt the given Publisher to the target adaptee. + * @param publisher the publisher to adapt + * @return the resulting adaptee + */ + Object fromPublisher(Publisher publisher); + + + /** + * A descriptor with information about the adaptee stream semantics. + */ + class Descriptor { + + private final boolean isMultiValue; + + private final boolean supportsEmpty; + + private final boolean isNoValue; + + + public Descriptor(boolean isMultiValue, boolean canBeEmpty, boolean isNoValue) { + this.isMultiValue = isMultiValue; + this.supportsEmpty = canBeEmpty; + this.isNoValue = isNoValue; + } + + + /** + * Return {@code true} if the adaptee implies 0..N values can be produced + * and is therefore a good fit to adapt to {@link Flux}. A {@code false} + * return value implies the adaptee will produce 1 value at most and is + * therefore a good fit for {@link Mono}. + */ + public boolean isMultiValue() { + return this.isMultiValue; + } + + /** + * Return {@code true} if the adaptee can complete without values. + */ + public boolean supportsEmpty() { + return this.supportsEmpty; + } + + /** + * Return {@code true} if the adaptee implies no values will be produced, + * i.e. providing only completion or error signal. + */ + public boolean isNoValue() { + return this.isNoValue; + } + + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java new file mode 100644 index 0000000000..921dae625f --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -0,0 +1,282 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.core; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.reactivestreams.Publisher; +import reactor.adapter.RxJava1Adapter; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import rx.Completable; +import rx.Observable; +import rx.Single; + +import org.springframework.core.ReactiveAdapter.Descriptor; +import org.springframework.util.ClassUtils; + +/** + * A registry of adapters to adapt to {@link Flux} and {@link Mono}. + * + *

By default there are adapters for {@link CompletableFuture}, RxJava 1, and + * also for a any Reactive Streams {@link Publisher}. Additional adapters can be + * registered via {@link #registerFluxAdapter) and {@link #registerMonoAdapter}. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class ReactiveAdapterRegistry { + + private static final boolean rxJava1Present = + ClassUtils.isPresent("rx.Observable", ReactiveAdapterRegistry.class.getClassLoader()); + + + private final Map, ReactiveAdapter> adapterMap = new LinkedHashMap<>(); + + + /** + * Create a registry and auto-register default adapters. + */ + public ReactiveAdapterRegistry() { + + // Flux and Mono ahead of Publisher... + registerMonoAdapter(Mono.class, + source -> (Mono) source, source -> source, new Descriptor(false, true, false)); + + registerFluxAdapter( + Flux.class, source -> (Flux) source, source -> source); + + registerFluxAdapter( + Publisher.class, source -> Flux.from((Publisher) source), source -> source); + + registerMonoAdapter(CompletableFuture.class, + source -> Mono.fromFuture((CompletableFuture) source), + source -> Mono.from((Publisher) source).toFuture(), + new Descriptor(false, true, false) + ); + + if (rxJava1Present) { + new RxJava1AdapterRegistrar().register(this); + } + } + + + /** + * Register an adapter for adapting to and from a {@link Mono}. The provided + * functions can assume that input will never be {@code null} and also that + * any {@link Optional} wrapper is unwrapped. + */ + public void registerMonoAdapter(Class adapteeType, + Function> toAdapter, Function, Object> fromAdapter, + Descriptor descriptor) { + + this.adapterMap.put(adapteeType, new MonoReactiveAdapter(toAdapter, fromAdapter, descriptor)); + } + + /** + * Register an adapter for adapting to and from a {@link Flux}. The provided + * functions can assume that input will never be {@code null} and also that + * any {@link Optional} wrapper is unwrapped. + */ + public void registerFluxAdapter(Class adapteeType, + Function> toAdapter, Function, Object> fromAdapter) { + + this.adapterMap.put(adapteeType, new FluxReactiveAdapter(toAdapter, fromAdapter)); + } + + + /** + * Get the adapter for the given adaptee type to adapt from. + */ + public ReactiveAdapter getAdapterFrom(Class adapteeType) { + return getAdapterFrom(adapteeType, null); + } + + /** + * Get the adapter for the given adaptee type to adapt from. + * If the instance is not {@code null} its actual type is used to check. + */ + public ReactiveAdapter getAdapterFrom(Class adapteeType, Object adaptee) { + Class actualType = getActualType(adapteeType, adaptee); + return getAdapterInternal(supportedType -> supportedType.isAssignableFrom(actualType)); + } + + /** + * Get the adapter for the given adaptee type to adapt to. + */ + public ReactiveAdapter getAdapterTo(Class adapteeType) { + return getAdapterTo(adapteeType, null); + } + + /** + * Get the adapter for the given adaptee type to adapt to. + * If the instance is not {@code null} its actual type is used to check. + */ + public ReactiveAdapter getAdapterTo(Class adapteeType, Object adaptee) { + Class actualType = getActualType(adapteeType, adaptee); + return getAdapterInternal(supportedType -> supportedType.equals(actualType)); + } + + + private static Class getActualType(Class adapteeType, Object adaptee) { + adaptee = unwrapOptional(adaptee); + return (adaptee != null ? adaptee.getClass() : adapteeType); + } + + private static Object unwrapOptional(Object value) { + if (value != null && value instanceof Optional) { + value = ((Optional) value).orElse(null); + } + return value; + } + + private ReactiveAdapter getAdapterInternal(Predicate> adapteeTypePredicate) { + return this.adapterMap.keySet().stream() + .filter(adapteeTypePredicate) + .map(this.adapterMap::get) + .findFirst() + .orElse(null); + } + + + @SuppressWarnings("unchecked") + private static class MonoReactiveAdapter implements ReactiveAdapter { + + private final Function> toAdapter; + + private final Function, Object> fromAdapter; + + private final Descriptor descriptor; + + + MonoReactiveAdapter(Function> to, Function, Object> from, Descriptor descriptor) { + this.toAdapter = to; + this.fromAdapter = from; + this.descriptor = descriptor; + } + + @Override + public Descriptor getDescriptor() { + return this.descriptor; + } + + @Override + public Mono toMono(Object source) { + source = unwrapOptional(source); + if (source == null) { + return Mono.empty(); + } + return (Mono) this.toAdapter.apply(source); + } + + @Override + public Flux toFlux(Object source) { + source = unwrapOptional(source); + if (source == null) { + return Flux.empty(); + } + return (Flux) this.toMono(source).flux(); + } + + @Override + public Publisher toPublisher(Object source) { + return toMono(source); + } + + @Override + public Object fromPublisher(Publisher source) { + return (source != null ? this.fromAdapter.apply((Mono) source) : null); + } + } + + @SuppressWarnings("unchecked") + private static class FluxReactiveAdapter implements ReactiveAdapter { + + private final Function> toAdapter; + + private final Function, Object> fromAdapter; + + private final Descriptor descriptor = new Descriptor(true, true, false); + + + FluxReactiveAdapter(Function> to, Function, Object> from) { + this.toAdapter = to; + this.fromAdapter = from; + } + + @Override + public Descriptor getDescriptor() { + return this.descriptor; + } + + @Override + public Mono toMono(Object source) { + source = unwrapOptional(source); + if (source == null) { + return Mono.empty(); + } + return (Mono) this.toAdapter.apply(source).next(); + } + + @Override + public Flux toFlux(Object source) { + source = unwrapOptional(source); + if (source == null) { + return Flux.empty(); + } + return (Flux) this.toAdapter.apply(source); + } + + @Override + public Publisher toPublisher(Object source) { + return toFlux(source); + } + + @Override + public Object fromPublisher(Publisher source) { + return (source != null ? this.fromAdapter.apply((Flux) source) : null); + } + } + + private static class RxJava1AdapterRegistrar { + + public void register(ReactiveAdapterRegistry registry) { + + registry.registerFluxAdapter(Observable.class, + source -> RxJava1Adapter.observableToFlux((Observable) source), + RxJava1Adapter::publisherToObservable + ); + + registry.registerMonoAdapter(Single.class, + source -> RxJava1Adapter.singleToMono((Single) source), + RxJava1Adapter::publisherToSingle, + new Descriptor(false, false, false) + ); + + registry.registerMonoAdapter(Completable.class, + source -> RxJava1Adapter.completableToMono((Completable) source), + RxJava1Adapter::publisherToCompletable, + new Descriptor(false, true, true) + ); + } + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/MonoToCompletableFutureConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/MonoToCompletableFutureConverter.java deleted file mode 100644 index 7917fa2617..0000000000 --- a/spring-core/src/main/java/org/springframework/core/convert/support/MonoToCompletableFutureConverter.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.convert.support; - -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.GenericConverter; - -/** - * Converter to adapt {@link CompletableFuture} to Reactive Streams and - * Reactor {@link Mono}. - * - * @author Sebastien Deleuze - * @since 5.0 - */ -public class MonoToCompletableFutureConverter implements GenericConverter { - - - @Override - public Set getConvertibleTypes() { - Set pairs = new LinkedHashSet<>(2); - pairs.add(new GenericConverter.ConvertiblePair(Mono.class, CompletableFuture.class)); - pairs.add(new GenericConverter.ConvertiblePair(CompletableFuture.class, Mono.class)); - return pairs; - } - - - @Override - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - if (source == null) { - return null; - } - else if (CompletableFuture.class.isAssignableFrom(sourceType.getType())) { - return Mono.fromFuture((CompletableFuture) source); - } - else if (CompletableFuture.class.isAssignableFrom(targetType.getType())) { - return Mono.from((Publisher) source).toFuture(); - } - return null; - } - -} diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ReactorToRxJava1Converter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ReactorToRxJava1Converter.java deleted file mode 100644 index d25b4f9c86..0000000000 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ReactorToRxJava1Converter.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2002-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.convert.support; - -import java.util.LinkedHashSet; -import java.util.Set; - -import org.reactivestreams.Publisher; -import reactor.adapter.RxJava1Adapter; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import rx.Completable; -import rx.Observable; -import rx.Single; - -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.GenericConverter; - -/** - * Converter to adapt RxJava1 {@link Observable}, {@link Single}, and - * {@link Completable} to Reactive Streams and Reactor types. - * - * @author Stephane Maldini - * @author Sebastien Deleuze - * @since 5.0 - */ -public final class ReactorToRxJava1Converter implements GenericConverter { - - @Override - public Set getConvertibleTypes() { - Set pairs = new LinkedHashSet<>(6); - pairs.add(new GenericConverter.ConvertiblePair(Flux.class, Observable.class)); - pairs.add(new GenericConverter.ConvertiblePair(Observable.class, Flux.class)); - pairs.add(new GenericConverter.ConvertiblePair(Mono.class, Single.class)); - pairs.add(new GenericConverter.ConvertiblePair(Single.class, Mono.class)); - pairs.add(new GenericConverter.ConvertiblePair(Mono.class, Completable.class)); - pairs.add(new GenericConverter.ConvertiblePair(Completable.class, Mono.class)); - return pairs; - } - - @Override - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - if (source == null) { - return null; - } - if (Observable.class.isAssignableFrom(sourceType.getType())) { - return RxJava1Adapter.observableToFlux((Observable) source); - } - else if (Observable.class.isAssignableFrom(targetType.getType())) { - return RxJava1Adapter.publisherToObservable((Publisher) source); - } - else if (Single.class.isAssignableFrom(sourceType.getType())) { - return RxJava1Adapter.singleToMono((Single) source); - } - else if (Single.class.isAssignableFrom(targetType.getType())) { - return RxJava1Adapter.publisherToSingle((Publisher) source); - } - else if (Completable.class.isAssignableFrom(sourceType.getType())) { - return RxJava1Adapter.completableToMono((Completable) source); - } - else if (Completable.class.isAssignableFrom(targetType.getType())) { - return RxJava1Adapter.publisherToCompletable((Publisher) source); - } - return null; - } - -} diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/MonoToCompletableFutureConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/MonoToCompletableFutureConverterTests.java deleted file mode 100644 index 64b33cc9b8..0000000000 --- a/spring-core/src/test/java/org/springframework/core/convert/support/MonoToCompletableFutureConverterTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.core.convert.support; - -import java.util.concurrent.CompletableFuture; - -import org.junit.Before; -import org.junit.Test; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * Unit tests for {@link ReactorToRxJava1Converter}. - * @author Rossen Stoyanchev - */ -public class MonoToCompletableFutureConverterTests { - - private GenericConversionService conversionService; - - - @Before - public void setUp() throws Exception { - this.conversionService = new GenericConversionService(); - this.conversionService.addConverter(new MonoToCompletableFutureConverter()); - } - - @Test - public void canConvert() throws Exception { - assertTrue(this.conversionService.canConvert(Mono.class, CompletableFuture.class)); - assertTrue(this.conversionService.canConvert(CompletableFuture.class, Mono.class)); - - assertFalse(this.conversionService.canConvert(Flux.class, CompletableFuture.class)); - assertFalse(this.conversionService.canConvert(CompletableFuture.class, Flux.class)); - - assertFalse(this.conversionService.canConvert(Publisher.class, CompletableFuture.class)); - assertFalse(this.conversionService.canConvert(CompletableFuture.class, Publisher.class)); - } - -} diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/ReactiveAdapterRegistryTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/ReactiveAdapterRegistryTests.java new file mode 100644 index 0000000000..7bc2464433 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/convert/support/ReactiveAdapterRegistryTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.core.convert.support; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Before; +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import rx.Completable; +import rx.Observable; +import rx.Single; + +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for {@link ReactiveAdapterRegistry}. + * @author Rossen Stoyanchev + */ +public class ReactiveAdapterRegistryTests { + + private ReactiveAdapterRegistry adapterRegistry; + + @Before + public void setUp() throws Exception { + this.adapterRegistry = new ReactiveAdapterRegistry(); + } + + @Test + public void getDefaultAdapters() throws Exception { + testMonoAdapter(Mono.class); + testFluxAdapter(Flux.class); + testFluxAdapter(Publisher.class); + testMonoAdapter(CompletableFuture.class); + testFluxAdapter(Observable.class); + testMonoAdapter(Single.class); + testMonoAdapter(Completable.class); + } + + private void testFluxAdapter(Class adapteeType) { + ReactiveAdapter adapter = this.adapterRegistry.getAdapterFrom(adapteeType); + assertNotNull(adapter); + assertTrue(adapter.getDescriptor().isMultiValue()); + + adapter = this.adapterRegistry.getAdapterTo(adapteeType); + assertNotNull(adapter); + assertTrue(adapter.getDescriptor().isMultiValue()); + } + + private void testMonoAdapter(Class adapteeType) { + ReactiveAdapter adapter = this.adapterRegistry.getAdapterFrom(adapteeType); + assertNotNull(adapter); + assertFalse(adapter.getDescriptor().isMultiValue()); + + adapter = this.adapterRegistry.getAdapterTo(adapteeType); + assertNotNull(adapter); + assertFalse(adapter.getDescriptor().isMultiValue()); + } + +} diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/ReactorToRxJava1ConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/ReactorToRxJava1ConverterTests.java deleted file mode 100644 index 427e9be4ef..0000000000 --- a/spring-core/src/test/java/org/springframework/core/convert/support/ReactorToRxJava1ConverterTests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.core.convert.support; - -import org.junit.Before; -import org.junit.Test; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import rx.Completable; -import rx.Observable; -import rx.Single; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * Unit tests for {@link ReactorToRxJava1Converter}. - * @author Rossen Stoyanchev - */ -public class ReactorToRxJava1ConverterTests { - - private GenericConversionService conversionService; - - - @Before - public void setUp() throws Exception { - this.conversionService = new GenericConversionService(); - this.conversionService.addConverter(new ReactorToRxJava1Converter()); - } - - @Test - public void canConvert() throws Exception { - assertTrue(this.conversionService.canConvert(Flux.class, Observable.class)); - assertTrue(this.conversionService.canConvert(Observable.class, Flux.class)); - - assertTrue(this.conversionService.canConvert(Mono.class, Single.class)); - assertTrue(this.conversionService.canConvert(Single.class, Mono.class)); - - assertTrue(this.conversionService.canConvert(Mono.class, Completable.class)); - assertTrue(this.conversionService.canConvert(Completable.class, Mono.class)); - - assertFalse(this.conversionService.canConvert(Flux.class, Single.class)); - assertFalse(this.conversionService.canConvert(Single.class, Flux.class)); - - assertFalse(this.conversionService.canConvert(Flux.class, Completable.class)); - assertFalse(this.conversionService.canConvert(Completable.class, Flux.class)); - - assertFalse(this.conversionService.canConvert(Mono.class, Observable.class)); - assertFalse(this.conversionService.canConvert(Observable.class, Mono.class)); - - assertFalse(this.conversionService.canConvert(Publisher.class, Observable.class)); - assertFalse(this.conversionService.canConvert(Observable.class, Publisher.class)); - } - -} diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java index 6244967dc4..d8bc0dcc96 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java @@ -34,8 +34,6 @@ import org.springframework.core.codec.ResourceDecoder; import org.springframework.core.codec.StringDecoder; import org.springframework.core.codec.StringEncoder; import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.format.support.DefaultFormattingConversionService; @@ -274,17 +272,8 @@ public class WebReactiveConfiguration implements ApplicationContextAware { /** * Override to add custom {@link Converter}s and {@link Formatter}s. - *

By default this method method registers: - *

*/ protected void addFormatters(FormatterRegistry registry) { - registry.addConverter(new MonoToCompletableFutureConverter()); - if (rxJava1Present) { - registry.addConverter(new ReactorToRxJava1Converter()); - } } /** @@ -334,19 +323,17 @@ public class WebReactiveConfiguration implements ApplicationContextAware { @Bean public SimpleResultHandler simpleResultHandler() { - return new SimpleResultHandler(mvcConversionService()); + return new SimpleResultHandler(); } @Bean public ResponseEntityResultHandler responseEntityResultHandler() { - return new ResponseEntityResultHandler(getMessageWriters(), mvcConversionService(), - mvcContentTypeResolver()); + return new ResponseEntityResultHandler(getMessageWriters(), mvcContentTypeResolver()); } @Bean public ResponseBodyResultHandler responseBodyResultHandler() { - return new ResponseBodyResultHandler(getMessageWriters(), mvcConversionService(), - mvcContentTypeResolver()); + return new ResponseBodyResultHandler(getMessageWriters(), mvcContentTypeResolver()); } /** @@ -405,7 +392,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware { ViewResolverRegistry registry = new ViewResolverRegistry(getApplicationContext()); configureViewResolvers(registry); List resolvers = registry.getViewResolvers(); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, mvcConversionService()); + ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, mvcContentTypeResolver()); handler.setDefaultViews(registry.getDefaultViews()); handler.setOrder(registry.getOrder()); return handler; diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java index abab1545ac..43f4b35a94 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupport.java @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.Set; import org.springframework.core.Ordered; -import org.springframework.core.convert.ConversionService; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.web.reactive.HandlerMapping; @@ -43,28 +43,32 @@ public abstract class ContentNegotiatingResultHandlerSupport implements Ordered private static final MediaType MEDIA_TYPE_APPLICATION_ALL = new MediaType("application"); - private final ConversionService conversionService; - private final RequestedContentTypeResolver contentTypeResolver; + private final ReactiveAdapterRegistry adapterRegistry; + private int order = LOWEST_PRECEDENCE; - protected ContentNegotiatingResultHandlerSupport(ConversionService conversionService, - RequestedContentTypeResolver contentTypeResolver) { + protected ContentNegotiatingResultHandlerSupport(RequestedContentTypeResolver contentTypeResolver) { + this(contentTypeResolver, new ReactiveAdapterRegistry()); + } + + protected ContentNegotiatingResultHandlerSupport(RequestedContentTypeResolver contentTypeResolver, + ReactiveAdapterRegistry adapterRegistry) { - Assert.notNull(conversionService, "'conversionService' is required."); Assert.notNull(contentTypeResolver, "'contentTypeResolver' is required."); - this.conversionService = conversionService; + Assert.notNull(adapterRegistry, "'adapterRegistry' is required."); this.contentTypeResolver = contentTypeResolver; + this.adapterRegistry = adapterRegistry; } /** - * Return the configured {@link ConversionService}. + * Return the configured {@link ReactiveAdapterRegistry}. */ - public ConversionService getConversionService() { - return this.conversionService; + public ReactiveAdapterRegistry getReactiveAdapterRegistry() { + return this.adapterRegistry; } /** diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java index f64d11e143..39d795d6f2 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/SimpleResultHandler.java @@ -18,14 +18,12 @@ package org.springframework.web.reactive.result; import java.util.Optional; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.Ordered; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.util.Assert; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; @@ -45,27 +43,26 @@ import org.springframework.web.server.ServerWebExchange; */ public class SimpleResultHandler implements Ordered, HandlerResultHandler { - protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class); - - protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class); - - - private ConversionService conversionService; + private ReactiveAdapterRegistry adapterRegistry; private int order = Ordered.LOWEST_PRECEDENCE; - public SimpleResultHandler(ConversionService conversionService) { - Assert.notNull(conversionService, "'conversionService' is required."); - this.conversionService = conversionService; + public SimpleResultHandler() { + this.adapterRegistry = new ReactiveAdapterRegistry(); + } + + public SimpleResultHandler(ReactiveAdapterRegistry adapterRegistry) { + Assert.notNull(adapterRegistry, "'adapterRegistry' is required."); + this.adapterRegistry = adapterRegistry; } /** - * Return the configured {@link ConversionService}. + * Return the configured {@link ReactiveAdapterRegistry}. */ - public ConversionService getConversionService() { - return this.conversionService; + public ReactiveAdapterRegistry getAdapterRegistry() { + return this.adapterRegistry; } /** @@ -88,37 +85,28 @@ public class SimpleResultHandler implements Ordered, HandlerResultHandler { @Override public boolean supports(HandlerResult result) { ResolvableType type = result.getReturnType(); - if (Void.TYPE.equals(type.getRawClass())) { + Class rawClass = type.getRawClass(); + if (Void.TYPE.equals(rawClass)) { return true; } - TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource()); - if (Publisher.class.isAssignableFrom(type.getRawClass()) || - canConvert(source, MONO_TYPE) || canConvert(source, FLUX_TYPE)) { - Class clazz = result.getReturnType().getGeneric(0).getRawClass(); + ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(rawClass, result.getReturnValue()); + if (adapter != null) { + Class clazz = type.getGeneric(0).getRawClass(); return Void.class.equals(clazz); } return false; } - private boolean canConvert(TypeDescriptor source, TypeDescriptor target) { - return getConversionService().canConvert(source, target); - } - @SuppressWarnings("unchecked") @Override public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { - Optional optional = result.getReturnValue(); - if (!optional.isPresent()) { + Optional optionalValue = result.getReturnValue(); + if (!optionalValue.isPresent()) { return Mono.empty(); } - Object value = optional.get(); - if (Publisher.class.isAssignableFrom(result.getReturnType().getRawClass())) { - return Mono.from((Publisher) value).then(); - } - TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource()); - return canConvert(source, MONO_TYPE) ? - ((Mono) getConversionService().convert(value, source, MONO_TYPE)) : - ((Flux) getConversionService().convert(value, source, FLUX_TYPE)).single(); + Class returnType = result.getReturnType().getRawClass(); + ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(returnType, optionalValue); + return adapter.toMono(optionalValue); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 499c8f6b93..eca6310ab3 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -25,10 +25,10 @@ import reactor.core.publisher.Mono; import org.springframework.core.Conventions; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.http.MediaType; import org.springframework.http.converter.reactive.HttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -57,34 +57,39 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException; */ public abstract class AbstractMessageReaderArgumentResolver { - private static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class); - - private static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class); - - private final List> messageReaders; - private final ConversionService conversionService; - private final Validator validator; + private final ReactiveAdapterRegistry adapterRegistry; + private final List supportedMediaTypes; /** - * Constructor with message converters and a ConversionService. - * @param messageReaders readers to convert from the request body - * @param service for converting to other reactive types from Flux and Mono + * Constructor with {@link HttpMessageReader}'s and a {@link Validator}. + * @param readers readers to convert from the request body * @param validator validator to validate decoded objects with */ - protected AbstractMessageReaderArgumentResolver(List> messageReaders, - ConversionService service, Validator validator) { + protected AbstractMessageReaderArgumentResolver(List> readers, Validator validator) { - Assert.notEmpty(messageReaders, "At least one message reader is required."); - Assert.notNull(service, "'conversionService' is required."); + this(readers, validator, new ReactiveAdapterRegistry()); + } + + /** + * Constructor that also accepts a {@link ReactiveAdapterRegistry}. + * @param messageReaders readers to convert from the request body + * @param validator validator to validate decoded objects with + * @param adapterRegistry for adapting to other reactive types from Flux and Mono + */ + protected AbstractMessageReaderArgumentResolver(List> messageReaders, + Validator validator, ReactiveAdapterRegistry adapterRegistry) { + + Assert.notEmpty(messageReaders, "At least one HttpMessageReader is required."); + Assert.notNull(adapterRegistry, "'adapterRegistry' is required"); this.messageReaders = messageReaders; - this.conversionService = service; this.validator = validator; + this.adapterRegistry = adapterRegistry; this.supportedMediaTypes = messageReaders.stream() .flatMap(converter -> converter.getReadableMediaTypes().stream()) .collect(Collectors.toList()); @@ -99,22 +104,21 @@ public abstract class AbstractMessageReaderArgumentResolver { } /** - * Return the configured {@link ConversionService}. + * Return the configured {@link ReactiveAdapterRegistry}. */ - public ConversionService getConversionService() { - return this.conversionService; + public ReactiveAdapterRegistry getReactiveAdapterRegistry() { + return this.adapterRegistry; } protected Mono readBody(MethodParameter bodyParameter, boolean isBodyRequired, ServerWebExchange exchange) { - TypeDescriptor typeDescriptor = new TypeDescriptor(bodyParameter); - boolean convertFromMono = getConversionService().canConvert(MONO_TYPE, typeDescriptor); - boolean convertFromFlux = getConversionService().canConvert(FLUX_TYPE, typeDescriptor); + Class bodyType = ResolvableType.forMethodParameter(bodyParameter).resolve(); + ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterTo(bodyType); ResolvableType elementType = ResolvableType.forMethodParameter(bodyParameter); - if (convertFromMono || convertFromFlux) { + if (adapter != null) { elementType = elementType.getGeneric(0); } @@ -126,28 +130,28 @@ public abstract class AbstractMessageReaderArgumentResolver { for (HttpMessageReader reader : getMessageReaders()) { if (reader.canRead(elementType, mediaType)) { - if (convertFromFlux) { + if (adapter != null && adapter.getDescriptor().isMultiValue()) { Flux flux = reader.read(elementType, request) .onErrorResumeWith(ex -> Flux.error(getReadError(ex, bodyParameter))); - if (checkRequired(bodyParameter, isBodyRequired)) { + if (checkRequired(adapter, isBodyRequired)) { flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter))); } if (this.validator != null) { flux = flux.map(applyValidationIfApplicable(bodyParameter)); } - return Mono.just(getConversionService().convert(flux, FLUX_TYPE, typeDescriptor)); + return Mono.just(adapter.fromPublisher(flux)); } else { Mono mono = reader.readMono(elementType, request) .otherwise(ex -> Mono.error(getReadError(ex, bodyParameter))); - if (checkRequired(bodyParameter, isBodyRequired)) { + if (checkRequired(adapter, isBodyRequired)) { mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter))); } if (this.validator != null) { mono = mono.map(applyValidationIfApplicable(bodyParameter)); } - if (convertFromMono) { - return Mono.just(getConversionService().convert(mono, MONO_TYPE, typeDescriptor)); + if (adapter != null) { + return Mono.just(adapter.fromPublisher(mono)); } else { return Mono.from(mono); @@ -159,11 +163,8 @@ public abstract class AbstractMessageReaderArgumentResolver { return Mono.error(new UnsupportedMediaTypeStatusException(mediaType, this.supportedMediaTypes)); } - protected boolean checkRequired(MethodParameter bodyParameter, boolean isBodyRequired) { - if ("rx.Single".equals(bodyParameter.getNestedParameterType().getName())) { - return true; - } - return isBodyRequired; + protected boolean checkRequired(ReactiveAdapter adapter, boolean isBodyRequired) { + return adapter != null && !adapter.getDescriptor().supportsEmpty() || isBodyRequired; } protected ServerWebInputException getReadError(Throwable ex, MethodParameter parameter) { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java index c42034efca..e82af46631 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java @@ -19,13 +19,12 @@ import java.util.List; import java.util.stream.Collectors; import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.http.MediaType; import org.springframework.http.converter.reactive.HttpMessageWriter; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -44,31 +43,42 @@ import org.springframework.web.server.ServerWebExchange; */ public abstract class AbstractMessageWriterResultHandler extends ContentNegotiatingResultHandlerSupport { - protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class); - - protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class); - - private final List> messageWriters; /** - * Constructor with message converters, a {@code ConversionService}, and a + * Constructor with {@link HttpMessageWriter}s and a * {@code RequestedContentTypeResolver}. * * @param messageWriters for serializing Objects to the response body stream - * @param conversionService for converting other reactive types (e.g. - * rx.Observable, rx.Single, etc.) to Flux or Mono * @param contentTypeResolver for resolving the requested content type */ protected AbstractMessageWriterResultHandler(List> messageWriters, - ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) { + RequestedContentTypeResolver contentTypeResolver) { - super(conversionService, contentTypeResolver); + super(contentTypeResolver); Assert.notEmpty(messageWriters, "At least one message writer is required."); this.messageWriters = messageWriters; } + /** + * Constructor with an additional {@link ReactiveAdapterRegistry}. + * + * @param messageWriters for serializing Objects to the response body stream + * @param contentTypeResolver for resolving the requested content type + * @param adapterRegistry for adapting other reactive types (e.g. rx.Observable, + * rx.Single, etc.) to Flux or Mono + */ + protected AbstractMessageWriterResultHandler(List> messageWriters, + RequestedContentTypeResolver contentTypeResolver, + ReactiveAdapterRegistry adapterRegistry) { + + super(contentTypeResolver, adapterRegistry); + Assert.notEmpty(messageWriters, "At least one message writer is required."); + this.messageWriters = messageWriters; + } + + /** * Return the configured message converters. */ @@ -78,31 +88,20 @@ public abstract class AbstractMessageWriterResultHandler extends ContentNegotiat @SuppressWarnings("unchecked") - protected Mono writeBody(ServerWebExchange exchange, Object body, - ResolvableType bodyType, MethodParameter bodyTypeParameter) { + protected Mono writeBody(Object body, MethodParameter bodyType, ServerWebExchange exchange) { - Publisher publisher = null; + Class bodyClass = bodyType.getParameterType(); + ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(bodyClass, body); + + Publisher publisher; ResolvableType elementType; - - if (Publisher.class.isAssignableFrom(bodyType.getRawClass())) { - publisher = (Publisher) body; + if (adapter != null) { + publisher = adapter.toPublisher(body); + elementType = ResolvableType.forMethodParameter(bodyType).getGeneric(0); } else { - TypeDescriptor descriptor = new TypeDescriptor(bodyTypeParameter); - if (getConversionService().canConvert(descriptor, MONO_TYPE)) { - publisher = (Publisher) getConversionService().convert(body, descriptor, MONO_TYPE); - } - else if (getConversionService().canConvert(descriptor, FLUX_TYPE)) { - publisher = (Publisher) getConversionService().convert(body, descriptor, FLUX_TYPE); - } - } - - if (publisher != null) { - elementType = bodyType.getGeneric(0); - } - else { - elementType = bodyType; publisher = Mono.justOrEmpty(body); + elementType = ResolvableType.forMethodParameter(bodyType); } if (void.class == elementType.getRawClass() || Void.class == elementType.getRawClass()) { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java index b737a54099..625460c8f9 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java @@ -20,8 +20,8 @@ import java.util.List; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.RequestEntity; @@ -45,26 +45,24 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes /** - * Constructor with message converters and a ConversionService. - * @param messageReaders readers for de-serializing the request body with - * @param service for converting to other reactive types from Flux and Mono + * Constructor with {@link HttpMessageReader}'s and a {@link Validator}. + * @param readers readers for de-serializing the request body with + * @param validator validator to validate decoded objects with */ - public HttpEntityArgumentResolver(List> messageReaders, - ConversionService service) { - - this(messageReaders, service, null); + public HttpEntityArgumentResolver(List> readers, Validator validator) { + super(readers, validator); } /** - * Constructor with message converters and a ConversionService. - * @param messageReaders readers for de-serializing the request body with - * @param service for converting to other reactive types from Flux and Mono + * Constructor that also accepts a {@link ReactiveAdapterRegistry}. + * @param readers readers for de-serializing the request body with * @param validator validator to validate decoded objects with + * @param adapterRegistry for adapting to other reactive types from Flux and Mono */ - public HttpEntityArgumentResolver(List> messageReaders, - ConversionService service, Validator validator) { + public HttpEntityArgumentResolver(List> readers, Validator validator, + ReactiveAdapterRegistry adapterRegistry) { - super(messageReaders, service, validator); + super(readers, validator, adapterRegistry); } @@ -77,20 +75,9 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes @Override public Mono resolveArgument(MethodParameter param, ModelMap model, ServerWebExchange exchange) { - ResolvableType entityType; - MethodParameter bodyParameter; - - if (getConversionService().canConvert(Mono.class, param.getParameterType())) { - entityType = ResolvableType.forMethodParameter(param).getGeneric(0); - bodyParameter = new MethodParameter(param); - bodyParameter.increaseNestingLevel(); - bodyParameter.increaseNestingLevel(); - } - else { - entityType = ResolvableType.forMethodParameter(param); - bodyParameter = new MethodParameter(param); - bodyParameter.increaseNestingLevel(); - } + ResolvableType entityType = ResolvableType.forMethodParameter(param); + MethodParameter bodyParameter = new MethodParameter(param); + bodyParameter.increaseNestingLevel(); return readBody(bodyParameter, false, exchange) .map(body -> createHttpEntity(body, entityType, exchange)) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java index 39eba423a4..3bca0935c8 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java @@ -21,7 +21,7 @@ import java.util.List; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; -import org.springframework.core.convert.ConversionService; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.converter.reactive.HttpMessageReader; import org.springframework.ui.ModelMap; import org.springframework.validation.Validator; @@ -49,26 +49,24 @@ public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentRe /** - * Constructor with message converters and a ConversionService. - * @param messageReaders readers for de-serializing the request body with - * @param service for converting to other reactive types from Flux and Mono + * Constructor with {@link HttpMessageReader}'s and a {@link Validator}. + * @param readers readers for de-serializing the request body with + * @param validator validator to validate decoded objects with */ - public RequestBodyArgumentResolver(List> messageReaders, - ConversionService service) { - - this(messageReaders, service, null); + public RequestBodyArgumentResolver(List> readers, Validator validator) { + super(readers, validator); } /** - * Constructor with message converters and a ConversionService. - * @param messageReaders readers for de-serializing the request body with - * @param service for converting to other reactive types from Flux and Mono + * Constructor that also accepts a {@link ReactiveAdapterRegistry}. + * @param readers readers for de-serializing the request body with * @param validator validator to validate decoded objects with + * @param adapterRegistry for adapting to other reactive types from Flux and Mono */ - public RequestBodyArgumentResolver(List> messageReaders, - ConversionService service, Validator validator) { + public RequestBodyArgumentResolver(List> readers, Validator validator, + ReactiveAdapterRegistry adapterRegistry) { - super(messageReaders, service, validator); + super(readers, validator, adapterRegistry); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index b550ea0ba4..cae25da552 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.codec.ByteBufferDecoder; import org.springframework.core.codec.StringDecoder; import org.springframework.core.convert.ConversionService; @@ -66,6 +67,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory private final List> messageReaders = new ArrayList<>(10); + private ReactiveAdapterRegistry reactiveAdapters = new ReactiveAdapterRegistry(); + private ConversionService conversionService = new DefaultFormattingConversionService(); private Validator validator; @@ -126,6 +129,14 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory return this.messageReaders; } + public void setReactiveAdapterRegistry(ReactiveAdapterRegistry registry) { + this.reactiveAdapters = registry; + } + + public ReactiveAdapterRegistry getReactiveAdapterRegistry() { + return this.reactiveAdapters; + } + /** * Configure a ConversionService for type conversion of controller method * arguments as well as for converting from different async types to @@ -187,13 +198,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory protected List initArgumentResolvers() { List resolvers = new ArrayList<>(); - // Annotation-based argument resolution ConversionService cs = getConversionService(); + ReactiveAdapterRegistry adapterRegistry = getReactiveAdapterRegistry(); + + // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(cs, getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory())); resolvers.add(new PathVariableMapMethodArgumentResolver()); - resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), cs, getValidator())); + resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), getValidator(), adapterRegistry)); resolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory())); @@ -202,6 +215,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory resolvers.add(new RequestAttributeMethodArgumentResolver(cs , getBeanFactory())); // Type-based argument resolution + resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), getValidator(), adapterRegistry)); resolvers.add(new ModelArgumentResolver()); // Custom resolvers diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java index 4812c9978f..7a39aeafab 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java @@ -21,15 +21,14 @@ import java.util.List; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpEntity; import org.springframework.http.converter.reactive.HttpMessageWriter; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; -import org.springframework.web.reactive.accept.HeaderContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.server.ServerWebExchange; @@ -53,42 +52,41 @@ import org.springframework.web.server.ServerWebExchange; public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandler implements HandlerResultHandler { - /** - * Constructor with message converters and a {@code ConversionService} only - * and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header - * to determine the requested content type. - * - * @param messageWriters writers for serializing to the response body stream - * @param conversionService for converting to Flux and Mono from other reactive types - */ - public ResponseBodyResultHandler(List> messageWriters, - ConversionService conversionService) { - - this(messageWriters, conversionService, new HeaderContentTypeResolver()); - } /** - * Constructor with message converters, a {@code ConversionService}, and a + * Constructor with {@link HttpMessageWriter}s and a * {@code RequestedContentTypeResolver}. * * @param messageWriters writers for serializing to the response body stream - * @param conversionService for converting other reactive types (e.g. - * rx.Observable, rx.Single, etc.) to Flux or Mono * @param contentTypeResolver for resolving the requested content type */ public ResponseBodyResultHandler(List> messageWriters, - ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) { + RequestedContentTypeResolver contentTypeResolver) { - super(messageWriters, conversionService, contentTypeResolver); + this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry()); + } + + /** + * Constructor with an additional {@link ReactiveAdapterRegistry}. + * + * @param messageWriters writers for serializing to the response body stream + * @param contentTypeResolver for resolving the requested content type + * @param adapterRegistry for adapting other reactive types (e.g. rx.Observable, + * rx.Single, etc.) to Flux or Mono + */ + public ResponseBodyResultHandler(List> messageWriters, + RequestedContentTypeResolver contentTypeResolver, + ReactiveAdapterRegistry adapterRegistry) { + + super(messageWriters, contentTypeResolver, adapterRegistry); setOrder(100); } @Override public boolean supports(HandlerResult result) { - ResolvableType returnType = result.getReturnType(); MethodParameter parameter = result.getReturnTypeSource(); - return hasResponseBodyAnnotation(parameter) && !isHttpEntityType(returnType); + return hasResponseBodyAnnotation(parameter) && !isHttpEntityType(result); } private boolean hasResponseBodyAnnotation(MethodParameter parameter) { @@ -97,26 +95,27 @@ public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandle parameter.getMethodAnnotation(ResponseBody.class) != null); } - private boolean isHttpEntityType(ResolvableType returnType) { - if (HttpEntity.class.isAssignableFrom(returnType.getRawClass())) { + private boolean isHttpEntityType(HandlerResult result) { + Class rawClass = result.getReturnType().getRawClass(); + if (HttpEntity.class.isAssignableFrom(rawClass)) { return true; } - else if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) { - ResolvableType genericType = returnType.getGeneric(0); - if (HttpEntity.class.isAssignableFrom(genericType.getRawClass())) { - return true; + else { + if (getReactiveAdapterRegistry().getAdapterFrom(rawClass, result.getReturnValue()) != null) { + ResolvableType genericType = result.getReturnType().getGeneric(0); + if (HttpEntity.class.isAssignableFrom(genericType.getRawClass())) { + return true; + } } } return false; } - @Override public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { Object body = result.getReturnValue().orElse(null); - ResolvableType bodyType = result.getReturnType(); MethodParameter bodyTypeParameter = result.getReturnTypeSource(); - return writeBody(exchange, body, bodyType, bodyTypeParameter); + return writeBody(body, bodyTypeParameter, exchange); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java index fd28d33fae..3f8264a256 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java @@ -21,8 +21,9 @@ import java.util.Optional; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.RequestEntity; @@ -31,7 +32,6 @@ import org.springframework.http.converter.reactive.HttpMessageWriter; import org.springframework.util.Assert; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; -import org.springframework.web.reactive.accept.HeaderContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.server.ServerWebExchange; @@ -47,52 +47,55 @@ import org.springframework.web.server.ServerWebExchange; public class ResponseEntityResultHandler extends AbstractMessageWriterResultHandler implements HandlerResultHandler { - /** - * Constructor with message converters and a {@code ConversionService} only - * and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header - * to determine the requested content type. - * - * @param messageWriters writers for serializing to the response body stream - * @param conversionService for converting to Flux and Mono from other reactive types - */ - public ResponseEntityResultHandler(List> messageWriters, - ConversionService conversionService) { - - this(messageWriters, conversionService, new HeaderContentTypeResolver()); - } /** - * Constructor with message converters, a {@code ConversionService}, and a + * Constructor with {@link HttpMessageWriter}s and a * {@code RequestedContentTypeResolver}. * * @param messageWriters writers for serializing to the response body stream - * @param conversionService for converting other reactive types (e.g. - * rx.Observable, rx.Single, etc.) to Flux or Mono * @param contentTypeResolver for resolving the requested content type */ public ResponseEntityResultHandler(List> messageWriters, - ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) { + RequestedContentTypeResolver contentTypeResolver) { - super(messageWriters, conversionService, contentTypeResolver); + this(messageWriters, contentTypeResolver, new ReactiveAdapterRegistry()); + } + + /** + * Constructor with an additional {@link ReactiveAdapterRegistry}. + * + * @param messageWriters writers for serializing to the response body stream + * @param contentTypeResolver for resolving the requested content type + * @param adapterRegistry for adapting other reactive types (e.g. rx.Observable, + * rx.Single, etc.) to Flux or Mono + */ + public ResponseEntityResultHandler(List> messageWriters, + RequestedContentTypeResolver contentTypeResolver, + ReactiveAdapterRegistry adapterRegistry) { + + super(messageWriters, contentTypeResolver, adapterRegistry); setOrder(0); } @Override public boolean supports(HandlerResult result) { - ResolvableType returnType = result.getReturnType(); + Class returnType = result.getReturnType().getRawClass(); if (isSupportedType(returnType)) { return true; } - else if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) { - ResolvableType genericType = result.getReturnType().getGeneric(0); - return isSupportedType(genericType); + else { + Optional returnValue = result.getReturnValue(); + ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(returnType, returnValue); + if (adapter != null && !adapter.getDescriptor().isMultiValue()) { + ResolvableType genericType = result.getReturnType().getGeneric(0); + return isSupportedType(genericType.getRawClass()); + } } return false; } - private boolean isSupportedType(ResolvableType returnType) { - Class clazz = returnType.getRawClass(); + private boolean isSupportedType(Class clazz) { return (HttpEntity.class.isAssignableFrom(clazz) && !RequestEntity.class.isAssignableFrom(clazz)); } @@ -101,25 +104,24 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { ResolvableType returnType = result.getReturnType(); - - ResolvableType bodyType; - MethodParameter bodyTypeParameter; + MethodParameter bodyType; Mono returnValueMono; - Optional optional = result.getReturnValue(); + Optional optionalValue = result.getReturnValue(); - if (optional.isPresent() && getConversionService().canConvert(returnType.getRawClass(), Mono.class)) { - returnValueMono = getConversionService().convert(optional.get(), Mono.class); - bodyType = returnType.getGeneric(0, 0); - bodyTypeParameter = new MethodParameter(result.getReturnTypeSource()); - bodyTypeParameter.increaseNestingLevel(); - bodyTypeParameter.increaseNestingLevel(); + Class rawClass = returnType.getRawClass(); + ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(rawClass, optionalValue); + + if (adapter != null) { + returnValueMono = adapter.toMono(optionalValue); + bodyType = new MethodParameter(result.getReturnTypeSource()); + bodyType.increaseNestingLevel(); + bodyType.increaseNestingLevel(); } else { - returnValueMono = Mono.justOrEmpty(optional); - bodyType = returnType.getGeneric(0); - bodyTypeParameter = new MethodParameter(result.getReturnTypeSource()); - bodyTypeParameter.increaseNestingLevel(); + returnValueMono = Mono.justOrEmpty(optionalValue); + bodyType = new MethodParameter(result.getReturnTypeSource()); + bodyType.increaseNestingLevel(); } return returnValueMono.then(returnValue -> { @@ -141,7 +143,7 @@ public class ResponseEntityResultHandler extends AbstractMessageWriterResultHand .forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue())); } - return writeBody(exchange, httpEntity.getBody(), bodyType, bodyTypeParameter); + return writeBody(httpEntity.getBody(), bodyType, exchange); }); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java index 650f5f9804..d1a146c205 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java @@ -31,16 +31,16 @@ import org.springframework.core.Conventions; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.Ordered; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.core.convert.ConversionService; import org.springframework.http.MediaType; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; -import org.springframework.web.reactive.accept.HeaderContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.result.ContentNegotiatingResultHandlerSupport; import org.springframework.web.server.NotAcceptableStatusException; @@ -84,26 +84,28 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler /** - * Constructor with {@code ViewResolver}s and a {@code ConversionService} only - * and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header - * to determine the requested content type. + * Constructor with {@link ViewResolver}s and a {@link RequestedContentTypeResolver}. * @param resolvers the resolver to use - * @param conversionService for converting other reactive types (e.g. rx.Single) to Mono + * @param contentTypeResolver for resolving the requested content type */ - public ViewResolutionResultHandler(List resolvers, ConversionService conversionService) { - this(resolvers, conversionService, new HeaderContentTypeResolver()); + public ViewResolutionResultHandler(List resolvers, + RequestedContentTypeResolver contentTypeResolver) { + + this(resolvers, contentTypeResolver, new ReactiveAdapterRegistry()); } /** * Constructor with {@code ViewResolver}s tand a {@code ConversionService}. * @param resolvers the resolver to use - * @param conversionService for converting other reactive types (e.g. rx.Single) to Mono * @param contentTypeResolver for resolving the requested content type + * @param adapterRegistry for adapting from other reactive types (e.g. + * rx.Single) to Mono */ - public ViewResolutionResultHandler(List resolvers, ConversionService conversionService, - RequestedContentTypeResolver contentTypeResolver) { + public ViewResolutionResultHandler(List resolvers, + RequestedContentTypeResolver contentTypeResolver, + ReactiveAdapterRegistry adapterRegistry) { - super(conversionService, contentTypeResolver); + super(contentTypeResolver, adapterRegistry); this.viewResolvers.addAll(resolvers); AnnotationAwareOrderComparator.sort(this.viewResolvers); } @@ -143,7 +145,7 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler if (isSupportedType(clazz)) { return true; } - if (getConversionService().canConvert(clazz, Mono.class)) { + if (getReactiveAdapterRegistry().getAdapterFrom(clazz, result.getReturnValue()) != null) { clazz = result.getReturnType().getGeneric(0).getRawClass(); return isSupportedType(clazz); } @@ -168,10 +170,12 @@ public class ViewResolutionResultHandler extends ContentNegotiatingResultHandler ResolvableType elementType; ResolvableType returnType = result.getReturnType(); - if (getConversionService().canConvert(returnType.getRawClass(), Mono.class)) { - Optional optionalValue = result.getReturnValue(); + Class rawClass = returnType.getRawClass(); + Optional optionalValue = result.getReturnValue(); + ReactiveAdapter adapter = getReactiveAdapterRegistry().getAdapterFrom(rawClass, optionalValue); + if (adapter != null) { if (optionalValue.isPresent()) { - Mono converted = getConversionService().convert(optionalValue.get(), Mono.class); + Mono converted = adapter.toMono(optionalValue); valueMono = converted.map(o -> o); } else { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java index 5a3b39ae1a..91a1bc2e23 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java @@ -30,7 +30,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.codec.StringEncoder; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpMethod; @@ -43,6 +42,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.reactive.accept.HeaderContentTypeResolver; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; @@ -198,7 +198,7 @@ public class DispatcherHandlerErrorTests { public ResponseBodyResultHandler resultHandler() { return new ResponseBodyResultHandler( Collections.singletonList(new EncoderHttpMessageWriter<>(new StringEncoder())), - new DefaultConversionService()); + new HeaderContentTypeResolver()); } @Bean diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java index 08bc361585..131d039a4c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/ContentNegotiatingResultHandlerSupportTests.java @@ -24,7 +24,6 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; -import org.springframework.core.convert.support.GenericConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.MockServerHttpRequest; @@ -125,7 +124,7 @@ public class ContentNegotiatingResultHandlerSupportTests { } public TestResultHandler(RequestedContentTypeResolver contentTypeResolver) { - super(new GenericConversionService(), contentTypeResolver); + super(contentTypeResolver); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleResultHandlerTests.java index 646956d4ef..af660ebdfb 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleResultHandlerTests.java @@ -27,10 +27,6 @@ import rx.Observable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.web.reactive.HandlerResult; import static org.junit.Assert.assertEquals; @@ -47,10 +43,7 @@ public class SimpleResultHandlerTests { @Before public void setUp() throws Exception { - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - this.resultHandler = new SimpleResultHandler(service); + this.resultHandler = new SimpleResultHandler(); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java index 5b1c273038..76142d278e 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/SimpleUrlHandlerMappingIntegrationTests.java @@ -28,7 +28,6 @@ import reactor.core.publisher.Mono; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; @@ -147,7 +146,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler @Bean public SimpleResultHandler resultHandler() { - return new SimpleResultHandler(new DefaultConversionService()); + return new SimpleResultHandler(); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java index 922d08e6d5..40de5da683 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java @@ -35,12 +35,8 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.StringDecoder; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -50,14 +46,20 @@ import org.springframework.http.converter.reactive.HttpMessageReader; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.ui.ExtendedModelMap; +import org.springframework.validation.Validator; import org.springframework.web.reactive.result.ResolvableMethod; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.junit.Assert.*; -import static org.springframework.core.ResolvableType.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.springframework.core.ResolvableType.forClassWithGenerics; /** * Unit tests for {@link HttpEntityArgumentResolver}.When adding a test also @@ -87,12 +89,7 @@ public class HttpEntityArgumentResolverTests { private HttpEntityArgumentResolver createResolver() { List> readers = new ArrayList<>(); readers.add(new DecoderHttpMessageReader<>(new StringDecoder())); - - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - - return new HttpEntityArgumentResolver(readers, service); + return new HttpEntityArgumentResolver(readers, mock(Validator.class)); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java index 71c6789154..539c3490ae 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java @@ -42,12 +42,8 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.Decoder; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.codec.json.JacksonJsonDecoder; @@ -66,8 +62,12 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.junit.Assert.*; -import static org.springframework.core.ResolvableType.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.core.ResolvableType.forClass; +import static org.springframework.core.ResolvableType.forClassWithGenerics; /** * Unit tests for {@link AbstractMessageReaderArgumentResolver}. @@ -275,15 +275,9 @@ public class MessageReaderArgumentResolverTests { @SuppressWarnings("Convert2MethodRef") private AbstractMessageReaderArgumentResolver resolver(Decoder... decoders) { - List> readers = new ArrayList<>(); Arrays.asList(decoders).forEach(decoder -> readers.add(new DecoderHttpMessageReader<>(decoder))); - - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - - return new AbstractMessageReaderArgumentResolver(readers, service, new TestBeanValidator()) {}; + return new AbstractMessageReaderArgumentResolver(readers, new TestBeanValidator()) {}; } private DataBuffer dataBuffer(String body) { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java index 2fac801f30..f01b6b51e0 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java @@ -41,9 +41,6 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.ByteBufferEncoder; import org.springframework.core.codec.StringEncoder; -import org.springframework.core.convert.support.GenericConversionService; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.buffer.support.DataBufferTestUtils; @@ -64,9 +61,11 @@ import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.junit.Assert.*; -import static org.springframework.http.MediaType.*; -import static org.springframework.web.reactive.HandlerMapping.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8; +import static org.springframework.web.reactive.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE; /** * Unit tests for {@link AbstractMessageWriterResultHandler}. @@ -93,7 +92,7 @@ public class MessageWriterResultHandlerTests { public void useDefaultContentType() throws Exception { Resource body = new ClassPathResource("logo.png", getClass()); ResolvableType type = ResolvableType.forType(Resource.class); - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertEquals("image/x-png", this.response.getHeaders().getFirst("Content-Type")); } @@ -105,7 +104,7 @@ public class MessageWriterResultHandlerTests { String body = "foo"; ResolvableType type = ResolvableType.forType(String.class); - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType()); } @@ -119,7 +118,7 @@ public class MessageWriterResultHandlerTests { } private void testVoidReturnType(Object body, ResolvableType type) { - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertNull(this.response.getHeaders().get("Content-Type")); assertNull(this.response.getBody()); @@ -131,7 +130,7 @@ public class MessageWriterResultHandlerTests { ResolvableType type = ResolvableType.forType(OutputStream.class); HttpMessageWriter writer = new EncoderHttpMessageWriter<>(new ByteBufferEncoder()); - Mono mono = createResultHandler(writer).writeBody(this.exchange, body, type, returnType(type)); + Mono mono = createResultHandler(writer).writeBody(body, returnType(type), this.exchange); TestSubscriber.subscribe(mono).assertError(IllegalStateException.class); } @@ -140,7 +139,7 @@ public class MessageWriterResultHandlerTests { public void jacksonTypeOfListElement() throws Exception { List body = Arrays.asList(new Foo("foo"), new Bar("bar")); ResolvableType type = ResolvableType.forClassWithGenerics(List.class, ParentClass.class); - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType()); assertResponseBody("[{\"type\":\"foo\",\"parentProperty\":\"foo\"}," + @@ -151,7 +150,7 @@ public class MessageWriterResultHandlerTests { public void jacksonTypeWithSubType() throws Exception { SimpleBean body = new SimpleBean(123L, "foo"); ResolvableType type = ResolvableType.forClass(Identifiable.class); - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType()); assertResponseBody("{\"id\":123,\"name\":\"foo\"}"); @@ -161,7 +160,7 @@ public class MessageWriterResultHandlerTests { public void jacksonTypeWithSubTypeOfListElement() throws Exception { List body = Arrays.asList(new SimpleBean(123L, "foo"), new SimpleBean(456L, "bar")); ResolvableType type = ResolvableType.forClassWithGenerics(List.class, Identifiable.class); - this.resultHandler.writeBody(this.exchange, body, type, returnType(type)).block(Duration.ofSeconds(5)); + this.resultHandler.writeBody(body, returnType(type), this.exchange).block(Duration.ofSeconds(5)); assertEquals(APPLICATION_JSON_UTF8, this.response.getHeaders().getContentType()); assertResponseBody("[{\"id\":123,\"name\":\"foo\"},{\"id\":456,\"name\":\"bar\"}]"); @@ -185,14 +184,8 @@ public class MessageWriterResultHandlerTests { else { writerList = Arrays.asList(writers); } - - GenericConversionService service = new GenericConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build(); - - return new AbstractMessageWriterResultHandler(writerList, service, resolver) {}; + return new AbstractMessageWriterResultHandler(writerList, resolver) {}; } private void assertResponseBody(String responseBody) { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java index 3ce8f43ba9..1fd74451f9 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java @@ -18,7 +18,6 @@ package org.springframework.web.reactive.result.method.annotation; import java.net.URI; import java.nio.ByteBuffer; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; @@ -38,18 +37,15 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.StringDecoder; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.converter.reactive.DecoderHttpMessageReader; import org.springframework.http.converter.reactive.HttpMessageReader; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.ui.ExtendedModelMap; +import org.springframework.validation.Validator; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.reactive.result.ResolvableMethod; import org.springframework.web.server.ServerWebExchange; @@ -62,6 +58,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import static org.springframework.core.ResolvableType.forClass; import static org.springframework.core.ResolvableType.forClassWithGenerics; @@ -93,12 +90,7 @@ public class RequestBodyArgumentResolverTests { private RequestBodyArgumentResolver resolver() { List> readers = new ArrayList<>(); readers.add(new DecoderHttpMessageReader<>(new StringDecoder())); - - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - - return new RequestBodyArgumentResolver(readers, service); + return new RequestBodyArgumentResolver(readers, mock(Validator.class)); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java index 52ce511667..32abc9d5dd 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java @@ -27,11 +27,6 @@ import reactor.core.publisher.Mono; import org.springframework.core.codec.ByteBufferEncoder; import org.springframework.core.codec.StringEncoder; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.codec.json.JacksonJsonEncoder; @@ -99,12 +94,8 @@ public class ResponseBodyResultHandlerTests { else { writerList = Arrays.asList(writers); } - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build(); - - return new ResponseBodyResultHandler(writerList, new DefaultConversionService(), resolver); + return new ResponseBodyResultHandler(writerList, resolver); } @Test diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java index a62d9a3f4b..0034b3050c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java @@ -34,11 +34,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.codec.ByteBufferEncoder; import org.springframework.core.codec.StringEncoder; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.buffer.support.DataBufferTestUtils; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -59,8 +55,11 @@ import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.junit.Assert.*; -import static org.springframework.core.ResolvableType.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.core.ResolvableType.forClassWithGenerics; /** * Unit tests for {@link ResponseEntityResultHandler}. When adding a test also @@ -100,14 +99,8 @@ public class ResponseEntityResultHandlerTests { else { writerList = Arrays.asList(writers); } - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); - - RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build(); - - return new ResponseEntityResultHandler(writerList, service, resolver); + return new ResponseEntityResultHandler(writerList, resolver); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java index ad5d15b838..4b86da852e 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java @@ -38,13 +38,9 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; -import org.springframework.core.convert.support.MonoToCompletableFutureConverter; -import org.springframework.core.convert.support.ReactorToRxJava1Converter; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.core.io.buffer.support.DataBufferTestUtils; -import org.springframework.format.support.DefaultFormattingConversionService; -import org.springframework.format.support.FormattingConversionService; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.MockServerHttpRequest; @@ -55,6 +51,8 @@ import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.accept.HeaderContentTypeResolver; +import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.result.ResolvableMethod; import org.springframework.web.server.NotAcceptableStatusException; import org.springframework.web.server.ServerWebExchange; @@ -62,9 +60,10 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.DefaultWebSessionManager; import org.springframework.web.server.session.WebSessionManager; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; -import static org.springframework.http.MediaType.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.springframework.http.MediaType.APPLICATION_JSON; /** * Unit tests for {@link ViewResolutionResultHandler}. @@ -247,12 +246,9 @@ public class ViewResolutionResultHandlerTests { } private ViewResolutionResultHandler createResultHandler(List defaultViews, ViewResolver... resolvers) { - FormattingConversionService service = new DefaultFormattingConversionService(); - service.addConverter(new MonoToCompletableFutureConverter()); - service.addConverter(new ReactorToRxJava1Converter()); List resolverList = Arrays.asList(resolvers); - - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolverList, service); + RequestedContentTypeResolver contentTypeResolver = new HeaderContentTypeResolver(); + ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolverList, contentTypeResolver); handler.setDefaultViews(defaultViews); return handler; }