-
Notifications
You must be signed in to change notification settings - Fork 38.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
please add JMS JsonMessageConverter [SPR-7404] #12062
Comments
James Hoare commented I based the following version on the amqp one, it just uses a Map rather than the class mapper strategy and also uses the JmsType header (helpful if your using AMQ's admin web app for testing). Would be great to get something like this in Spring JMS. import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageConversionException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Map;
/**
* JSON converter that uses the Jackson Json library.
*/
public class JsonMessageConverter implements MessageConverter {
private static Log log = LogFactory.getLog(JsonMessageConverter.class);
private ObjectMapper jsonObjectMapper = new ObjectMapper();
private Map<String, Class> idClassMapping;
public JsonMessageConverter() {
initializeJsonObjectMapper();
}
/**
* Map of key message type to value FQN
* @param idClassMapping
*/
public void setIdClassMapping(Map<String, Class> idClassMapping) {
this.idClassMapping = idClassMapping;
}
/**
* A simple check against the map of class simple name key to the FQN name of the class
*
* @param classId a string value that should be a property of the message header
* @return Class to convert to
*/
public Class toClass(String classId) {
if (classId == null) {
throw new MessageConversionException(
"failed to convert json-based Message content. Could not resolve JmsType in header, value was null");
}
if (this.idClassMapping.containsKey(classId)) {
return idClassMapping.get(classId);
}
throw new MessageConversionException(
"failed to convert json-based Message content. Could not resolve JmsType in header, value was null");
}
/**
* Subclass and override to customize.
*/
protected void initializeJsonObjectMapper() {
jsonObjectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
jsonObjectMapper.configure(SerializationConfig.Feature.AUTO_DETECT_FIELDS, true);
jsonObjectMapper.configure(SerializationConfig.Feature.USE_ANNOTATIONS, true);
jsonObjectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
jsonObjectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true);
//iso-8601 format
jsonObjectMapper.getDeserializationConfig().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
}
/**
* Convert a Java object to a JMS Message using 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
* @return the JMS Message
* @throws javax.jms.JMSException if thrown by JMS API methods
* @throws org.springframework.jms.support.converter.MessageConversionException
* in case of conversion failure
*/
@Override
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
Message message = null;
try {
String json = jsonObjectMapper.writeValueAsString(object);
log.info(object + " converted to " + json);
message = session.createTextMessage(json);
message.setJMSType(object.getClass().getSimpleName());
return message;
} catch (JsonParseException e) {
throw new MessageConversionException("Failed to convert Message content", e);
} catch (JsonMappingException e) {
throw new MessageConversionException("Failed to convert Message content", e);
} catch (Exception e) {
throw new MessageConversionException("Failed to convert Message content", e);
}
}
/**
* Convert from a JMS Message to a Java object.
*
* @param message the message to convert
* @return the converted Java object
* @throws javax.jms.JMSException if thrown by JMS API methods
* @throws org.springframework.jms.support.converter.MessageConversionException
* in case of conversion failure
*/
@Override
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
Class clazzToConvertTo = toClass(message.getJMSType());
try {
Object converted = jsonObjectMapper.readValue(textMessage.getText(), clazzToConvertTo);
log.info("Jms Message " + textMessage.getText() + " converted to " + converted);
return converted;
} catch (JsonParseException e) {
throw new MessageConversionException("Mapping of JSON message " + textMessage.getText() + " directly to payload of type " + clazzToConvertTo.getName() + " failed.", e);
} catch (JsonMappingException e) {
throw new MessageConversionException("Mapping of JSON message " + textMessage.getText() + " directly to payload of type " + clazzToConvertTo.getName() + " failed.", e);
} catch (IOException e) {
throw new MessageConversionException("Mapping of JSON message " + textMessage.getText() + " directly to payload of type " + clazzToConvertTo.getName() + " failed.", e);
}
} else {
throw new MessageConversionException("Only support JMSTextMessage, received: " + message);
}
}
} |
Kyrill Alyoshin commented Does this thing even work? It seems SI in "synchronous" interaction overrides outbound JMSType header on a with the original inbound JMSType header, which effectively makes it impossible to correctly determine the the Java type to convert to. It seems converters have to be specified for each gateway where Java type is explicitly defined. |
Michael Pilone commented I stumbled on this feature request while searching for a good way to build a JMS solution using JSON message payloads. While the JsonMessageConverter would be nice, it is pretty easy to write one by hand. I think the bigger issue here is Kyrill's comment "It seems converters have to be specified for each gateway where Java type is explicitly defined." Even if we had a JsonMessageConverter, having to instantiate a different converter for each target type isn't great. It may be that in combination with a json converter, a better adapter is needed which passes the target Object type to the converter so adapting the message to a method call can take the parameters into account during unmarshalling. This is important for any format that doesn't have the type information encoded in the payload (like XML might). For example, if you wanted to use ProtocolBuffers as the format. |
Dave Syer commented I can't comment directly on the behaviour of Spring Integration and overriding JMS headers (and it doesn't seem relevant here, so maybe an INT-* issue would be useful to track that separate discussion?). The JsonMessageConverter in Spring AMQP is used pretty successfully by a lot of projects already, so the approach in general seems to be valid. It's really just a question of determining for each message what Java type the boy needs to be converted to - that could be a complex decision, which is why the AMQP case uses a strategy with a sensible default implementation. I think I would recommend the same design here (extending James's suggestion, or copying Spring AMQP depending which way you look at it). As you say, it is pretty trivial to implement, so there is hardly any barrier to trying it to see if it works, as I expect James already has. |
Dave Syer commented Added a converter based on Spring AMQP with Java types specified in custom headers (message properties) |
John Thoms opened SPR-7404 and commented
org.springframework.jms.support.converter.JsonMessageConverter would handle marshalling of JMS payloads similiar to MappingJacksonHttpMessageConverter for http and
spring-amqp/org.springframework.amqp.support.converter.JsonMessageConverter
Reference URL: http://forum.springsource.org/showthread.php?p=311501
Issue Links:
Referenced from: commits 1adf825, 7ec9292
1 votes, 1 watchers
The text was updated successfully, but these errors were encountered: