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 0000000000..440230ccfe --- /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 5f57fcda2b..36d2c25d88 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 5fe5eaef0c..06f46b227f 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); - - 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); } @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 handleReturnValueWithMultipleHandlers() throws Exception { + HandlerMethodReturnValueHandler anotherIntegerHandler = mock(HandlerMethodReturnValueHandler.class); + when(anotherIntegerHandler.supportsReturnType(this.integerType)).thenReturn(true); - assertEquals("Didn't use the 1st registered handler", Integer.valueOf(55), h1.getReturnValue()); - assertNull("Shouldn't have use the 2nd registered handler", h2.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 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); + + 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 ca63af0236..0000000000 --- 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 88c7c04c0d..6691d65cc3 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 a38c05bdab..dbb46cc799 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 6e108b3697..6543b76c56 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 3b66aec935..620b9654e2 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 af9fb1a3d2..18d25a4f16 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 a02656a5c7..3c7ed2d5e3 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 {