diff options
Diffstat (limited to 'spring-jms/src/main/java/org/springframework/jms/support/converter')
3 files changed, 239 insertions, 10 deletions
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 b64abf2f..5d9bc44b 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-2014 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; @@ -50,14 +53,15 @@ import org.springframework.util.ClassUtils; * <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li> * </ul> * - * <p>Tested against Jackson 2.2; compatible with Jackson 2.0 and higher. + * <p>Compatible with Jackson 2.6 and higher, as of Spring 4.3. * * @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. @@ -190,6 +194,35 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } @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 { JavaType targetJavaType = getJavaTypeForMessage(message); @@ -200,6 +233,29 @@ 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 +266,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 +303,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 + * @since 4.3 + * @see Session#createBytesMessage + */ + 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 +351,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}. + * <p>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."); } @@ -286,7 +402,6 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } - /** * Convenience method to dispatch to converters for individual message types. */ @@ -391,4 +506,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 a4db7431..fbd1d5ad 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 @@ -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. @@ -24,6 +24,8 @@ import org.springframework.beans.factory.InitializingBean; 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.messaging.support.MessageBuilder; import org.springframework.util.Assert; @@ -93,8 +95,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; } @@ -104,7 +109,7 @@ public class MessagingMessageConverter implements MessageConverter, Initializing if (message == null) { return null; } - Map<String, Object> mappedHeaders = this.headerMapper.toHeaders(message); + Map<String, Object> mappedHeaders = extractHeaders(message); Object convertedObject = extractPayload(message); MessageBuilder<Object> builder = (convertedObject instanceof org.springframework.messaging.Message) ? MessageBuilder.fromMessage((org.springframework.messaging.Message<Object>) convertedObject) : @@ -122,9 +127,29 @@ 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); + } + + protected final 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 00000000..2fb5cd73 --- /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. + * + * <p>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; + +} |