Support Message argument with payload conversion

The MessageMethodArgumentResolver now also supports applying a
MessageConverter to the payload.

This is effectively a shortcut for declaring a method with an @Payload
argument + MessageHeaders and then creating a new message from the two.

Issue: SPR-13288
This commit is contained in:
Rossen Stoyanchev
2016-02-18 18:06:13 -05:00
parent ec3571fd4d
commit 416966d943
5 changed files with 133 additions and 27 deletions

View File

@@ -158,7 +158,7 @@ public class DefaultMessageHandlerMethodFactory implements MessageHandlerMethodF
resolvers.add(new HeadersMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new MessageMethodArgumentResolver());
resolvers.add(new MessageMethodArgumentResolver(this.messageConverter));
if (this.customArgumentResolvers != null) {
resolvers.addAll(this.customArgumentResolvers);

View File

@@ -21,8 +21,14 @@ import java.lang.reflect.Type;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@code HandlerMethodArgumentResolver} for {@link Message} method arguments.
@@ -34,6 +40,19 @@ import org.springframework.util.ClassUtils;
*/
public class MessageMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final MessageConverter converter;
/**
* Create a new instance with the given {@link MessageConverter}.
* @param converter the MessageConverter to use (required)
*/
public MessageMethodArgumentResolver(MessageConverter converter) {
Assert.notNull(converter, "MessageConverter must not be null");
this.converter = converter;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Message.class.isAssignableFrom(parameter.getParameterType());
@@ -41,23 +60,32 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
@Override
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
Class<?> paramType = parameter.getParameterType();
if (!paramType.isAssignableFrom(message.getClass())) {
Class<?> targetMessageType = parameter.getParameterType();
Class<?> targetPayloadType = getPayloadType(parameter);
if (!targetMessageType.isAssignableFrom(message.getClass())) {
String actual = ClassUtils.getQualifiedName(message.getClass());
String expected = ClassUtils.getQualifiedName(paramType);
String expected = ClassUtils.getQualifiedName(targetMessageType);
throw new MethodArgumentTypeMismatchException(message, parameter, "The actual message type " +
"[" + actual + "] does not match the expected type [" + expected + "]");
}
Class<?> targetPayloadType = getPayloadType(parameter);
Object payload = message.getPayload();
if (payload != null && !targetPayloadType.isInstance(payload)) {
throw new MethodArgumentTypeMismatchException(message, parameter,
"The expected Message<?> payload type [" + ClassUtils.getQualifiedName(targetPayloadType) +
"] does not match the actual payload type [" + ClassUtils.getQualifiedName(payload.getClass()) + "]");
if (payload == null || targetPayloadType.isInstance(payload)) {
return message;
}
return message;
if (isEmptyPayload(payload)) {
String actual = ClassUtils.getQualifiedName(payload.getClass());
String expected = ClassUtils.getQualifiedName(targetPayloadType);
throw new MessageConversionException(message, "Cannot convert from the " +
"expected payload type [" + expected + "] to the " +
"actual payload type [" + actual + "] when the payload is empty.");
}
payload = convertPayload(message, parameter, targetPayloadType);
return MessageBuilder.createMessage(payload, message.getHeaders());
}
private Class<?> getPayloadType(MethodParameter parameter) {
@@ -66,4 +94,42 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
return resolvableType.getGeneric(0).resolve(Object.class);
}
/**
* Check if the given {@code payload} is empty.
* @param payload the payload to check (can be {@code null})
*/
protected boolean isEmptyPayload(Object payload) {
if (payload == null) {
return true;
}
else if (payload instanceof byte[]) {
return ((byte[]) payload).length == 0;
}
else if (payload instanceof String) {
return !StringUtils.hasText((String) payload);
}
else {
return false;
}
}
private Object convertPayload(Message<?> message, MethodParameter parameter, Class<?> targetPayloadType) {
Object result;
if (this.converter instanceof SmartMessageConverter) {
SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter;
result = smartConverter.fromMessage(message, targetPayloadType, parameter);
}
else {
result = this.converter.fromMessage(message, targetPayloadType);
}
if (result == null) {
String actual = ClassUtils.getQualifiedName(targetPayloadType);
String expected = ClassUtils.getQualifiedName(message.getPayload().getClass());
throw new MessageConversionException(message, "No converter found to convert payload " +
"type [" + actual + "] to expected payload type [" + expected + "].");
}
return result;
}
}

View File

@@ -317,7 +317,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
// Type-based argument resolution
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new MessageMethodArgumentResolver());
resolvers.add(new MessageMethodArgumentResolver(this.messageConverter));
resolvers.addAll(getCustomArgumentResolvers());
resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));