This commit is contained in:
Rossen Stoyanchev 2015-05-22 09:06:18 -04:00
parent fee63fdfb8
commit 0db216daab
8 changed files with 63 additions and 58 deletions

View File

@ -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.");
}
}

View File

@ -467,7 +467,7 @@ public abstract class AbstractMethodMessageHandler<T>
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<T>
private class ReturnValueListenableFutureCallback implements ListenableFutureCallback<Object> {
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<T>
@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);

View File

@ -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}.
*
* <p>Implementations only intended for asynchronous return value handling can extend
* {@link AbstractAsyncReturnValueHandler}.</p>
* <p>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.
*
* <p><strong>Note:</strong> 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.
*
* <p><strong>Note:</strong> 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);

View File

@ -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<Object>((CompletableFuture<Object>)returnValue);
return new CompletableToListenableFutureAdapter<Object>((CompletableFuture<Object>) returnValue);
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -318,14 +318,13 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
@Override
protected List<? extends HandlerMethodReturnValueHandler> initReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
// 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