diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractAsyncReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractAsyncReturnValueHandler.java index 11942e705d1..993599c0a56 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractAsyncReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractAsyncReturnValueHandler.java @@ -20,21 +20,25 @@ import org.springframework.core.MethodParameter; import org.springframework.messaging.Message; /** - * Abstract base class for {@link AsyncHandlerMethodReturnValueHandler} implementations - * only intended for asynchronous return value handling. + * Convenient base class for {@link AsyncHandlerMethodReturnValueHandler} + * implementations that support only asynchronous (Future-like) return values a + * and merely serve as adapters of such types to Spring's + * {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}. * * @author Sebastien Deleuze * @since 4.2 */ public abstract class AbstractAsyncReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { - @Override - public void handleReturnValue(Object returnValue, MethodParameter returnType, Message message) throws Exception { - throw new UnsupportedOperationException("Not supported"); - } - @Override public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { return true; } + + @Override + public void handleReturnValue(Object returnValue, MethodParameter returnType, Message message) { + // Should never be called since we return "true" from isAsyncReturnValue + throw new IllegalStateException("Unexpected invocation."); + } + } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java index 013e07b0bf8..d5f932e1043 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java @@ -467,7 +467,7 @@ public abstract class AbstractMethodMessageHandler if (this.returnValueHandlers.isAsyncReturnValue(returnValue, returnType)) { ListenableFuture future = this.returnValueHandlers.toListenableFuture(returnValue, returnType); if (future != null) { - future.addCallback(new ReturnValueListenableFutureCallback(returnType, invocable, message)); + future.addCallback(new ReturnValueListenableFutureCallback(invocable, message)); } } else { @@ -596,17 +596,12 @@ public abstract class AbstractMethodMessageHandler private class ReturnValueListenableFutureCallback implements ListenableFutureCallback { - private final MethodParameter returnType; - private final InvocableHandlerMethod handlerMethod; private final Message message; - public ReturnValueListenableFutureCallback(MethodParameter returnType, - InvocableHandlerMethod handlerMethod, Message message) { - - this.returnType = returnType; + public ReturnValueListenableFutureCallback(InvocableHandlerMethod handlerMethod, Message message) { this.handlerMethod = handlerMethod; this.message = message; } @@ -614,7 +609,8 @@ public abstract class AbstractMethodMessageHandler @Override public void onSuccess(Object result) { try { - returnValueHandlers.handleReturnValue(result, handlerMethod.getAsyncReturnValueType(result), this.message); + MethodParameter returnType = this.handlerMethod.getAsyncReturnValueType(result); + returnValueHandlers.handleReturnValue(result, returnType, this.message); } catch (Throwable ex) { handleFailure(ex); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java index 2a20a329b12..48489547446 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java @@ -16,14 +16,16 @@ package org.springframework.messaging.handler.invocation; import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; import org.springframework.util.concurrent.ListenableFuture; /** - * An extension of {@link HandlerMethodReturnValueHandler} for handling async - * return value types. + * An extension of {@link HandlerMethodReturnValueHandler} for handling async, + * Future-like return value types that support success and error callbacks. + * Essentially anything that can be adapted to a {@link ListenableFuture}. * - *

Implementations only intended for asynchronous return value handling can extend - * {@link AbstractAsyncReturnValueHandler}.

+ *

Implementations should consider extending the convenient base class + * {@link AbstractAsyncReturnValueHandler}. * * @author Rossen Stoyanchev * @since 4.2 @@ -32,30 +34,36 @@ import org.springframework.util.concurrent.ListenableFuture; public interface AsyncHandlerMethodReturnValueHandler extends HandlerMethodReturnValueHandler { /** - * Whether the return value type represents a value that will be produced - * asynchronously. If this method returns {@code true}, the - * {@link #toListenableFuture(Object, MethodParameter)} will be invoked next. - * @param returnValue the value returned from the handler method - * @param returnType the type of the return value. This type must have - * previously been passed to + * Whether the return value represents an asynchronous, Future-like type + * with success and error callbacks. If this method returns {@code true}, + * then {@link #toListenableFuture} is invoked next. If it returns + * {@code false}, then {@link #handleReturnValue} is called. + * + *

Note: this method will only be invoked after * {@link #supportsReturnType(org.springframework.core.MethodParameter)} - * and it must have returned {@code true} + * is called and it returns {@code true}. + * + * @param returnValue the value returned from the handler method + * @param returnType the type of the return value. * @return true if the return value type represents an async value. */ boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType); /** - * Adapt the given asynchronous return value to a ListenableFuture. - * Implementations can return an instance of - * {@link org.springframework.util.concurrent.SettableListenableFuture} and - * then set it to an Object (success) or a Throwable (failure) to complete - * handling. - * @param returnValue the value returned from the handler method - * @param returnType the type of the return value. This type must have - * previously been passed to + * Adapt the asynchronous return value to a {@link ListenableFuture}. + * Implementations should consider returning an instance of + * {@link org.springframework.util.concurrent.SettableListenableFuture + * SettableListenableFuture}. Return value handling will then continue when + * the ListenableFuture is completed with either success or error. + * + *

Note: this method will only be invoked after * {@link #supportsReturnType(org.springframework.core.MethodParameter)} - * and it must have returned {@code true} - * @return a ListenableFuture + * is called and it returns {@code true}. + * + * @param returnValue the value returned from the handler method + * @param returnType the type of the return value. + * @return the resulting ListenableFuture or {@code null} in which case no + * further handling will be performed. */ ListenableFuture toListenableFuture(Object returnValue, MethodParameter returnType); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/CompletableFutureReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/CompletableFutureReturnValueHandler.java index 12c1171a250..e08c9fb08ec 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/CompletableFutureReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/CompletableFutureReturnValueHandler.java @@ -24,7 +24,7 @@ import org.springframework.util.concurrent.CompletableToListenableFutureAdapter; import org.springframework.util.concurrent.ListenableFuture; /** - * An {@link AsyncHandlerMethodReturnValueHandler} for {@link CompletableFuture} return type handling. + * Support for {@link CompletableFuture} as a return value type. * * @author Sebastien Deleuze * @since 4.2 @@ -40,7 +40,7 @@ public class CompletableFutureReturnValueHandler extends AbstractAsyncReturnValu @Override @SuppressWarnings("unchecked") public ListenableFuture toListenableFuture(Object returnValue, MethodParameter returnType) { - return new CompletableToListenableFutureAdapter((CompletableFuture)returnValue); + return new CompletableToListenableFutureAdapter((CompletableFuture) returnValue); } } \ No newline at end of file diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodReturnValueHandlerComposite.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodReturnValueHandlerComposite.java index 3f908931362..a09214e90fc 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodReturnValueHandlerComposite.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodReturnValueHandlerComposite.java @@ -104,12 +104,8 @@ public class HandlerMethodReturnValueHandlerComposite implements AsyncHandlerMet @Override public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); - if (handler != null && handler instanceof AsyncHandlerMethodReturnValueHandler) { - if (((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(returnValue, returnType)) { - return true; - } - } - return false; + return (handler != null && handler instanceof AsyncHandlerMethodReturnValueHandler && + ((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(returnValue, returnType)); } @Override diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java index a34e0e5b26f..2f4aaab5b74 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java @@ -28,7 +28,6 @@ import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ResolvableType; import org.springframework.messaging.Message; import org.springframework.messaging.handler.HandlerMethod; -import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; /** @@ -152,15 +151,15 @@ public class InvocableHandlerMethod extends HandlerMethod { } /** - * Adds HandlerMethod details such as the controller type and method signature to the given error message. + * Adds HandlerMethod details such as the controller type and method + * signature to the given error message. * @param message error message to append the HandlerMethod details to */ protected String getDetailedErrorMessage(String message) { - StringBuilder sb = new StringBuilder(message).append("\n"); - sb.append("HandlerMethod details: \n"); - sb.append("Controller [").append(getBeanType().getName()).append("]\n"); - sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); - return sb.toString(); + return message + "\n" + + "HandlerMethod details: \n" + + "Controller [" + getBeanType().getName() + "]\n" + + "Method [" + getBridgedMethod().toGenericString() + "]\n"; } /** @@ -249,6 +248,7 @@ public class InvocableHandlerMethod extends HandlerMethod { return new AsyncResultMethodParameter(returnValue); } + private class AsyncResultMethodParameter extends HandlerMethodParameter { private final Object returnValue; @@ -266,7 +266,9 @@ public class InvocableHandlerMethod extends HandlerMethod { if (this.returnValue != null) { return this.returnValue.getClass(); } - Assert.isTrue(!ResolvableType.NONE.equals(this.returnType), "Expected Future-like type with generic parameter"); + if (ResolvableType.NONE.equals(this.returnType)) { + throw new IllegalArgumentException("Expected Future-like type with generic parameter"); + } return this.returnType.getRawClass(); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/ListenableFutureReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/ListenableFutureReturnValueHandler.java index 71c790a09d2..4825aafdb96 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/ListenableFutureReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/ListenableFutureReturnValueHandler.java @@ -20,7 +20,7 @@ import org.springframework.core.MethodParameter; import org.springframework.util.concurrent.ListenableFuture; /** - * An {@link AsyncHandlerMethodReturnValueHandler} for {@link ListenableFuture} return type handling. + * Support for {@link ListenableFuture} as a return value type. * * @author Sebastien Deleuze * @since 4.2 @@ -35,7 +35,7 @@ public class ListenableFutureReturnValueHandler extends AbstractAsyncReturnValue @Override @SuppressWarnings("unchecked") public ListenableFuture toListenableFuture(Object returnValue, MethodParameter returnType) { - return (ListenableFuture)returnValue; + return (ListenableFuture) returnValue; } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index 2cfcb25a054..58f9c9a9a2c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -318,14 +318,13 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan @Override protected List initReturnValueHandlers() { + List handlers = new ArrayList(); // Single-purpose return value types - ListenableFutureReturnValueHandler lfh = new ListenableFutureReturnValueHandler(); - handlers.add(lfh); + handlers.add(new ListenableFutureReturnValueHandler()); if (completableFuturePresent) { - CompletableFutureReturnValueHandler cfh = new CompletableFutureReturnValueHandler(); - handlers.add(cfh); + handlers.add(new CompletableFutureReturnValueHandler()); } // Annotation-based return value types