Spring Integration's XML support extends the Spring Integration Core with
implementations of splitter, transformer, selector and router designed
to make working with xml messages in Spring Integration simple. The provided messaging
components are designed to work with xml represented in a range of formats including
instances of
java.lang.String
, org.w3c.dom.Document
and javax.xml.transform.Source
. It should be noted however that
where a DOM representation is required, for example in order to evaluate an XPath expression,
the String
payload will be converted into the required type and then
converted back again to String
. Components that require an instance of
DocumentBuilder
will create a namespace aware instance if one is
not provided. Where greater control of the document being created is required an appropriately
configured instance of DocumentBuilder
should be provided.
This section will explain the workings of
UnmarshallingTransformer
,
MarshallingTransformer
,
XsltPayloadTransformer
and how to configure them as
beans. All of the provided xml transformers extend
AbstractTransformer
or AbstractPayloadTransformer
and therefore implement Transformer
. When configuring xml
transformers as beans in Spring Integration you would normally configure the transformer
in conjunction with either a MessageTransformingChannelInterceptor
or a
MessageTransformingHandler
. This allows the transformer to be used as either an interceptor,
which transforms the message as it is sent or received to the channel, or as an endpoint. Finally the
namespace support will be discussed which allows for the simple configuration of the transformers as
elements in XML.
UnmarshallingTransformer
allows an xml Source
to be unmarshalled using implementations of Spring OXM Unmarshaller
.
Spring OXM provides several implementations supporting marshalling and unmarshalling using JAXB,
Castor and JiBX amongst others. Since the unmarshaller requires an instance of
Source
where the message payload is not currently an instance of
Source
, conversion will be attempted. Currently String
and org.w3c.dom.Document
payloads are supported. Custom conversion to a
Source
is also supported by injecting an implementation of
SourceFactory
.
<bean id="unmarshallingTransformer" class="org.springframework.integration.xml.transformer.UnmarshallingTransformer"> <constructor-arg> <bean class="org.springframework.oxm.jaxb.Jaxb1Marshaller"> <property name="contextPath" value="org.example" /> </bean> </constructor-arg> </bean>
The MarshallingTransformer
allows an object graph to be converted
into xml using a Spring OXM Marshaller
. By default the
MarshallingTransformer
will return a DomResult
.
However the type of result can be controlled by configuring an alternative ResultFactory
such as StringResultFactory
. In many cases it will be more convenient to transform
the payload into an alternative xml format. To achieve this configure a
ResultTransformer
. Two implementations are provided, one which converts to
String
and another which converts to Document
.
<bean id="marshallingTransformer" class="org.springframework.integration.xml.transformer.MarshallingTransformer"> <constructor-arg> <bean class="org.springframework.oxm.jaxb.Jaxb1Marshaller"> <property name="contextPath" value="org.example" /> </bean> </constructor-arg> <constructor-arg> <bean class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer" /> </constructor-arg> </bean>
By default, the MarshallingTransformer
will pass the payload Object
to the Marshaller
, but if its boolean "extractPayload" property
is set to "false", the entire Message
instance will be passed
to the Marshaller
instead. That may be useful for certain custom
implementations of the Marshaller
interface, but typically the
payload is the appropriate source Object for marshalling when delegating to any of the various
out-of-the-box Marshaller
implementations.
XsltPayloadTransformer
transforms xml payloads using xsl.
The transformer requires an instance of either Resource
or
Templates
. Passing in a Templates
instance
allows for greater configuration of the TransformerFactory
used to create
the template instance. As in the case of XmlPayloadMarshallingTransformer
by default XsltPayloadTransformer
will create a message with a
Result
payload. This can be customised by providing a
ResultFactory
and/or a ResultTransformer
.
<bean id="xsltPayloadTransformer" class="org.springframework.integration.xml.transformer.XsltPayloadTransformer"> <constructor-arg value="classpath:org/example/xsl/transform.xsl" /> <constructor-arg> <bean class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer" /> </constructor-arg> </bean>
Namespace support for all xml transformers is provided in the Spring Integration xml namespace,
a template for which can be seen below. The namespace support for transformers creates an instance of either
EventDrivenConsumer
or PollingConsumer
according to the type of the provided input channel. The namespace support is designed
to reduce the amount of xml configuration by allowing the creation of an endpoint and transformer
using one element.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:integration="http://www.springframework.org/schema/integration" xmlns:si-xml="http://www.springframework.org/schema/integration/xml" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml-2.0.xsd"> </beans>
The namespace support for UnmarshallingTransformer
is shown below.
Since the namespace is now creating an endpoint instance rather than a transformer,
a poller can also be nested within the element to control the polling of the input channel.
<si-xml:unmarshalling-transformer id="defaultUnmarshaller" input-channel="input" output-channel="output" unmarshaller="unmarshaller"/> <si-xml:unmarshalling-transformer id="unmarshallerWithPoller" input-channel="input" output-channel="output" unmarshaller="unmarshaller"> <si:poller fixed-rate="2000"/> <si-xml:unmarshalling-transformer/>
The namespace support for the marshalling transformer requires an input channel, output channel and a
reference to a marshaller. The optional result-type attribute can be used to control the type of result created,
valid values are StringResult or DomResult (the default). Where the provided result types are not sufficient a
reference to a custom implementation of ResultFactory
can be provided as an alternative
to setting the result-type attribute using the result-factory attribute. An optional result-transformer can also be
specified in order to convert the created Result
after marshalling.
<si-xml:marshalling-transformer input-channel="marshallingTransformerStringResultFactory" output-channel="output" marshaller="marshaller" result-type="StringResult" /> <si-xml:marshalling-transformer input-channel="marshallingTransformerWithResultTransformer" output-channel="output" marshaller="marshaller" result-transformer="resultTransformer" /> <bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer"/>
Namespace support for the XsltPayloadTransformer
allows either a resource to be passed in in order to create the
Templates
instance or alternatively a precreated Templates
instance can be passed in as a reference. In common with the marshalling transformer the type of the result output can
be controlled by specifying either the result-factory or result-type attribute. A result-transfomer attribute can also
be used to reference an implementation of ResultTransfomer
where conversion of the result
is required before sending.
<si-xml:xslt-transformer id="xsltTransformerWithResource" input-channel="withResourceIn" output-channel="output" xsl-resource="org/springframework/integration/xml/config/test.xsl"/> <si-xml:xslt-transformer id="xsltTransformerWithTemplatesAndResultTransformer" input-channel="withTemplatesAndResultTransformerIn" output-channel="output" xsl-templates="templates" result-transformer="resultTransformer"/>
Very often to assist with transformation you may need to have access to Message data (e.g., Message Headers). For example; you may need to get access to certain Message Headers and pass them on as parameters to a transformer (e.g., transformer.setParameter(..)). Spring Integration provides two convenient ways to accomplish this. Just look at the following XML snippet.
<si-xml:xslt-transformer id="paramHeadersCombo" input-channel="paramHeadersComboChannel" output-channel="output" xsl-resource="classpath:transformer.xslt" xslt-param-headers="testP*, *foo, bar, baz"> <int-xml:xslt-param name="helloParameter" value="hello"/> <int-xml:xslt-param name="firstName" expression="headers.fname"/> </int-xml:xslt-transformer>
If message header names match 1:1 to parameter names, you can simply use xslt-param-headers attribute. There you can also use wildcards for simple pattern matching which supports the following simple pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy".
You can also configure individual xslt parameters via xslt-param sub element. There you can use expression
or value
attribute.
The expression
attribute should be any valid SpEL expression with Message being the root object of the expression evaluation context.
The value
attribute just like any value
in Spring beans allows you to specify simple scalar vallue. YOu can also use property placeholders (e.g., ${some.value})
So as you can see, with the expression
and value
attribute xslt parameters could now be mapped to any accessible part of the Message as well as any literal value.
XPathMessageSplitter
supports messages with either
String
or Document
payloads.
The splitter uses the provided XPath expression to split the payload into a number of
nodes. By default this will result in each Node
instance
becoming the payload of a new message. Where it is preferred that each message be a Document
the createDocuments
flag can be set. Where a String
payload is passed
in the payload will be converted then split before being converted back to a number of String
messages. The XPath splitter implements MessageHandler
and should
therefore be configured in conjunction with an appropriate endpoint (see the namespace support below
for a simpler configuration alternative).
<bean id="splittingEndpoint" class="org.springframework.integration.endpoint.EventDrivenConsumer"> <constructor-arg ref="orderChannel" /> <constructor-arg> <bean class="org.springframework.integration.xml.splitter.XPathMessageSplitter"> <constructor-arg value="/order/items" /> <property name="documentBuilder" ref="customisedDocumentBuilder" /> <property name="outputChannel" ref="orderItemsChannel" /> </bean> </constructor-arg> </bean>
Two Router implementations based on XPath are provided XPathSingleChannelRouter
and
XPathMultiChannelRouter
. The implementations differ in respect to how many channels
any given message may be routed to, exactly one in the case of the single channel version
or zero or more in the case of the multichannel router. Both evaluate an XPath
expression against the xml payload of the message, supported payload types by default
are Node
, Document
and
String
. For other payload types a custom implementation
of XmlPayloadConverter
can be provided. The router
implementations use ChannelResolver
to convert the
result(s) of the XPath expression to a channel name. By default a
BeanFactoryChannelResolver
strategy will be used, this means that the string returned by the XPath
evaluation should correspond directly to the name of a channel. Where this is not the case
an alternative implementation of ChannelResolver
can
be used. Where there is a simple mapping from Xpath result to channel name
the provided MapBasedChannelResolver
can be used.
<!-- Expects a channel for each value of order type to exist --> <bean id="singleChannelRoutingEndpoint" class="org.springframework.integration.endpoint.EventDrivenConsumer"> <constructor-arg ref="orderChannel" /> <constructor-arg> <bean class="org.springframework.integration.xml.router.XPathSingleChannelRouter"> <constructor-arg value="/order/@type" /> </bean> </constructor-arg> </bean> <!-- Multi channel router which uses a map channel resolver to resolve the channel name based on the XPath evaluation result Since the router is multi channel it may deliver message to one or both of the configured channels --> <bean id="multiChannelRoutingEndpoint" class="org.springframework.integration.endpoint.EventDrivenConsumer"> <constructor-arg ref="orderChannel" /> <constructor-arg> <bean class="org.springframework.integration.xml.router.XPathMultiChannelRouter"> <constructor-arg value="/order/recipient" /> <property name="channelResolver"> <bean class="org.springframework.integration.channel.MapBasedChannelResolver"> <constructor-arg> <map> <entry key="accounts" value-ref="accountConfirmationChannel" /> <entry key="humanResources" value-ref="humanResourcesConfirmationChannel" /> </map> </constructor-arg> </bean> </property> </bean> </constructor-arg> </bean>
Two MessageSelector
implementations are provided,
BooleanTestXPathMessageSelector
and StringValueTestXPathMessageSelector
.
BooleanTestXPathMessageSelector
requires an XPathExpression which evaluates to a boolean,
for example boolean(/one/two) which will only select messages which have an element named
two which is a child of a root element named one. StringValueTestXPathMessageSelector
evaluates any XPath expression as a String
and compares the result with the provided value.
<!-- Interceptor which rejects messages that do not have a root element order --> <bean id="orderSelectingInterceptor" class="org.springframework.integration.channel.interceptor.MessageSelectingInterceptor"> <constructor-arg> <bean class="org.springframework.integration.xml.selector.BooleanTestXPathMessageSelector"> <constructor-arg value="boolean(/order)" /> </bean> </constructor-arg> </bean> <!-- Interceptor which rejects messages that are not version one orders --> <bean id="versionOneOrderSelectingInterceptor" class="org.springframework.integration.channel.interceptor.MessageSelectingInterceptor"> <constructor-arg> <bean class="org.springframework.integration.xml.selector.StringValueTestXPathMessageSelector"> <constructor-arg value="/order/@version" index="0"/> <constructor-arg value="1" index="1"/> </bean> </constructor-arg> </bean>
When it comes to message transformation XPath is a great way to transform Messages that have XML payloads by defining XPath transformers via xpath-transformer element.
Simple XPath transformation
Let's look at the following transformer configuration:
<xpath-transformer input-channel="inputChannel" output-channel="outputChannel" xpath-expression="/person/@name" />
. . . and Message
Message<?> message =
MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();
After sending this message to the 'inputChannel' the XPath transformer configured above will transform this XML Message to a simple Message with payload of 'John Doe' all based on the simple XPath Expression specified in the xpath-expression attribute.
XPath also has capability to perform simple conversion of extracted elements
to a desired type. Valid return types are defined in XPathConstants
and follows
the conversion rules specified by the XPath
.
The following constants are defined by the XPathConstants
: BOOLEAN, DOM_OBJECT_MODEL, NODE, NODESET, NUMBER, STRING
You can configure the desired type by simply using evaluation-type attribute of the xpath-transformer element.
<xpath-transformer input-channel="numberInput" xpath-expression="/person/@age" evaluation-type="NUMBER_RESULT" output-channel="output"/> <xpath-transformer input-channel="booleanInput" xpath-expression="/person/@married = 'true'" evaluation-type="BOOLEAN_RESULT" output-channel="output"/>
Node Mappers
If you need to provide custom mapping for the node extracted by the XPath expression simply provide a reference to the
implementation of the org.springframework.xml.xpath.NodeMapper
- an interface used by
XPathOperations
implementations for mapping Node objects on a per-node basis. To provide a
reference to a NodeMapper
simply use node-mapper attribute:
<xpath-transformer input-channel="nodeMapperInput" xpath-expression="/person/@age" node-mapper="testNodeMapper" output-channel="output"/>
. . . and Sample NodeMapper implementation:
class TestNodeMapper implements NodeMapper { public Object mapNode(Node node, int nodeNum) throws DOMException { return node.getTextContent() + "-mapped"; } }
XML Payload Converter
You can also use implementation of the org.springframework.integration.xml.XmlPayloadConverter
to
provide more granular transformation:
<xpath-transformer input-channel="customConverterInput" xpath-expression="/test/@type" converter="testXmlPayloadConverter" output-channel="output"/>
. . . and Sample XmlPayloadConverter implementation:
class TestXmlPayloadConverter implements XmlPayloadConverter { public Source convertToSource(Object object) { throw new UnsupportedOperationException(); } // public Node convertToNode(Object object) { try { return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( new InputSource(new StringReader("<test type='custom'/>"))); } catch (Exception e) { throw new IllegalStateException(e); } } // public Document convertToDocument(Object object) { throw new UnsupportedOperationException(); } }
Combination of SpEL and XPath expressions
You can also combine Spring Expression Language (SpEL) expressions with XPath expression and configure them using expression attribute:
xpath-expression id="testExpression" expression="/person/@age * 2"/>
In the above case the overall result of the expression will be the result of the XPathe expression multiplied by 2.
All XPath based components have namespace support allowing them to be configured as
Message Endpoints with the exception of the XPath selectors which are not designed to act as
endpoints. Each component allows the XPath to either be referenced at the top level or configured via a nested
xpath-expression element. So the following configurations of an xpath-selector are all valid and represent the general
form of XPath namespace support. All forms of XPath expression result in the creation of an
XPathExpression
using the Spring XPathExpressionFactory
<si-xml:xpath-selector id="xpathRefSelector" xpath-expression="refToXpathExpression" evaluation-result-type="boolean" /> <si-xml:xpath-selector id="selectorWithNoNS" evaluation-result-type="boolean" > <si-xml:xpath-expression expression="/name"/> </si-xml:xpath-selector> <si-xml:xpath-selector id="selectorWithOneNS" evaluation-result-type="boolean" > <si-xml:xpath-expression expression="/ns1:name" ns-prefix="ns1" ns-uri="www.example.org" /> </si-xml:xpath-selector> <si-xml:xpath-selector id="selectorWithTwoNS" evaluation-result-type="boolean" > <si-xml:xpath-expression expression="/ns1:name/ns2:type"> <map> <entry key="ns1" value="www.example.org/one" /> <entry key="ns2" value="www.example.org/two" /> </map> </si-xml:xpath-expression> </si-xml:xpath-selector> <si-xml:xpath-selector id="selectorWithNamespaceMapRef" evaluation-result-type="boolean" > <si-xml:xpath-expression expression="/ns1:name/ns2:type" namespace-map="defaultNamespaces"/> </si-xml:xpath-selector> <util:map id="defaultNamespaces"> <util:entry key="ns1" value="www.example.org/one" /> <util:entry key="ns2" value="www.example.org/two" /> </util:map>
XPath splitter namespace support allows the creation of a Message Endpoint with an input channel and output channel.
<!-- Split the order into items creating a new message for each item node --> <si-xml:xpath-splitter id="orderItemSplitter" input-channel="orderChannel" output-channel="orderItemsChannel"> <si-xml:xpath-expression expression="/order/items"/> </si-xml:xpath-splitter> <!-- Split the order into items creating a new document for each item--> <si-xml:xpath-splitter id="orderItemDocumentSplitter" input-channel="orderChannel" output-channel="orderItemsChannel" create-documents="true"> <si-xml:xpath-expression expression="/order/items"/> <si:poller fixed-rate="2000"/> </si-xml:xpath-splitter>
XPath router namespace support allows for the creation of a Message Endpoint with an input channel but no output channel since the output channel is determined dynamically. The multi-channel attribute causes the creation of a multi channel router capable of routing a single message to many channels when true and a single channel router when false.
<!-- route the message according to exactly one order type channel --> <si-xml:xpath-router id="orderTypeRouter" input-channel="orderChannel" multi-channel="false"> <si-xml:xpath-expression expression="/order/type"/> </si-xml:xpath-router> <!-- route the order to all responders--> <si-xml:xpath-router id="responderRouter" input-channel="orderChannel" multi-channel="true"> <si-xml:xpath-expression expression="/request/responders"/> <si:poller fixed-rate="2000"/> </si-xml:xpath-router>