Refactor reactive type conversion support

This commit replaces Reactive Streams converters for RxJava1 and
CompletableFuture with Reactor specific ones. The results in conversion
that preserves stream semantics, i.e. Mono vs Flux.

For example this is allowed:
Flux -> Observable
Mono -> Single
Mono -> CompletableFuture

This is not allowed:
Flux -> Single
Mono -> Observable
Flux -> CompletableFuture

As a result it is now possible to check through the ConversionService
if a target type to convert to is a stream of many or of one which is
useful for decoding purposes.

The commit also adds PublisherToFluxConverter to allow conversion from
raw Publisher to Flux. The reverse is not necessary since Flux is a
Publisher and it's a no-op conversion.
This commit is contained in:
Sebastien Deleuze
2016-07-01 16:59:50 +02:00
committed by Rossen Stoyanchev
parent 952395192f
commit 12d7b78169
10 changed files with 108 additions and 48 deletions

View File

@@ -24,13 +24,12 @@ import reactor.core.publisher.Flux;
import rx.Observable;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
import org.springframework.core.convert.support.PublisherToFluxConverter;
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.result.SimpleResultHandler;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -46,8 +45,9 @@ public class SimpleResultHandlerTests {
public void supportsWithConversionService() throws NoSuchMethodException {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(new ReactiveStreamsToCompletableFutureConverter());
conversionService.addConverter(new ReactiveStreamsToRxJava1Converter());
conversionService.addConverter(new MonoToCompletableFutureConverter());
conversionService.addConverter(new PublisherToFluxConverter());
conversionService.addConverter(new ReactorToRxJava1Converter());
SimpleResultHandler resultHandler = new SimpleResultHandler(conversionService);
TestController controller = new TestController();

View File

@@ -40,8 +40,9 @@ 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.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
import org.springframework.core.convert.support.PublisherToFluxConverter;
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;
@@ -136,7 +137,6 @@ public class MessageConverterResultHandlerTests {
}
@Test // SPR-12811
@Ignore
public void jacksonTypeOfListElement() throws Exception {
List<ParentClass> body = Arrays.asList(new Foo("foo"), new Bar("bar"));
ResolvableType bodyType = ResolvableType.forClassWithGenerics(List.class, ParentClass.class);
@@ -185,8 +185,9 @@ public class MessageConverterResultHandlerTests {
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
service.addConverter(new MonoToCompletableFutureConverter());
service.addConverter(new PublisherToFluxConverter());
service.addConverter(new ReactorToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();

View File

@@ -30,7 +30,6 @@ import java.util.concurrent.CompletableFuture;
import javax.xml.bind.annotation.XmlRootElement;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -43,11 +42,12 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.codec.Decoder;
import org.springframework.core.convert.support.PublisherToFluxConverter;
import org.springframework.http.codec.json.JacksonJsonDecoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
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.http.HttpMethod;
@@ -169,10 +169,7 @@ public class RequestBodyArgumentResolverTests {
assertEquals(map, resolveValue("map", Map.class, body));
}
// TODO: @Ignore
@Test
@Ignore
public void list() throws Exception {
String body = "[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]";
assertEquals(Arrays.asList(new TestBean("f1", "b1"), new TestBean("f2", "b2")),
@@ -180,7 +177,6 @@ public class RequestBodyArgumentResolverTests {
}
@Test
@Ignore
public void array() throws Exception {
String body = "[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]";
assertArrayEquals(new TestBean[] {new TestBean("f1", "b1"), new TestBean("f2", "b2")},
@@ -220,8 +216,9 @@ public class RequestBodyArgumentResolverTests {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
Arrays.asList(decoders).forEach(decoder -> converters.add(new CodecHttpMessageConverter<>(decoder)));
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
service.addConverter(new MonoToCompletableFutureConverter());
service.addConverter(new PublisherToFluxConverter());
service.addConverter(new ReactorToRxJava1Converter());
return new RequestBodyArgumentResolver(converters, service, new TestBeanValidator());
}

View File

@@ -30,8 +30,9 @@ 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.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
import org.springframework.core.convert.support.PublisherToFluxConverter;
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.json.JacksonJsonEncoder;
@@ -100,8 +101,9 @@ public class ResponseBodyResultHandlerTests {
converterList = Arrays.asList(converters);
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
service.addConverter(new MonoToCompletableFutureConverter());
service.addConverter(new PublisherToFluxConverter());
service.addConverter(new ReactorToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new ResponseBodyResultHandler(converterList, new DefaultConversionService(), resolver);

View File

@@ -33,8 +33,9 @@ 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.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.convert.support.MonoToCompletableFutureConverter;
import org.springframework.core.convert.support.PublisherToFluxConverter;
import org.springframework.core.convert.support.ReactorToRxJava1Converter;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -105,8 +106,11 @@ public class ResponseEntityResultHandlerTests {
converterList = Arrays.asList(converters);
}
GenericConversionService service = new GenericConversionService();
service.addConverter(new ReactiveStreamsToCompletableFutureConverter());
service.addConverter(new ReactiveStreamsToRxJava1Converter());
service.addConverter(new MonoToCompletableFutureConverter());
service.addConverter(new PublisherToFluxConverter());
service.addConverter(new ReactorToRxJava1Converter());
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
return new ResponseEntityResultHandler(converterList, service, resolver);

View File

@@ -39,7 +39,8 @@ import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.core.convert.support.PublisherToFluxConverter;
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;
@@ -252,7 +253,8 @@ public class ViewResolutionResultHandlerTests {
private ViewResolutionResultHandler createResultHandler(List<View> defaultViews, ViewResolver... resolvers) {
ConfigurableConversionService service = new DefaultConversionService();
service.addConverter(new ReactiveStreamsToRxJava1Converter());
service.addConverter(new ReactorToRxJava1Converter());
service.addConverter(new PublisherToFluxConverter());
List<ViewResolver> resolverList = Arrays.asList(resolvers);
ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolverList, service);
handler.setDefaultViews(defaultViews);