Polish "simple" handler support

- correct name of HttpHandlerHandlerAdapter to WebHandlerHandlerAdapter
- shorten SimpleHandlerResultHandler to SimpleResultHandler
- add HandlerResult constructor without Model
- update tests
This commit is contained in:
Rossen Stoyanchev 2016-04-14 14:50:59 -04:00
parent ff6b639cf9
commit 3460e577ad
6 changed files with 65 additions and 44 deletions

View File

@ -44,6 +44,16 @@ public class HandlerResult {
private Function<Throwable, Mono<HandlerResult>> exceptionHandler; private Function<Throwable, Mono<HandlerResult>> exceptionHandler;
/**
* Create a new {@code HandlerResult}.
* @param handler the handler that handled the request
* @param returnValue the return value from the handler possibly {@code null}
* @param returnValueType the return value type
*/
public HandlerResult(Object handler, Object returnValue, ResolvableType returnValueType) {
this(handler, returnValue, returnValueType, null);
}
/** /**
* Create a new {@code HandlerResult}. * Create a new {@code HandlerResult}.
* @param handler the handler that handled the request * @param handler the handler that handled the request
@ -54,11 +64,10 @@ public class HandlerResult {
public HandlerResult(Object handler, Object returnValue, ResolvableType returnValueType, ModelMap model) { public HandlerResult(Object handler, Object returnValue, ResolvableType returnValueType, ModelMap model) {
Assert.notNull(handler, "'handler' is required"); Assert.notNull(handler, "'handler' is required");
Assert.notNull(returnValueType, "'returnValueType' is required"); Assert.notNull(returnValueType, "'returnValueType' is required");
Assert.notNull(model, "'model' is required");
this.handler = handler; this.handler = handler;
this.returnValue = Optional.ofNullable(returnValue); this.returnValue = Optional.ofNullable(returnValue);
this.returnValueType = returnValueType; this.returnValueType = returnValueType;
this.model = model; this.model = (model != null ? model : new ExtendedModelMap());
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,24 +29,28 @@ import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler; import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* Supports {@link HandlerResult} with a {@code void} or {@code Publisher<Void>} value. * A simple handler for return values of type {@code void}, or
* An optional {link ConversionService} can be used to support types that can be converted to * {@code Publisher<Void>}, or if a {link ConversionService} is provided, also
* {@code Publisher<Void>}, like {@code Observable<Void>} or {@code CompletableFuture<Void>}. * of any other async return value types that can be converted to
* {@code Publisher<Void>} such as {@code Observable<Void>} or
* {@code CompletableFuture<Void>}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Rossen Stoyanchev
*/ */
public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler { public class SimpleResultHandler implements Ordered, HandlerResultHandler {
private int order = Ordered.LOWEST_PRECEDENCE; private int order = Ordered.LOWEST_PRECEDENCE;
private ConversionService conversionService; private ConversionService conversionService;
public SimpleHandlerResultHandler() { public SimpleResultHandler() {
} }
public SimpleHandlerResultHandler(ConversionService conversionService) { public SimpleResultHandler(ConversionService conversionService) {
Assert.notNull(conversionService, "'conversionService' is required."); Assert.notNull(conversionService, "'conversionService' is required.");
this.conversionService = conversionService; this.conversionService = conversionService;
} }
@ -61,30 +65,36 @@ public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler
return this.order; return this.order;
} }
@Override @Override
public boolean supports(HandlerResult result) { public boolean supports(HandlerResult result) {
ResolvableType type = result.getReturnValueType(); ResolvableType type = result.getReturnValueType();
return (type != null && Void.TYPE.equals(type.getRawClass()) || return (type != null && (Void.TYPE.equals(type.getRawClass()) || isConvertibleToVoidPublisher(type)));
(isConvertibleToPublisher(type) && Void.class.isAssignableFrom(type.getGeneric(0).getRawClass()))); }
private boolean isConvertibleToVoidPublisher(ResolvableType type) {
return (isConvertibleToPublisher(type) &&
Void.class.isAssignableFrom(type.getGeneric(0).getRawClass()));
} }
private boolean isConvertibleToPublisher(ResolvableType type) { private boolean isConvertibleToPublisher(ResolvableType type) {
return Publisher.class.isAssignableFrom(type.getRawClass()) || Class<?> clazz = type.getRawClass();
((this.conversionService != null) && return (Publisher.class.isAssignableFrom(clazz) ||
this.conversionService.canConvert(type.getRawClass(), Publisher.class)); ((this.conversionService != null) && this.conversionService.canConvert(clazz, Publisher.class)));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Optional<Object> value = result.getReturnValue(); Optional<Object> optional = result.getReturnValue();
if (!value.isPresent() || Void.TYPE.equals(result.getReturnValueType().getRawClass())) { if (!optional.isPresent()) {
return Mono.empty(); return Mono.empty();
} }
if (value.get() instanceof Mono) { Object returnValue = optional.get();
return (Mono<Void>) value.get(); if (returnValue instanceof Mono) {
return (Mono<Void>) returnValue;
} }
return Mono.from(this.conversionService.convert(value.get(), Publisher.class)); return Mono.from(this.conversionService.convert(returnValue, Publisher.class));
} }
} }

View File

@ -29,13 +29,12 @@ import org.springframework.web.server.WebHandler;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* Support use of {@link org.springframework.web.server.WebHandler} through the * Adapter to use a {@link WebHandler} through the {@link DispatcherHandler}.
* {@link DispatcherHandler}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sebastien Deleuze * @author Sebastien Deleuze
*/ */
public class HttpHandlerHandlerAdapter implements HandlerAdapter { public class WebHandlerHandlerAdapter implements HandlerAdapter {
private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics( private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics(
Publisher.class, Void.class); Publisher.class, Void.class);
@ -49,9 +48,8 @@ public class HttpHandlerHandlerAdapter implements HandlerAdapter {
@Override @Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) { public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler; WebHandler webHandler = (WebHandler) handler;
Mono<Void> completion = webHandler.handle(exchange); Mono<Void> mono = webHandler.handle(exchange);
ModelMap model = new ExtendedModelMap(); return Mono.just(new HandlerResult(webHandler, mono, PUBLISHER_VOID));
return Mono.just(new HandlerResult(webHandler, completion, PUBLISHER_VOID, model));
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,7 +27,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter; import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter; import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResult;
@ -37,12 +36,12 @@ import static org.junit.Assert.assertTrue;
/** /**
* @author Sebastien Deleuze * @author Sebastien Deleuze
*/ */
public class SimpleHandlerResultHandlerTests { public class SimpleResultHandlerTests {
@Test @Test
public void supports() throws NoSuchMethodException { public void supports() throws NoSuchMethodException {
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler(); SimpleResultHandler resultHandler = new SimpleResultHandler();
TestController controller = new TestController(); TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue")); HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue"));
@ -77,7 +76,7 @@ public class SimpleHandlerResultHandlerTests {
GenericConversionService conversionService = new GenericConversionService(); GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(new ReactiveStreamsToCompletableFutureConverter()); conversionService.addConverter(new ReactiveStreamsToCompletableFutureConverter());
conversionService.addConverter(new ReactiveStreamsToRxJava1Converter()); conversionService.addConverter(new ReactiveStreamsToRxJava1Converter());
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler(conversionService); SimpleResultHandler resultHandler = new SimpleResultHandler(conversionService);
TestController controller = new TestController(); TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue")); HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue"));
@ -106,7 +105,7 @@ public class SimpleHandlerResultHandlerTests {
} }
private HandlerResult createHandlerResult(HandlerMethod hm, ResolvableType type) { private HandlerResult createHandlerResult(HandlerMethod hm, ResolvableType type) {
return new HandlerResult(hm, null, type, new ExtendedModelMap()); return new HandlerResult(hm, null, type);
} }

View File

@ -47,9 +47,11 @@ import static org.junit.Assert.assertEquals;
/** /**
* Integration tests with simple WebHandler's processing requests.
*
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests { public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTests {
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
@ -59,14 +61,14 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
StaticApplicationContext wac = new StaticApplicationContext(); StaticApplicationContext wac = new StaticApplicationContext();
wac.registerSingleton("hm", TestHandlerMapping.class); wac.registerSingleton("hm", TestHandlerMapping.class);
wac.registerSingleton("ha", HttpHandlerHandlerAdapter.class); wac.registerSingleton("ha", WebHandlerHandlerAdapter.class);
wac.registerSingleton("rh", SimpleHandlerResultHandler.class); wac.registerSingleton("rh", SimpleResultHandler.class);
wac.refresh(); wac.refresh();
DispatcherHandler webHandler = new DispatcherHandler(); DispatcherHandler dispatcherHandler = new DispatcherHandler();
webHandler.setApplicationContext(wac); dispatcherHandler.setApplicationContext(wac);
return WebHttpHandlerBuilder.webHandler(webHandler) return WebHttpHandlerBuilder.webHandler(dispatcherHandler)
.exceptionHandlers(new ResponseStatusExceptionHandler()) .exceptionHandlers(new ResponseStatusExceptionHandler())
.build(); .build();
} }
@ -137,12 +139,16 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
} }
} }
private static DataBuffer asDataBuffer(String text) {
return new DefaultDataBufferAllocator().allocateBuffer().write(text.getBytes(StandardCharsets.UTF_8));
}
private static class FooHandler implements WebHandler { private static class FooHandler implements WebHandler {
@Override @Override
public Mono<Void> handle(ServerWebExchange exchange) { public Mono<Void> handle(ServerWebExchange exchange) {
DataBuffer buffer = new DefaultDataBufferAllocator().allocateBuffer() DataBuffer buffer = asDataBuffer("foo");
.write("foo".getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().setBody(Flux.just(buffer)); return exchange.getResponse().setBody(Flux.just(buffer));
} }
} }
@ -151,8 +157,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
@Override @Override
public Mono<Void> handle(ServerWebExchange exchange) { public Mono<Void> handle(ServerWebExchange exchange) {
DataBuffer buffer = new DefaultDataBufferAllocator().allocateBuffer() DataBuffer buffer = asDataBuffer("bar");
.write("bar".getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().setBody(Flux.just(buffer)); return exchange.getResponse().setBody(Flux.just(buffer));
} }
} }

View File

@ -67,7 +67,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.ViewResolver; import org.springframework.web.reactive.ViewResolver;
import org.springframework.web.reactive.handler.SimpleHandlerResultHandler; import org.springframework.web.reactive.handler.SimpleResultHandler;
import org.springframework.web.reactive.view.ViewResolverResultHandler; import org.springframework.web.reactive.view.ViewResolverResultHandler;
import org.springframework.web.reactive.view.freemarker.FreeMarkerConfigurer; import org.springframework.web.reactive.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.reactive.view.freemarker.FreeMarkerViewResolver; import org.springframework.web.reactive.view.freemarker.FreeMarkerViewResolver;
@ -421,8 +421,8 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
} }
@Bean @Bean
public SimpleHandlerResultHandler simpleHandlerResultHandler() { public SimpleResultHandler simpleHandlerResultHandler() {
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler(conversionService()); SimpleResultHandler resultHandler = new SimpleResultHandler(conversionService());
resultHandler.setOrder(2); resultHandler.setOrder(2);
return resultHandler; return resultHandler;
} }