From dc8de514089f23766ee24f681d0db6003ca71fdf Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 22 Jul 2015 15:15:24 +0200 Subject: [PATCH] Json view support for JMS Support of @JsonView on @JmsListener annotated method that uses the jackson converter. Also update MappingJackson2MessageConverter to offer a public API to set the JSON view to use to serialize a payload. Issue: SPR-13237 --- .../AbstractAdaptableMessageListener.java | 40 +++-- .../MessagingMessageListenerAdapter.java | 16 +- .../MappingJackson2MessageConverter.java | 159 +++++++++++++++++- .../converter/MessagingMessageConverter.java | 23 ++- .../converter/SmartMessageConverter.java | 51 ++++++ .../MessagingMessageListenerAdapterTests.java | 35 +++- .../MappingJackson2MessageConverterTests.java | 134 ++++++++++++++- 7 files changed, 439 insertions(+), 19 deletions(-) create mode 100644 spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java index 10ae1cf820..57a1a53088 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -36,6 +36,7 @@ import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.converter.MessagingMessageConverter; import org.springframework.jms.support.converter.SimpleMessageConverter; +import org.springframework.jms.support.converter.SmartMessageConverter; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.jms.support.destination.DynamicDestinationResolver; import org.springframework.util.Assert; @@ -269,14 +270,17 @@ public abstract class AbstractAdaptableMessageListener * @see #setMessageConverter */ protected Message buildMessage(Session session, Object result) throws JMSException { - Object content = (result instanceof JmsResponse ? ((JmsResponse) result).getResponse() : result); - if (content instanceof org.springframework.messaging.Message) { - return this.messagingMessageConverter.toMessage(content, session); - } + Object content = preProcessResponse(result instanceof JmsResponse + ? ((JmsResponse) result).getResponse() : result); MessageConverter converter = getMessageConverter(); if (converter != null) { - return converter.toMessage(content, session); + if (content instanceof org.springframework.messaging.Message) { + return this.messagingMessageConverter.toMessage(content, session); + } + else { + return converter.toMessage(content, session); + } } if (!(content instanceof Message)) { @@ -286,6 +290,17 @@ public abstract class AbstractAdaptableMessageListener return (Message) content; } + /** + * Pre-process the given result before it is converted to a {@link Message}. + * @param result the result of the invocation + * @return the payload response to handle, either the {@code result} argument or any other + * object (for instance wrapping the result). + * @since 4.3 + */ + protected Object preProcessResponse(Object result) { + return result; + } + /** * Post-process the given response message before it will be sent. *

The default implementation sets the response's correlation id @@ -425,12 +440,17 @@ public abstract class AbstractAdaptableMessageListener } @Override - protected Message createMessageForPayload(Object payload, Session session) throws JMSException { + protected Message createMessageForPayload(Object payload, Session session, Object conversionHint) + throws JMSException { MessageConverter converter = getMessageConverter(); - if (converter != null) { - return converter.toMessage(payload, session); + if (converter == null) { + throw new IllegalStateException("No message converter, cannot handle '" + payload + "'"); + } + if (converter instanceof SmartMessageConverter) { + return ((SmartMessageConverter) converter).toMessage(payload, session, conversionHint); + } - throw new IllegalStateException("No message converter - cannot handle [" + payload + "]"); + return converter.toMessage(payload, session); } } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java index 0791f4c400..b87dd6b236 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -19,11 +19,14 @@ package org.springframework.jms.listener.adapter; import javax.jms.JMSException; import javax.jms.Session; +import org.springframework.core.MethodParameter; import org.springframework.jms.support.JmsHeaderMapper; import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; +import org.springframework.messaging.core.AbstractMessageSendingTemplate; import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; +import org.springframework.messaging.support.MessageBuilder; /** * A {@link javax.jms.MessageListener} adapter that invokes a configurable @@ -72,6 +75,17 @@ public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageLis } } + @Override + protected Object preProcessResponse(Object result) { + MethodParameter returnType = this.handlerMethod.getReturnType(); + if (result instanceof Message) { + return MessageBuilder.fromMessage((Message) result) + .setHeader(AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build(); + } + return MessageBuilder.withPayload(result).setHeader( + AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build(); + } + protected Message toMessagingMessage(javax.jms.Message jmsMessage) { try { return (Message) getMessagingMessageConverter().fromMessage(jmsMessage); diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java index c9805aedf4..c6fc97cdd7 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,12 +29,15 @@ import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; +import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.core.MethodParameter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -55,9 +58,10 @@ import org.springframework.util.ClassUtils; * @author Mark Pollack * @author Dave Syer * @author Juergen Hoeller + * @author Stephane Nicoll * @since 3.1.4 */ -public class MappingJackson2MessageConverter implements MessageConverter, BeanClassLoaderAware { +public class MappingJackson2MessageConverter implements SmartMessageConverter, BeanClassLoaderAware { /** * The default encoding used for writing to text messages: UTF-8. @@ -189,6 +193,33 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl return message; } + @Override + public Message toMessage(Object object, Session session, Object conversionHint) + throws JMSException, MessageConversionException { + return toMessage(object, session, getSerializationView(conversionHint)); + } + + /** + * Convert a Java object to a JMS Message using the specified json view + * and the supplied session to create the message object. + * @param object the object to convert + * @param session the Session to use for creating a JMS Message + * @param jsonView the view to use to filter the content + * @return the JMS Message + * @throws javax.jms.JMSException if thrown by JMS API methods + * @throws MessageConversionException in case of conversion failure + * @since 4.3 + */ + public Message toMessage(Object object, Session session, Class jsonView) + throws JMSException, MessageConversionException { + if (jsonView != null) { + return toMessage(object, session, this.objectMapper.writerWithView(jsonView)); + } + else { + return toMessage(object, session, this.objectMapper.writer()); + } + } + @Override public Object fromMessage(Message message) throws JMSException, MessageConversionException { try { @@ -200,6 +231,28 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } + protected Message toMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, MessageConversionException { + Message message; + try { + switch (this.targetType) { + case TEXT: + message = mapToTextMessage(object, session, objectWriter); + break; + case BYTES: + message = mapToBytesMessage(object, session, objectWriter); + break; + default: + message = mapToMessage(object, session, objectWriter, this.targetType); + } + } + catch (IOException ex) { + throw new MessageConversionException("Could not map JSON object [" + object + "]", ex); + } + setTypeIdOnMessage(object, message); + return message; + } + /** * Map the given object to a {@link TextMessage}. @@ -210,12 +263,31 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors * @see Session#createBytesMessage + * @deprecated as of 4.3, use {@link #mapToTextMessage(Object, Session, ObjectWriter)} */ + @Deprecated protected TextMessage mapToTextMessage(Object object, Session session, ObjectMapper objectMapper) throws JMSException, IOException { + return mapToTextMessage(object, session, objectMapper.writer()); + } + + /** + * Map the given object to a {@link TextMessage}. + * @param object the object to be mapped + * @param session current JMS session + * @param objectWriter the writer to use + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + * @see Session#createBytesMessage + * @since 4.3 + */ + protected TextMessage mapToTextMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, IOException { + StringWriter writer = new StringWriter(); - objectMapper.writeValue(writer, object); + objectWriter.writeValue(writer, object); return session.createTextMessage(writer.toString()); } @@ -228,13 +300,33 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors * @see Session#createBytesMessage + * @deprecated as of 4.3, use {@link #mapToBytesMessage(Object, Session, ObjectWriter)} */ + @Deprecated protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectMapper objectMapper) throws JMSException, IOException { + return mapToBytesMessage(object, session, objectMapper.writer()); + } + + + /** + * Map the given object to a {@link BytesMessage}. + * @param object the object to be mapped + * @param session current JMS session + * @param objectWriter the writer to use + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + * @see Session#createBytesMessage + * @since 4.3 + */ + protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding); - objectMapper.writeValue(writer, object); + objectWriter.writeValue(writer, object); BytesMessage message = session.createBytesMessage(); message.writeBytes(bos.toByteArray()); @@ -256,10 +348,31 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @return the resulting message * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors + * @deprecated as of 4.3, use {@link #mapToMessage(Object, Session, ObjectWriter, MessageType)} */ + @Deprecated protected Message mapToMessage(Object object, Session session, ObjectMapper objectMapper, MessageType targetType) throws JMSException, IOException { + return mapToMessage(object, session, objectMapper.writer(), targetType); + } + + /** + * Template method that allows for custom message mapping. + * Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or + * {@link MessageType#BYTES}. + *

The default implementation throws an {@link IllegalArgumentException}. + * @param object the object to marshal + * @param session the JMS Session + * @param objectWriter the writer to use + * @param targetType the target message type (other than TEXT or BYTES) + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + */ + protected Message mapToMessage(Object object, Session session, ObjectWriter objectWriter, MessageType targetType) + throws JMSException, IOException { + throw new IllegalArgumentException("Unsupported message type [" + targetType + "]. MappingJackson2MessageConverter by default only supports TextMessages and BytesMessages."); } @@ -391,4 +504,42 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } + /** + * 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: " + conversionHint); + } + return classes[0]; + } + } diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java index 6b793fa363..1c3b88a6e0 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java @@ -24,6 +24,7 @@ import org.springframework.jms.support.JmsHeaderMapper; import org.springframework.jms.support.SimpleJmsHeaderMapper; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.core.AbstractMessagingTemplate; import org.springframework.util.Assert; /** @@ -92,8 +93,11 @@ public class MessagingMessageConverter implements MessageConverter, Initializing Message.class.getName() + "] is handled by this converter"); } Message input = (Message) object; - javax.jms.Message reply = createMessageForPayload(input.getPayload(), session); - this.headerMapper.fromHeaders(input.getHeaders(), reply); + MessageHeaders headers = input.getHeaders(); + Object conversionHint = (headers != null ? headers.get( + AbstractMessagingTemplate.CONVERSION_HINT_HEADER) : null); + javax.jms.Message reply = createMessageForPayload(input.getPayload(), session, conversionHint); + this.headerMapper.fromHeaders(headers, reply); return reply; } @@ -116,11 +120,26 @@ public class MessagingMessageConverter implements MessageConverter, Initializing /** * Create a JMS message for the specified payload. * @see MessageConverter#toMessage(Object, Session) + * @deprecated as of 4.3, use {@link #createMessageForPayload(Object, Session, Object)} */ + @Deprecated protected javax.jms.Message createMessageForPayload(Object payload, Session session) throws JMSException { return this.payloadConverter.toMessage(payload, session); } + /** + * Create a JMS message for the specified payload and conversionHint. The conversion + * hint is an extra object passed to the {@link MessageConverter}, e.g. the associated + * {@code MethodParameter} (may be {@code null}}. + * @see MessageConverter#toMessage(Object, Session) + * @since 4.3 + */ + @SuppressWarnings("deprecation") + protected javax.jms.Message createMessageForPayload(Object payload, Session session, Object conversionHint) + throws JMSException { + return createMessageForPayload(payload, session); + } + private MessageHeaders extractHeaders(javax.jms.Message message) { return this.headerMapper.toHeaders(message); } diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java new file mode 100644 index 0000000000..2fb5cd734e --- /dev/null +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jms.support.converter; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * An extended {@link MessageConverter} SPI with conversion hint support. + * + *

In case of a conversion hint being provided, the framework will call + * the extended method if a converter implements this interface, instead + * of calling the regular {@code toMessage} variant. + * + * @author Stephane Nicoll + * @since 4.3 + */ +public interface SmartMessageConverter extends MessageConverter { + + /** + * A variant of {@link #toMessage(Object, Session)} which takes an extra conversion + * context as an argument, allowing to take e.g. annotations on a payload parameter + * into account. + * @param object the object to convert + * @param session the Session to use for creating a JMS Message + * @param conversionHint an extra object passed to the {@link MessageConverter}, + * e.g. the associated {@code MethodParameter} (may be {@code null}} + * @return the JMS Message + * @throws javax.jms.JMSException if thrown by JMS API methods + * @throws MessageConversionException in case of conversion failure + * @see #toMessage(Object, Session) + */ + Message toMessage(Object object, Session session, Object conversionHint) + throws JMSException, MessageConversionException; + +} diff --git a/spring-jms/src/test/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapterTests.java b/spring-jms/src/test/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapterTests.java index 9f87143aa5..0b574b36f3 100644 --- a/spring-jms/src/test/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapterTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapterTests.java @@ -27,6 +27,7 @@ import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; +import com.fasterxml.jackson.annotation.JsonView; import org.junit.Before; import org.junit.Test; @@ -236,7 +237,21 @@ public class MessagingMessageListenerAdapterTests { verify(reply).setObjectProperty("foo", "bar"); } - private TextMessage testReplyWithJackson(String methodName, String replyContent) throws JMSException { + @Test + public void replyJacksonMessageAndJsonView() throws JMSException { + TextMessage reply = testReplyWithJackson("replyJacksonMessageAndJsonView", + "{\"name\":\"Response\"}"); + verify(reply).setObjectProperty("foo", "bar"); + } + + @Test + public void replyJacksonPojoAndJsonView() throws JMSException { + TextMessage reply = testReplyWithJackson("replyJacksonPojoAndJsonView", + "{\"name\":\"Response\"}"); + verify(reply, never()).setObjectProperty("foo", "bar"); + } + + public TextMessage testReplyWithJackson(String methodName, String replyContent) throws JMSException { Queue replyDestination = mock(Queue.class); Session session = mock(Session.class); @@ -327,6 +342,17 @@ public class MessagingMessageListenerAdapterTests { .setHeader("foo", "bar").build(); } + @JsonView(Summary.class) + public Message replyJacksonMessageAndJsonView(Message input) { + return MessageBuilder.withPayload(createSampleResponse(input.getPayload())) + .setHeader("foo", "bar").build(); + } + + @JsonView(Summary.class) + public SampleResponse replyJacksonPojoAndJsonView(Message input) { + return createSampleResponse(input.getPayload()); + } + private SampleResponse createSampleResponse(String name) { return new SampleResponse(name, "lengthy description"); } @@ -340,15 +366,22 @@ public class MessagingMessageListenerAdapterTests { } } + interface Summary {}; + interface Full extends Summary {}; private static class SampleResponse { private int counter = 42; + @JsonView(Summary.class) private String name; + @JsonView(Full.class) private String description; + SampleResponse() { + } + public SampleResponse(String name, String description) { this.name = name; this.description = description; diff --git a/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJackson2MessageConverterTests.java b/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJackson2MessageConverterTests.java index af9e2c7599..fcf8ed16b1 100644 --- a/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJackson2MessageConverterTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJackson2MessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -17,28 +17,39 @@ package org.springframework.jms.support.converter; import java.io.ByteArrayInputStream; +import java.lang.reflect.Method; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.jms.BytesMessage; +import javax.jms.JMSException; import javax.jms.Session; import javax.jms.TextMessage; +import com.fasterxml.jackson.annotation.JsonView; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.springframework.core.MethodParameter; + import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * @author Arjen Poutsma * @author Dave Syer + * @author Stephane Nicoll */ public class MappingJackson2MessageConverterTests { + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private MappingJackson2MessageConverter converter; private Session sessionMock; @@ -167,6 +178,91 @@ public class MappingJackson2MessageConverterTests { assertEquals("Invalid result", result, unmarshalled); } + @Test + public void toTextMessageWithReturnType() throws JMSException, NoSuchMethodException { + Method method = this.getClass().getDeclaredMethod("summary"); + MethodParameter returnType = new MethodParameter(method, -1); + testToTextMessageWithReturnType(returnType); + verify(sessionMock).createTextMessage("{\"name\":\"test\"}"); + } + + @Test + public void toTextMessageWithNullReturnType() throws JMSException, NoSuchMethodException { + testToTextMessageWithReturnType(null); + verify(sessionMock).createTextMessage("{\"name\":\"test\",\"description\":\"lengthy description\"}"); + } + + @Test + public void toTextMessageWithReturnTypeAndNoJsonView() throws JMSException, NoSuchMethodException { + Method method = this.getClass().getDeclaredMethod("none"); + MethodParameter returnType = new MethodParameter(method, -1); + + testToTextMessageWithReturnType(returnType); + verify(sessionMock).createTextMessage("{\"name\":\"test\",\"description\":\"lengthy description\"}"); + } + + @Test + public void toTextMessageWithReturnTypeAndMultipleJsonViews() throws JMSException, NoSuchMethodException { + Method method = this.getClass().getDeclaredMethod("invalid"); + MethodParameter returnType = new MethodParameter(method, -1); + + thrown.expect(IllegalArgumentException.class); + testToTextMessageWithReturnType(returnType); + } + + private void testToTextMessageWithReturnType(MethodParameter returnType) throws JMSException, NoSuchMethodException { + converter.setTargetType(MessageType.TEXT); + TextMessage textMessageMock = mock(TextMessage.class); + + MyAnotherBean bean = new MyAnotherBean("test", "lengthy description"); + given(sessionMock.createTextMessage(isA(String.class))).willReturn(textMessageMock); + converter.toMessage(bean, sessionMock, returnType); + verify(textMessageMock).setStringProperty("__typeid__", MyAnotherBean.class.getName()); + } + + @Test + public void toTextMessageWithJsonViewClass() throws JMSException { + converter.setTargetType(MessageType.TEXT); + TextMessage textMessageMock = mock(TextMessage.class); + + MyAnotherBean bean = new MyAnotherBean("test", "lengthy description"); + given(sessionMock.createTextMessage(isA(String.class))).willReturn(textMessageMock); + + + converter.toMessage(bean, sessionMock, Summary.class); + verify(textMessageMock).setStringProperty("__typeid__", MyAnotherBean.class.getName()); + verify(sessionMock).createTextMessage("{\"name\":\"test\"}"); + } + + @Test + public void toTextMessageWithAnotherJsonViewClass() throws JMSException { + converter.setTargetType(MessageType.TEXT); + TextMessage textMessageMock = mock(TextMessage.class); + + MyAnotherBean bean = new MyAnotherBean("test", "lengthy description"); + given(sessionMock.createTextMessage(isA(String.class))).willReturn(textMessageMock); + + + converter.toMessage(bean, sessionMock, Full.class); + verify(textMessageMock).setStringProperty("__typeid__", MyAnotherBean.class.getName()); + verify(sessionMock).createTextMessage("{\"name\":\"test\",\"description\":\"lengthy description\"}"); + } + + + @JsonView(Summary.class) + public MyAnotherBean summary() { + return new MyAnotherBean(); + } + + public MyAnotherBean none() { + return new MyAnotherBean(); + } + + @JsonView({Summary.class, Full.class}) + public MyAnotherBean invalid() { + return new MyAnotherBean(); + } + public static class MyBean { public MyBean() { @@ -210,4 +306,40 @@ public class MappingJackson2MessageConverterTests { } } + private interface Summary {}; + private interface Full extends Summary {}; + + private static class MyAnotherBean { + + @JsonView(Summary.class) + private String name; + + @JsonView(Full.class) + private String description; + + private MyAnotherBean() { + } + + public MyAnotherBean(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + } + }