diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java index e70394e14d..1f1c937c2e 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java @@ -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 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; + } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/ByteArrayMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/ByteArrayMessageConverter.java index 921db92649..a89790aa12 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/ByteArrayMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/ByteArrayMessageConverter.java @@ -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; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java index 426fcb6d33..94749cd694 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java @@ -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]; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java index 83955d9631..12d9d8d8bb 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java @@ -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()) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/StringMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/StringMessageConverter.java index b43bd14a0a..a0d094aa1b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/StringMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/StringMessageConverter.java @@ -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); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/core/AbstractMessageSendingTemplate.java b/spring-messaging/src/main/java/org/springframework/messaging/core/AbstractMessageSendingTemplate.java index aae1a736a0..ca98c9e270 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/core/AbstractMessageSendingTemplate.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/core/AbstractMessageSendingTemplate.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. @@ -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 implements MessageSendingOperations { + /** + * 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 implements MessageSendin */ protected Message doConvert(Object payload, Map headers, MessagePostProcessor postProcessor) { MessageHeaders messageHeaders = null; + Object conversionHint = (headers != null ? headers.get(CONVERSION_HINT_HEADER) : null); + Map headersToUse = processHeadersToSend(headers); if (headersToUse != null) { if (headersToUse instanceof MessageHeaders) { @@ -154,7 +166,10 @@ public abstract class AbstractMessageSendingTemplate 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 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. *

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 processHeadersToSend(Map headers) { return headers; diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java index 09d1c1df7b..d65cfedd2f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java @@ -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; } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java index 2305b799a5..4a340304e6 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java @@ -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(); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java index e825122736..b0fa04bce0 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandler.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,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(); } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java index 7c46a272c1..edc5b5a70b 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java @@ -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 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) { + } + } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java index 78a2bb02be..277f770130 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java @@ -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 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 { diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java index 3ad80bd9b0..ceaa4f0f72 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java @@ -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)); }