From 210e10c6576ac87906d1a1f8ae037df72dfd2a9c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 16 Jun 2015 08:40:46 -0400 Subject: [PATCH] Add AsyncHandlerMethodReturnValueHandler Before this change HandlerMethodReturnValueHandler's were invoked in a specific order (type-based, annotation-based, custom). However handlers that deal with asynchronous return value handling need to always be considered first. This affects custom handlers in particular since they are normally ordered last. This change introduces an AsyncHandlerMethodReturnValueHandler sub-interface with a single method to determine if the return value is asynchronous and if it is to look for a matching handler only among those that are of type AsyncHandlerMethodReturnValueHandler. Issue: SPR-13083 --- .../AsyncHandlerMethodReturnValueHandler.java | 50 ++++++++++ ...dlerMethodReturnValueHandlerComposite.java | 44 ++++++--- ...ethodReturnValueHandlerCompositeTests.java | 96 +++++++++++++------ .../support/StubReturnValueHandler.java | 52 ---------- .../AsyncTaskMethodReturnValueHandler.java | 11 ++- .../CallableMethodReturnValueHandler.java | 10 +- .../CompletionStageReturnValueHandler.java | 10 +- ...eferredResultMethodReturnValueHandler.java | 10 +- .../ListenableFutureReturnValueHandler.java | 11 ++- ...ResponseBodyEmitterReturnValueHandler.java | 18 +++- 10 files changed, 203 insertions(+), 109 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/web/method/support/AsyncHandlerMethodReturnValueHandler.java delete mode 100644 spring-web/src/test/java/org/springframework/web/method/support/StubReturnValueHandler.java diff --git a/spring-web/src/main/java/org/springframework/web/method/support/AsyncHandlerMethodReturnValueHandler.java b/spring-web/src/main/java/org/springframework/web/method/support/AsyncHandlerMethodReturnValueHandler.java new file mode 100644 index 00000000000..440230ccfe2 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/method/support/AsyncHandlerMethodReturnValueHandler.java @@ -0,0 +1,50 @@ +/* + * 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.web.method.support; + +import org.springframework.core.MethodParameter; + +/** + * A {@link HandlerMethodReturnValueHandler} that handles return values that + * represent asynchronous computation. Such handlers need to be invoked with + * precedence over other handlers that might otherwise match the return value + * type -- e.g. a method that returns a Promise type that is also annotated with + * {@code @ResponseBody}. + * + *

In {@link #handleReturnValue}, implementations of this class should create + * a {@link org.springframework.web.context.request.async.DeferredResult} or + * adapt to it and then invoke {@code WebAsyncManager} to start async processing. + * For example: + *

+ * DeferredResult deferredResult = (DeferredResult) returnValue;
+ * WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);
+ * 
+ * the return value to a DeferredResult + * + * @author Rossen Stoyanchev + * @since 4.2 + */ +public interface AsyncHandlerMethodReturnValueHandler extends HandlerMethodReturnValueHandler { + + /** + * Whether the given return value represents asynchronous computation. + * @param returnValue the return value + * @param returnType the return type + * @return {@code true} if the return value is asynchronous. + */ + boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType); + +} diff --git a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java index 5f57fcda2bc..36d2c25d887 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java @@ -34,7 +34,7 @@ import org.springframework.web.context.request.NativeWebRequest; * @author Rossen Stoyanchev * @since 3.1 */ -public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { +public class HandlerMethodReturnValueHandlerComposite implements AsyncHandlerMethodReturnValueHandler { protected final Log logger = LogFactory.getLog(getClass()); @@ -58,6 +58,15 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe return getReturnValueHandler(returnType) != null; } + private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { + for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { + if (handler.supportsReturnType(returnType)) { + return handler; + } + } + return null; + } + /** * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found. @@ -66,32 +75,41 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { - HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); + HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } - /** - * Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type. - */ - private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { - for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) { - if (logger.isTraceEnabled()) { - logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + - returnType.getGenericParameterType() + "]"); + private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) { + boolean isAsyncValue = isAsyncReturnValue(value, returnType); + for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { + if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { + continue; } - if (returnValueHandler.supportsReturnType(returnType)) { - return returnValueHandler; + if (handler.supportsReturnType(returnType)) { + return handler; } } return null; } + @Override + public boolean isAsyncReturnValue(Object value, MethodParameter returnType) { + for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { + if (handler instanceof AsyncHandlerMethodReturnValueHandler) { + if (((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) { + return true; + } + } + } + return false; + } + /** * Add the given {@link HandlerMethodReturnValueHandler}. */ public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) { - returnValueHandlers.add(handler); + this.returnValueHandlers.add(handler); return this; } diff --git a/spring-web/src/test/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerCompositeTests.java b/spring-web/src/test/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerCompositeTests.java index 5fe5eaef0c8..06f46b227f2 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerCompositeTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerCompositeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -22,76 +22,114 @@ import org.junit.Test; import org.springframework.core.MethodParameter; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Test fixture with {@link HandlerMethodReturnValueHandlerComposite}. - * * @author Rossen Stoyanchev */ +@SuppressWarnings("unused") public class HandlerMethodReturnValueHandlerCompositeTests { private HandlerMethodReturnValueHandlerComposite handlers; + private HandlerMethodReturnValueHandler integerHandler; + ModelAndViewContainer mavContainer; - private MethodParameter paramInt; + private MethodParameter integerType; + + private MethodParameter stringType; - private MethodParameter paramStr; @Before public void setUp() throws Exception { - handlers = new HandlerMethodReturnValueHandlerComposite(); + + this.integerType = new MethodParameter(getClass().getDeclaredMethod("handleInteger"), -1); + this.stringType = new MethodParameter(getClass().getDeclaredMethod("handleString"), -1); + + this.integerHandler = mock(HandlerMethodReturnValueHandler.class); + when(this.integerHandler.supportsReturnType(this.integerType)).thenReturn(true); + + this.handlers = new HandlerMethodReturnValueHandlerComposite(); + this.handlers.addHandler(this.integerHandler); + mavContainer = new ModelAndViewContainer(); - paramInt = new MethodParameter(getClass().getDeclaredMethod("handleInteger"), -1); - paramStr = new MethodParameter(getClass().getDeclaredMethod("handleString"), -1); } @Test public void supportsReturnType() throws Exception { - registerHandler(Integer.class); - - assertTrue(this.handlers.supportsReturnType(paramInt)); - assertFalse(this.handlers.supportsReturnType(paramStr)); + assertTrue(this.handlers.supportsReturnType(this.integerType)); + assertFalse(this.handlers.supportsReturnType(this.stringType)); } @Test public void handleReturnValue() throws Exception { - StubReturnValueHandler handler = registerHandler(Integer.class); - this.handlers.handleReturnValue(Integer.valueOf(55), paramInt, mavContainer, null); + this.handlers.handleReturnValue(55, this.integerType, this.mavContainer, null); + verify(this.integerHandler).handleReturnValue(55, this.integerType, this.mavContainer, null); + } + + @Test + public void handleReturnValueWithMultipleHandlers() throws Exception { + HandlerMethodReturnValueHandler anotherIntegerHandler = mock(HandlerMethodReturnValueHandler.class); + when(anotherIntegerHandler.supportsReturnType(this.integerType)).thenReturn(true); - assertEquals(Integer.valueOf(55), handler.getReturnValue()); + this.handlers.handleReturnValue(55, this.integerType, this.mavContainer, null); + + verify(this.integerHandler).handleReturnValue(55, this.integerType, this.mavContainer, null); + verifyNoMoreInteractions(anotherIntegerHandler); } + // SPR-13083 + @Test - public void handleReturnValueMultipleHandlers() throws Exception { - StubReturnValueHandler h1 = registerHandler(Integer.class); - StubReturnValueHandler h2 = registerHandler(Integer.class); - this.handlers.handleReturnValue(Integer.valueOf(55), paramInt, mavContainer, null); + public void handleReturnValueWithAsyncHandler() throws Exception { + + Promise promise = new Promise<>(); + MethodParameter promiseType = new MethodParameter(getClass().getDeclaredMethod("handlePromise"), -1); + + HandlerMethodReturnValueHandler responseBodyHandler = mock(HandlerMethodReturnValueHandler.class); + when(responseBodyHandler.supportsReturnType(promiseType)).thenReturn(true); + this.handlers.addHandler(responseBodyHandler); + + AsyncHandlerMethodReturnValueHandler promiseHandler = mock(AsyncHandlerMethodReturnValueHandler.class); + when(promiseHandler.supportsReturnType(promiseType)).thenReturn(true); + when(promiseHandler.isAsyncReturnValue(promise, promiseType)).thenReturn(true); + this.handlers.addHandler(promiseHandler); + + this.handlers.handleReturnValue(promise, promiseType, this.mavContainer, null); - assertEquals("Didn't use the 1st registered handler", Integer.valueOf(55), h1.getReturnValue()); - assertNull("Shouldn't have use the 2nd registered handler", h2.getReturnValue()); + verify(promiseHandler).isAsyncReturnValue(promise, promiseType); + verify(promiseHandler).supportsReturnType(promiseType); + verify(promiseHandler).handleReturnValue(promise, promiseType, this.mavContainer, null); + verifyNoMoreInteractions(promiseHandler); + verifyNoMoreInteractions(responseBodyHandler); } @Test(expected=IllegalArgumentException.class) public void noSuitableReturnValueHandler() throws Exception { - registerHandler(Integer.class); - this.handlers.handleReturnValue("value", paramStr, null, null); + this.handlers.handleReturnValue("value", this.stringType, null, null); } - private StubReturnValueHandler registerHandler(Class returnType) { - StubReturnValueHandler handler = new StubReturnValueHandler(returnType); - handlers.addHandler(handler); - return handler; - } - @SuppressWarnings("unused") private Integer handleInteger() { return null; } - @SuppressWarnings("unused") private String handleString() { return null; } + private Promise handlePromise() { + return null; + } + + private static class Promise {} + } \ No newline at end of file diff --git a/spring-web/src/test/java/org/springframework/web/method/support/StubReturnValueHandler.java b/spring-web/src/test/java/org/springframework/web/method/support/StubReturnValueHandler.java deleted file mode 100644 index ca63af02366..00000000000 --- a/spring-web/src/test/java/org/springframework/web/method/support/StubReturnValueHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002-2012 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.web.method.support; - -import org.springframework.core.MethodParameter; -import org.springframework.web.context.request.NativeWebRequest; - -/** - * Supports a fixed return value type. Records the last handled return value. - * - * @author Rossen Stoyanchev - */ -public class StubReturnValueHandler implements HandlerMethodReturnValueHandler { - - private final Class returnType; - - private Object returnValue; - - public StubReturnValueHandler(Class returnType) { - this.returnType = returnType; - } - - public Object getReturnValue() { - return this.returnValue; - } - - @Override - public boolean supportsReturnType(MethodParameter returnType) { - return returnType.getParameterType().equals(this.returnType); - } - - @Override - public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, - NativeWebRequest webRequest) throws Exception { - this.returnValue = returnValue; - } - -} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AsyncTaskMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AsyncTaskMethodReturnValueHandler.java index 88c7c04c0dd..6691d65cc32 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AsyncTaskMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AsyncTaskMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -21,7 +21,7 @@ import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.WebAsyncTask; import org.springframework.web.context.request.async.WebAsyncUtils; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** @@ -30,7 +30,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @author Rossen Stoyanchev * @since 3.2 */ -public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnValueHandler { +public class AsyncTaskMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { private final BeanFactory beanFactory; @@ -45,6 +45,11 @@ public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnVal return WebAsyncTask.class.isAssignableFrom(returnType.getParameterType()); } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + return (returnValue != null && returnValue instanceof WebAsyncTask); + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java index a38c05bdab9..dbb46cc799f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -21,6 +21,7 @@ import java.util.concurrent.Callable; import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; @@ -30,13 +31,18 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @author Rossen Stoyanchev * @since 3.2 */ -public class CallableMethodReturnValueHandler implements HandlerMethodReturnValueHandler { +public class CallableMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return Callable.class.isAssignableFrom(returnType.getParameterType()); } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + return (returnValue != null && returnValue instanceof Callable); + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java index 6e108b36975..6543b76c56e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java @@ -25,7 +25,7 @@ import org.springframework.lang.UsesJava8; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.WebAsyncUtils; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** @@ -36,13 +36,18 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @since 4.2 */ @UsesJava8 -public class CompletionStageReturnValueHandler implements HandlerMethodReturnValueHandler { +public class CompletionStageReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return CompletionStage.class.isAssignableFrom(returnType.getParameterType()); } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + return (returnValue != null && returnValue instanceof CompletionStage); + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { @@ -70,7 +75,6 @@ public class CompletionStageReturnValueHandler implements HandlerMethodReturnVal return null; } }); - } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java index 3b66aec9355..620b9654e2f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -20,6 +20,7 @@ import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; @@ -29,13 +30,18 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @author Rossen Stoyanchev * @since 3.2 */ -public class DeferredResultMethodReturnValueHandler implements HandlerMethodReturnValueHandler { +public class DeferredResultMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return DeferredResult.class.isAssignableFrom(returnType.getParameterType()); } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + return (returnValue != null && returnValue instanceof DeferredResult); + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java index af9fb1a3d24..18d25a4f163 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -22,7 +22,7 @@ import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.WebAsyncUtils; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** @@ -32,13 +32,18 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @author Rossen Stoyanchev * @since 4.1 */ -public class ListenableFutureReturnValueHandler implements HandlerMethodReturnValueHandler { +public class ListenableFutureReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return ListenableFuture.class.isAssignableFrom(returnType.getParameterType()); } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + return (returnValue != null && returnValue instanceof ListenableFuture); + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java index a02656a5c7c..3c7ed2d5e33 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java @@ -40,7 +40,7 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.filter.ShallowEtagHeaderFilter; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; +import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** @@ -50,7 +50,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; * @author Rossen Stoyanchev * @since 4.2 */ -public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodReturnValueHandler { +public class ResponseBodyEmitterReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { private static final Log logger = LogFactory.getLog(ResponseBodyEmitterReturnValueHandler.class); @@ -75,6 +75,20 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur return false; } + @Override + public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { + if (returnValue != null) { + if (returnValue instanceof ResponseBodyEmitter) { + return true; + } + else if (returnValue instanceof ResponseEntity) { + Object body = ((ResponseEntity) returnValue).getBody(); + return (body != null && body instanceof ResponseBodyEmitter); + } + } + return false; + } + @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {