diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/DispatcherHandler.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/DispatcherHandler.java index 3393e3d463..de38f689aa 100644 --- a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/DispatcherHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/DispatcherHandler.java @@ -91,9 +91,9 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware { } HandlerAdapter handlerAdapter = getHandlerAdapter(handler); - Publisher resultPublisher = handlerAdapter.handle(request, response, handler); - return Streams.wrap(resultPublisher).concatMap((HandlerResult result) -> { + try { + HandlerResult result = handlerAdapter.handle(request, response, handler); for (HandlerResultHandler resultHandler : resultHandlers) { if (resultHandler.supports(result)) { return resultHandler.handleResult(request, response, result); @@ -101,7 +101,11 @@ public class DispatcherHandler implements HttpHandler, ApplicationContextAware { } return Streams.fail(new IllegalStateException( "No HandlerResultHandler for " + result.getValue())); - }); + } + catch(Exception ex) { + return Streams.fail(ex); + } + } protected Object getHandler(ServerHttpRequest request) { diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/HandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/HandlerAdapter.java index 49f778540a..94e8355e47 100644 --- a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/HandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/HandlerAdapter.java @@ -15,8 +15,6 @@ */ package org.springframework.reactive.web.dispatch; -import org.reactivestreams.Publisher; - import org.springframework.reactive.web.http.ServerHttpRequest; import org.springframework.reactive.web.http.ServerHttpResponse; @@ -27,6 +25,6 @@ public interface HandlerAdapter { boolean supports(Object handler); - Publisher handle(ServerHttpRequest request, ServerHttpResponse response, Object handler); + HandlerResult handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) throws Exception; } diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/SimpleHandlerResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/SimpleHandlerResultHandler.java new file mode 100644 index 0000000000..c26000c394 --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/SimpleHandlerResultHandler.java @@ -0,0 +1,49 @@ +/* + * 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.reactive.web.dispatch; + +import org.reactivestreams.Publisher; + +import org.springframework.core.Ordered; +import org.springframework.reactive.web.http.ServerHttpRequest; +import org.springframework.reactive.web.http.ServerHttpResponse; + +/** + * Supports {@link HandlerResult} with a {@code Publisher} value. + * + * @author Sebastien Deleuze + */ +public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler { + + private int order = Ordered.LOWEST_PRECEDENCE; + + @Override + public int getOrder() { + return this.order; + } + + @Override + public boolean supports(HandlerResult result) { + Object value = result.getValue(); + return value != null && Publisher.class.isAssignableFrom(value.getClass()); + } + + @Override + public Publisher handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) { + return (Publisher)result.getValue(); + } +} diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/handler/HttpHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/handler/HttpHandlerAdapter.java index e6a05cbe1c..0dc2e9463d 100644 --- a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/handler/HttpHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/handler/HttpHandlerAdapter.java @@ -16,7 +16,6 @@ package org.springframework.reactive.web.dispatch.handler; import org.reactivestreams.Publisher; -import reactor.rx.Streams; import org.springframework.reactive.web.dispatch.HandlerAdapter; import org.springframework.reactive.web.dispatch.HandlerResult; @@ -34,6 +33,7 @@ import org.springframework.reactive.web.http.ServerHttpResponse; * handler mappings. * * @author Rossen Stoyanchev + * @author Sebastien Deleuze */ public class HttpHandlerAdapter implements HandlerAdapter { @@ -44,10 +44,10 @@ public class HttpHandlerAdapter implements HandlerAdapter { } @Override - public Publisher handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) { - HttpHandler httpHandler = (HttpHandler) handler; - Publisher publisher = httpHandler.handle(request, response); - return Streams.wrap(publisher).map(aVoid -> null); + public HandlerResult handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) { + HttpHandler httpHandler = (HttpHandler)handler; + Publisher completion = httpHandler.handle(request, response); + return new HandlerResult(httpHandler, completion); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/RequestMappingHandlerAdapter.java index 5fcab45fd2..e2d1747200 100644 --- a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/RequestMappingHandlerAdapter.java @@ -19,9 +19,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.reactivestreams.Publisher; -import reactor.rx.Streams; - import org.springframework.beans.factory.InitializingBean; import org.springframework.reactive.codec.decoder.JacksonJsonDecoder; import org.springframework.reactive.codec.decoder.JsonObjectDecoder; @@ -64,21 +61,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin } @Override - public Publisher handle(ServerHttpRequest request, ServerHttpResponse response, - Object handler) { + public HandlerResult handle(ServerHttpRequest request, ServerHttpResponse response, + Object handler) throws Exception { final InvocableHandlerMethod invocable = new InvocableHandlerMethod((HandlerMethod) handler); invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers); - Object result; - try { - result = invocable.invokeForRequest(request); - } - catch (Exception ex) { - return Streams.fail(ex); - } + Object result = invocable.invokeForRequest(request); - return Streams.just(new HandlerResult(invocable, result)); + return new HandlerResult(invocable, result); } } diff --git a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandler.java index 8109c3f24f..00ccc67ace 100644 --- a/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandler.java @@ -16,6 +16,7 @@ package org.springframework.reactive.web.dispatch.method.annotation; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; @@ -25,7 +26,6 @@ import java.util.concurrent.CompletableFuture; import org.reactivestreams.Publisher; import reactor.rx.Promise; -import reactor.rx.Stream; import reactor.rx.Streams; import rx.Observable; import rx.RxReactiveStreams; @@ -33,6 +33,7 @@ import rx.Single; import org.springframework.core.MethodParameter; import org.springframework.core.Ordered; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpHeaders; @@ -60,7 +61,7 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered private final List> serializers; private final List> postProcessors; - private int order = Ordered.LOWEST_PRECEDENCE; + private int order = 0; public ResponseBodyResultHandler(List> serializers) { @@ -86,8 +87,10 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered public boolean supports(HandlerResult result) { Object handler = result.getHandler(); if (handler instanceof HandlerMethod) { - Method method = ((HandlerMethod) handler).getMethod(); - return AnnotatedElementUtils.isAnnotated(method, ResponseBody.class.getName()); + HandlerMethod handlerMethod = (HandlerMethod) handler; + Type publisherVoidType = new ParameterizedTypeReference>(){}.getType(); + return AnnotatedElementUtils.isAnnotated(handlerMethod.getMethod(), ResponseBody.class.getName()) && + !handlerMethod.getReturnType().getGenericParameterType().equals(publisherVoidType); } return false; } diff --git a/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/handler/SimpleUrlHandlerMappingIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/handler/SimpleUrlHandlerMappingIntegrationTests.java index b9700a02f1..de986c811f 100644 --- a/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/handler/SimpleUrlHandlerMappingIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/handler/SimpleUrlHandlerMappingIntegrationTests.java @@ -28,6 +28,7 @@ import reactor.rx.Streams; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.reactive.web.dispatch.DispatcherHandler; +import org.springframework.reactive.web.dispatch.SimpleHandlerResultHandler; import org.springframework.reactive.web.http.AbstractHttpHandlerIntegrationTests; import org.springframework.reactive.web.http.HttpHandler; import org.springframework.reactive.web.http.ServerHttpRequest; @@ -52,6 +53,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler StaticWebApplicationContext wac = new StaticWebApplicationContext(); wac.registerSingleton("hm", TestHandlerMapping.class); wac.registerSingleton("ha", HttpHandlerAdapter.class); + wac.registerSingleton("hhrh", SimpleHandlerResultHandler.class); wac.refresh(); DispatcherHandler dispatcherHandler = new DispatcherHandler(); diff --git a/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandlerTests.java new file mode 100644 index 0000000000..b4500ceed6 --- /dev/null +++ b/spring-web-reactive/src/test/java/org/springframework/reactive/web/dispatch/method/annotation/ResponseBodyResultHandlerTests.java @@ -0,0 +1,68 @@ +/* + * 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.reactive.web.dispatch.method.annotation; + +import java.util.Collections; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.reactivestreams.Publisher; + +import org.springframework.reactive.web.dispatch.HandlerResult; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.method.HandlerMethod; + +/** + * @author Sebastien Deleuze + */ +public class ResponseBodyResultHandlerTests { + + @Test + public void supports() throws NoSuchMethodException { + ResponseBodyResultHandler resultHandler = new ResponseBodyResultHandler(Collections.emptyList()); + TestController controller = new TestController(); + + HandlerMethod notAnnotatedMethod = new HandlerMethod(controller, TestController.class.getMethod("notAnnotated")); + assertFalse(resultHandler.supports(new HandlerResult(notAnnotatedMethod, null))); + + HandlerMethod publisherStringMethod = new HandlerMethod(controller, TestController.class.getMethod("publisherString")); + assertTrue(resultHandler.supports(new HandlerResult(publisherStringMethod, null))); + + HandlerMethod publisherVoidMethod = new HandlerMethod(controller, TestController.class.getMethod("publisherVoid")); + assertFalse(resultHandler.supports(new HandlerResult(publisherVoidMethod, null))); + } + + + private static class TestController { + + public Publisher notAnnotated() { + return null; + } + + @ResponseBody + public Publisher publisherString() { + return null; + } + + @ResponseBody + public Publisher publisherVoid() { + return null; + } + } + +}