I am sending JMS messages with XML payloads and some custom headers contentType
with values text/json
and text/xml
using Jmeter.
My Spring integration configuration looks like this:
<jms:message-driven-channel-adapter channel="jmsInChannel" destination-name="queue.demo" connection-factory="jmsConnectionFactory1" />
<int:channel id="jmsInChannel" />
<int:header-value-router input-channel="jmsInChannel" header-name="contentType" default-output-channel="nullChannel">
<int:mapping value="text/json" channel="jsonTransformerChannel" />
<int:mapping value="text/xml" channel="xmlTransformerChannel" />
</int:header-value-router>
Up to this point, everything works perfectly, the messages are successfully routed to their respective transformers.
My problem is, when it's an XML payload I first used a JAXB Unmarshaller along with <ixml:unmarshalling-transofmer ../>
provided by http://www.springframework.org/schema/integration/xml
.
I could get the payload, but it couldn't process the message afterwards as a JMS message, it became a pure POJO. So I lost the headers, and I couldn't use <int: ../>
components without serializing the POJO, which is not what I wanted to achieve.
I have found a work-around, where I defined my own Unmarshalling bean in java like so:
<int:channel id="xmlTransformerChannel" />
<int:transformer input-channel="xmlTransformerChannel" ref="xmlMsgToCustomerPojoTransformer" output-channel="enrichInChannel" />
method:
@SuppressWarnings("rawtypes")
public Message transform(String message) {
logger.info("Message Received \r\n" + message);
try {
MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message)));
return MessageBuilder.withPayload(myModel).build();
} catch (XmlMappingException e) {
return MessageBuilder.withPayload(e).build();
} catch (Exception e) {
return MessageBuilder.withPayload(e).build();
}
}
I could successfully process the message as a Spring integration Message, but I lost the original custom JMS headers.
In contrast, all I had to do to transform the json payloads and keep the Message format AND preserving my custom headers is this xml configuration:
<int:channel id="jsonTransformerChannel" />
<int:json-to-object-transformer input-channel="jsonTransformerChannel" output-channel="enrichInChannel" type="com.alrawas.ig5.MyModel" />
My question is, how to keep the original custom JMS headers after unmarshalling the xml payload?
UPDATE:
I did try to write the xml transformer this way, taking Message as input instead of only string, it did not throw exception in this method, but it did later in the routing phase
public Message<?> transform(Message<String> message) {
logger.info("Message Received \r\n" + message);
try {
MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message.getPayload())));
return (Message<MyModel>) MessageBuilder.withPayload(myModel).copyHeaders(message.getHeaders()).build();
} catch (XmlMappingException e) {
return MessageBuilder.withPayload(e).build();
} catch (Exception e) {
return MessageBuilder.withPayload(e).build();
}
}
I ran into a problem in a component I use later in this flow:
<int:object-to-json-transformer input-channel="outr" output-channel="outch" />
<int:router method="route" input-channel="outch" default-output-channel="nullChannel">
<bean class="com.alrawas.ig5.MyCustomRouter" />
</int:router>
In my route method threw Cannot cast String to com.alrawas.ig5.MyModel Exception:
public class MyCustomRouter {
public String route(Message<MyModel> myModel) {
Integer tenNumber = myModel.getPayload().getNumber(); // <-- Cast Exception here
System.out.println(myModel);
return (tenNumber % 10 == 0) ? "stayLocal" : "goRemote";
}
}
This Cast Exception only happens after unmarshalling xml, JSON payloads work fine without losing headers or throwing cast exceptions.
UPDATE:
Check my answer below: contentType was not really a custom header