PayloadArgumentResolver supports JsonView as well (through AbstractMessageConverter revision)

AbstractMessageConverter provides overloaded methods with a conversion hint, MappingJackson2MessageConverter takes that hint into account, and SimpMessagingTemplate transformes such a hint in the given headers map into an explicit argument invocation argument.

Issue: SPR-13265
This commit is contained in:
Juergen Hoeller 2015-07-24 17:50:31 +02:00
parent 24b1dc14d2
commit 0f54f686b2
12 changed files with 216 additions and 115 deletions

View File

@ -24,7 +24,6 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
@ -40,20 +39,11 @@ import org.springframework.util.MimeType;
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 4.0
*/
public abstract class AbstractMessageConverter implements MessageConverter {
/**
* Name of the header that can be set to provide further information
* ({@link MethodParameter} instance) about the origin of the payload (for
* {@link #toMessage(Object, MessageHeaders)}) or about the target of the payload
* ({@link #fromMessage(Message, Class)}).
* @since 4.2
*/
public static final String METHOD_PARAMETER_HINT_HEADER = "methodParameterHint";
protected final Log logger = LogFactory.getLog(getClass());
private final List<MimeType> supportedMimeTypes;
@ -174,10 +164,27 @@ public abstract class AbstractMessageConverter implements MessageConverter {
@Override
public final Object fromMessage(Message<?> message, Class<?> targetClass) {
return fromMessage(message, targetClass, null);
}
/**
* A variant of {@link #fromMessage(Message, Class)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a payload parameter into account.
* @param message the input message
* @param targetClass the target class for the conversion
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the result of the conversion, or {@code null} if the converter cannot
* perform the conversion
* @since 4.2
* @see #fromMessage(Message, Class)
*/
public final Object fromMessage(Message<?> message, Class<?> targetClass, Object conversionHint) {
if (!canConvertFrom(message, targetClass)) {
return null;
}
return convertFromInternal(message, targetClass);
return convertFromInternal(message, targetClass, conversionHint);
}
protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) {
@ -186,13 +193,33 @@ public abstract class AbstractMessageConverter implements MessageConverter {
@Override
public final Message<?> toMessage(Object payload, MessageHeaders headers) {
return toMessage(payload, headers, null);
}
/**
* A variant of {@link #toMessage(Object, MessageHeaders)} which takes an extra
* conversion context as an argument, allowing to take e.g. annotations
* on a return type into account.
* @param payload the Object to convert
* @param headers optional headers for the message (may be {@code null})
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the new message, or {@code null} if the converter does not support the
* Object type or the target media type
* @since 4.2
* @see #toMessage(Object, MessageHeaders)
*/
public final Message<?> toMessage(Object payload, MessageHeaders headers, Object conversionHint) {
if (!canConvertTo(payload, headers)) {
return null;
}
payload = convertToInternal(payload, headers);
MimeType mimeType = getDefaultContentType(payload);
payload = convertToInternal(payload, headers, conversionHint);
if (payload == null) {
return null;
}
MimeType mimeType = getDefaultContentType(payload);
if (headers != null) {
MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(headers, MessageHeaderAccessor.class);
if (accessor != null && accessor.isMutable()) {
@ -244,13 +271,52 @@ public abstract class AbstractMessageConverter implements MessageConverter {
/**
* Convert the message payload from serialized form to an Object.
* @param message the input message
* @param targetClass the target class for the conversion
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the result of the conversion, or {@code null} if the converter cannot
* perform the conversion
* @since 4.2
*/
public abstract Object convertFromInternal(Message<?> message, Class<?> targetClass);
@SuppressWarnings("deprecation")
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
return convertFromInternal(message, targetClass);
}
/**
* Convert the payload object to serialized form.
* @param payload the Object to convert
* @param headers optional headers for the message (may be {@code null})
* @param conversionHint an extra object passed to the {@link MessageConverter},
* e.g. the associated {@code MethodParameter} (may be {@code null}}
* @return the resulting payload for the message, or {@code null} if the converter
* cannot perform the conversion
* @since 4.2
*/
public abstract Object convertToInternal(Object payload, MessageHeaders headers);
@SuppressWarnings("deprecation")
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
return convertToInternal(payload, headers);
}
/**
* Convert the message payload from serialized form to an Object.
* @deprecated as of Spring 4.2, in favor of {@link #convertFromInternal(Message, Class, Object)}
* (which is also protected instead of public)
*/
@Deprecated
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
return null;
}
/**
* Convert the payload object to serialized form.
* @deprecated as of Spring 4.2, in favor of {@link #convertFromInternal(Message, Class, Object)}
* (which is also protected instead of public)
*/
@Deprecated
public Object convertToInternal(Object payload, MessageHeaders headers) {
return null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@ -29,7 +29,6 @@ import org.springframework.util.MimeTypeUtils;
*/
public class ByteArrayMessageConverter extends AbstractMessageConverter {
public ByteArrayMessageConverter() {
super(MimeTypeUtils.APPLICATION_OCTET_STREAM);
}
@ -37,16 +36,16 @@ public class ByteArrayMessageConverter extends AbstractMessageConverter {
@Override
protected boolean supports(Class<?> clazz) {
return byte[].class == clazz;
return (byte[].class == clazz);
}
@Override
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
return message.getPayload();
}
@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
return payload;
}

View File

@ -195,15 +195,28 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
}
@Override
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
@SuppressWarnings("deprecation")
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
JavaType javaType = this.objectMapper.constructType(targetClass);
Object payload = message.getPayload();
Class<?> view = getSerializationView(conversionHint);
// Note: in the view case, calling withType instead of forType for compatibility with Jackson <2.5
try {
if (payload instanceof byte[]) {
return this.objectMapper.readValue((byte[]) payload, javaType);
if (view != null) {
return this.objectMapper.readerWithView(view).withType(javaType).readValue((byte[]) payload);
}
else {
return this.objectMapper.readValue((byte[]) payload, javaType);
}
}
else {
return this.objectMapper.readValue((String) payload, javaType);
if (view != null) {
return this.objectMapper.readerWithView(view).withType(javaType).readValue(payload.toString());
}
else {
return this.objectMapper.readValue(payload.toString(), javaType);
}
}
}
catch (IOException ex) {
@ -212,15 +225,15 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
}
@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
try {
Class<?> serializationView = getSerializationView(headers);
Class<?> view = getSerializationView(conversionHint);
if (byte[].class == getSerializedPayloadClass()) {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
JsonEncoding encoding = getJsonEncoding(getMimeType(headers));
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(out, encoding);
if (serializationView != null) {
this.objectMapper.writerWithView(serializationView).writeValue(generator, payload);
if (view != null) {
this.objectMapper.writerWithView(view).writeValue(generator, payload);
}
else {
this.objectMapper.writeValue(generator, payload);
@ -229,8 +242,8 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
}
else {
Writer writer = new StringWriter();
if (serializationView != null) {
this.objectMapper.writerWithView(serializationView).writeValue(writer, payload);
if (view != null) {
this.objectMapper.writerWithView(view).writeValue(writer, payload);
}
else {
this.objectMapper.writeValue(writer, payload);
@ -244,20 +257,40 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
return payload;
}
private Class<?> getSerializationView(MessageHeaders headers) {
MethodParameter returnType = (headers != null ?
(MethodParameter) headers.get(METHOD_PARAMETER_HINT_HEADER) : null);
if (returnType == null) {
return null;
}
JsonView annotation = returnType.getMethodAnnotation(JsonView.class);
if (annotation == null) {
/**
* Determine a Jackson serialization view based on the given conversion hint.
* @param conversionHint the conversion hint Object as passed into the
* converter for the current conversion attempt
* @return the serialization view class, or {@code null} if none
*/
protected Class<?> getSerializationView(Object conversionHint) {
if (conversionHint instanceof MethodParameter) {
MethodParameter methodParam = (MethodParameter) conversionHint;
JsonView annotation = methodParam.getParameterAnnotation(JsonView.class);
if (annotation == null) {
annotation = methodParam.getMethodAnnotation(JsonView.class);
if (annotation == null) {
return null;
}
}
return extractViewClass(annotation, conversionHint);
}
else if (conversionHint instanceof JsonView) {
return extractViewClass((JsonView) conversionHint, conversionHint);
}
else if (conversionHint instanceof Class) {
return (Class) conversionHint;
}
else {
return null;
}
}
private Class<?> extractViewClass(JsonView annotation, Object conversionHint) {
Class<?>[] classes = annotation.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for handler methods with exactly 1 class argument: " + returnType);
"@JsonView only supported for handler methods with exactly 1 class argument: " + conversionHint);
}
return classes[0];
}

View File

@ -134,7 +134,7 @@ public class MarshallingMessageConverter extends AbstractMessageConverter {
}
@Override
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
try {
Source source = getSource(message.getPayload());
@ -159,7 +159,7 @@ public class MarshallingMessageConverter extends AbstractMessageConverter {
}
@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
Assert.notNull(this.marshaller, "Property 'marshaller' is required");
try {
if (byte[].class == getSerializedPayloadClass()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@ -46,18 +46,18 @@ public class StringMessageConverter extends AbstractMessageConverter {
@Override
protected boolean supports(Class<?> clazz) {
return String.class == clazz;
return (String.class == clazz);
}
@Override
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
Charset charset = getContentTypeCharset(getMimeType(message.getHeaders()));
Object payload = message.getPayload();
return (payload instanceof String) ? payload : new String((byte[]) payload, charset);
return (payload instanceof String ? payload : new String((byte[]) payload, charset));
}
@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
if (byte[].class == getSerializedPayloadClass()) {
Charset charset = getContentTypeCharset(getMimeType(headers));
payload = ((String) payload).getBytes(charset);

View File

@ -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.
@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SimpleMessageConverter;
@ -39,6 +40,15 @@ import org.springframework.util.Assert;
*/
public abstract class AbstractMessageSendingTemplate<D> implements MessageSendingOperations<D> {
/**
* Name of the header that can be set to provide further information
* (e.g. a {@code MethodParameter} instance) about the origin of the
* payload, to be taken into account as a conversion hint.
* @since 4.2
*/
public static final String CONVERSION_HINT_HEADER = "conversionHint";
protected final Log logger = LogFactory.getLog(getClass());
private volatile D defaultDestination;
@ -144,6 +154,8 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
*/
protected Message<?> doConvert(Object payload, Map<String, Object> headers, MessagePostProcessor postProcessor) {
MessageHeaders messageHeaders = null;
Object conversionHint = (headers != null ? headers.get(CONVERSION_HINT_HEADER) : null);
Map<String, Object> headersToUse = processHeadersToSend(headers);
if (headersToUse != null) {
if (headersToUse instanceof MessageHeaders) {
@ -154,7 +166,10 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
}
}
Message<?> message = getMessageConverter().toMessage(payload, messageHeaders);
MessageConverter converter = getMessageConverter();
Message<?> message = (converter instanceof AbstractMessageConverter ?
((AbstractMessageConverter) converter).toMessage(payload, messageHeaders, conversionHint) :
converter.toMessage(payload, messageHeaders));
if (message == null) {
String payloadType = (payload != null ? payload.getClass().getName() : null);
Object contentType = (messageHeaders != null ? messageHeaders.get(MessageHeaders.CONTENT_TYPE) : null);
@ -168,11 +183,11 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
}
/**
* Provides access to the map of input headers before a send operation. Sub-classes
* can modify the headers and then return the same or a different map.
* Provides access to the map of input headers before a send operation.
* Subclasses can modify the headers and then return the same or a different map.
* <p>This default implementation in this class returns the input map.
* @param headers the headers to send or {@code null}
* @return the actual headers to send or {@code null}
* @param headers the headers to send (or {@code null} if none)
* @return the actual headers to send (or {@code null} if none)
*/
protected Map<String, Object> processHeadersToSend(Map<String, Object> headers) {
return headers;

View File

@ -21,6 +21,7 @@ import java.lang.annotation.Annotation;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.Payload;
@ -85,8 +86,8 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
}
@Override
public Object resolveArgument(MethodParameter param, Message<?> message) throws Exception {
Payload ann = param.getParameterAnnotation(Payload.class);
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
Payload ann = parameter.getParameterAnnotation(Payload.class);
if (ann != null && StringUtils.hasText(ann.expression())) {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver");
}
@ -94,28 +95,30 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
Object payload = message.getPayload();
if (isEmptyPayload(payload)) {
if (ann == null || ann.required()) {
String paramName = getParameterName(param);
String paramName = getParameterName(parameter);
BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName);
bindingResult.addError(new ObjectError(paramName, "@Payload param is required"));
throw new MethodArgumentNotValidException(message, param, bindingResult);
throw new MethodArgumentNotValidException(message, parameter, bindingResult);
}
else {
return null;
}
}
Class<?> targetClass = param.getParameterType();
Class<?> targetClass = parameter.getParameterType();
if (ClassUtils.isAssignable(targetClass, payload.getClass())) {
validate(message, param, payload);
validate(message, parameter, payload);
return payload;
}
else {
payload = this.converter.fromMessage(message, targetClass);
payload = (this.converter instanceof AbstractMessageConverter ?
((AbstractMessageConverter) this.converter).fromMessage(message, targetClass, parameter) :
this.converter.fromMessage(message, targetClass));
if (payload == null) {
throw new MessageConversionException(message,
"No converter found to convert to " + targetClass + ", message=" + message);
}
validate(message, param, payload);
validate(message, parameter, payload);
return payload;
}
}

View File

@ -25,7 +25,6 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver;
@ -33,6 +32,7 @@ import org.springframework.messaging.handler.invocation.HandlerMethodReturnValue
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.user.DestinationUserNameProvider;
import org.springframework.messaging.support.MessageHeaderInitializer;
@ -220,7 +220,7 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
if (sessionId != null) {
headerAccessor.setSessionId(sessionId);
}
headerAccessor.setHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
headerAccessor.setHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER, returnType);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}

View File

@ -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,12 +22,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.support.MessageHeaderInitializer;
@ -121,7 +121,7 @@ public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturn
}
headerAccessor.setSessionId(sessionId);
headerAccessor.setSubscriptionId(subscriptionId);
headerAccessor.setHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
headerAccessor.setHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER, returnType);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}

View File

@ -74,8 +74,7 @@ public class MappingJackson2MessageConverterTests {
@Test
public void fromMessage() throws Exception {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],"
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
MyBean actual = (MyBean) converter.fromMessage(message, MyBean.class);
@ -178,20 +177,25 @@ public class MappingJackson2MessageConverterTests {
}
@Test
public void jsonView() throws Exception {
public void toMessageJsonView() throws Exception {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
Map<String, Object> map = new HashMap<>();
Method method = this.getClass().getDeclaredMethod("handle");
Method method = getClass().getDeclaredMethod("jsonViewResponse");
MethodParameter returnType = new MethodParameter(method, -1);
map.put(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
MessageHeaders headers = new MessageHeaders(map);
Message<?> message = converter.toMessage(handle(), headers);
Message<?> message = converter.toMessage(jsonViewResponse(), new MessageHeaders(map), returnType);
String actual = new String((byte[]) message.getPayload(), UTF_8);
assertThat(actual, containsString("\"withView1\":\"with\""));
assertThat(actual, not(containsString("\"withView2\":\"with\"")));
assertThat(actual, not(containsString("\"withoutView\":\"without\"")));
assertThat(actual, containsString("\"withView2\":\"with\""));
assertThat(actual, not(containsString("\"withoutView\":\"with\"")));
method = getClass().getDeclaredMethod("jsonViewPayload", JacksonViewBean.class);
MethodParameter param = new MethodParameter(method, 0);
JacksonViewBean back = (JacksonViewBean) converter.fromMessage(message, JacksonViewBean.class, param);
assertNull(back.getWithView1());
assertEquals("with", back.getWithView2());
assertNull(back.getWithoutView());
}
@ -266,7 +270,7 @@ public class MappingJackson2MessageConverterTests {
@JsonView(MyJacksonView1.class)
private String withView1;
@JsonView(MyJacksonView2.class)
@JsonView({MyJacksonView1.class, MyJacksonView2.class})
private String withView2;
private String withoutView;
@ -297,12 +301,15 @@ public class MappingJackson2MessageConverterTests {
}
@JsonView(MyJacksonView1.class)
public JacksonViewBean handle() {
public JacksonViewBean jsonViewResponse() {
JacksonViewBean bean = new JacksonViewBean();
bean.setWithView1("with");
bean.setWithView2("with");
bean.setWithoutView("without");
bean.setWithoutView("with");
return bean;
}
public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) {
}
}

View File

@ -38,7 +38,6 @@ import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
@ -143,7 +142,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void sendToNoAnnotations() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app", "/dest", null);
@ -156,12 +154,11 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/topic/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendTo() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -175,19 +172,18 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/dest1", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
accessor = getCapturedAccessor(1);
assertEquals(sessionId, accessor.getSessionId());
assertEquals("/dest2", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendToDefaultDestination() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -201,12 +197,11 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/topic/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToDefaultDestReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToDefaultDestReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendToDefaultDestinationWhenUsingDotPathSeparator() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app/", "dest.foo.bar", null);
@ -220,7 +215,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void testHeadersToSend() throws Exception {
Message<?> inputMessage = createInputMessage("sess1", "sub1", "/app", "/dest", null);
SimpMessageSendingOperations messagingTemplate = Mockito.mock(SimpMessageSendingOperations.class);
@ -237,12 +231,11 @@ public class SendToMethodReturnValueHandlerTests {
assertTrue(accessor.isMutable());
assertEquals("sess1", accessor.getSessionId());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendToUser() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -263,11 +256,8 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/user/" + user.getName() + "/dest2", accessor.getDestination());
}
// SPR-12170
@Test
@Test // SPR-12170
public void sendToWithDestinationPlaceholders() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
Map<String, String> vars = new LinkedHashMap<>(1);
@ -290,7 +280,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void sendToUserSingleSession() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -305,19 +294,18 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals(MIME_TYPE, accessor.getContentType());
assertEquals("/user/" + user.getName() + "/dest1", accessor.getDestination());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
accessor = getCapturedAccessor(1);
assertEquals(sessionId, accessor.getSessionId());
assertEquals("/user/" + user.getName() + "/dest2", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendToUserWithUserNameProvider() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -336,7 +324,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void sendToUserDefaultDestination() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -354,7 +341,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void sendToUserDefaultDestinationWhenUsingDotPathSeparator() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
TestUser user = new TestUser();
@ -369,7 +355,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void sendToUserDefaultDestinationSingleSession() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -384,12 +369,11 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/user/" + user.getName() + "/queue/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionDefaultDestReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.sendToUserSingleSessionDefaultDestReturnType, accessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void sendToUserSessionWithoutUserName() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -409,7 +393,6 @@ public class SendToMethodReturnValueHandlerTests {
@Test
public void jsonView() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -420,7 +403,7 @@ public class SendToMethodReturnValueHandlerTests {
Message<?> message = this.messageCaptor.getValue();
assertNotNull(message);
assertEquals("{\"withView1\":\"with\"}", new String((byte[])message.getPayload(), StandardCharsets.UTF_8));
assertEquals("{\"withView1\":\"with\"}", new String((byte[]) message.getPayload(), StandardCharsets.UTF_8));
}
@ -515,8 +498,8 @@ public class SendToMethodReturnValueHandlerTests {
}
private interface MyJacksonView1 {};
private interface MyJacksonView2 {};
private interface MyJacksonView1 {}
private interface MyJacksonView2 {}
@SuppressWarnings("unused")
private static class JacksonViewBean {

View File

@ -34,7 +34,6 @@ import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
@ -82,7 +81,6 @@ public class SubscriptionMethodReturnValueHandlerTests {
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
@ -116,7 +114,6 @@ public class SubscriptionMethodReturnValueHandlerTests {
@Test
public void testMessageSentToChannel() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -138,13 +135,12 @@ public class SubscriptionMethodReturnValueHandlerTests {
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
assertEquals(destination, headerAccessor.getDestination());
assertEquals(MIME_TYPE, headerAccessor.getContentType());
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testHeadersPassedToMessagingTemplate() throws Exception {
String sessionId = "sess1";
String subscriptionId = "subs1";
String destination = "/dest";
@ -165,12 +161,11 @@ public class SubscriptionMethodReturnValueHandlerTests {
assertTrue(headerAccessor.isMutable());
assertEquals(sessionId, headerAccessor.getSessionId());
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER));
}
@Test
public void testJsonView() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
@ -184,7 +179,7 @@ public class SubscriptionMethodReturnValueHandlerTests {
Message<?> message = this.messageCaptor.getValue();
assertNotNull(message);
assertEquals("{\"withView1\":\"with\"}", new String((byte[])message.getPayload(), StandardCharsets.UTF_8));
assertEquals("{\"withView1\":\"with\"}", new String((byte[]) message.getPayload(), StandardCharsets.UTF_8));
}