For reference see: https://jira.springsource.org/browse/INT-2882 * Verify spacing * Ensure all source code samples are typed: e.g. <programlisting language="xml"> * Ensure source code fits space in PDF format
176 lines
9.7 KiB
XML
176 lines
9.7 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="chain"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>Message Handler Chain</title>
|
||
|
||
<section id="chain-introduction">
|
||
<title>Introduction</title>
|
||
<para>
|
||
The <classname>MessageHandlerChain</classname> is an implementation of
|
||
<interfacename>MessageHandler</interfacename> that can be configured as a single Message Endpoint while
|
||
actually delegating to a chain of other handlers, such as Filters, Transformers, Splitters, and so on.
|
||
This can lead to a much simpler configuration when several handlers need to be connected in a fixed, linear
|
||
progression. For example, it is fairly common to provide a Transformer before other components. Similarly, when
|
||
providing a <emphasis>Filter</emphasis> before some other component in a chain, you are essentially creating a
|
||
<ulink url="http://www.eaipatterns.com/MessageSelector.html">Selective Consumer</ulink>. In either case, the
|
||
chain only requires a single <code>input-channel</code> and a single <code>output-channel</code> eliminating
|
||
the need to define channels for each individual component.
|
||
<tip>
|
||
Spring Integration's <interfacename>Filter</interfacename> provides a boolean property <methodname>throwExceptionOnRejection</methodname>.
|
||
When providing multiple Selective Consumers on the same point-to-point channel with different acceptance criteria,
|
||
this value should be set to 'true' (the default is false) so that the dispatcher will know that the Message was
|
||
rejected and as a result will attempt to pass the Message on to other subscribers. If the Exception were not
|
||
thrown, then it would appear to the dispatcher as if the Message had been passed on successfully even though
|
||
the Filter had <emphasis>dropped</emphasis> the Message to prevent further processing. If you do indeed want
|
||
to "drop" the Messages, then the Filter's 'discard-channel' might be useful since it does give you a chance
|
||
to perform some operation with the dropped message (e.g. send to a JMS queue or simply write to a log).
|
||
</tip>
|
||
</para>
|
||
<para>
|
||
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between
|
||
components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
|
||
</para>
|
||
<para>
|
||
Internally, the chain will be expanded into a linear setup of the listed endpoints, separated by anonymous channels.
|
||
The reply channel header will not be taken into account within the chain: only after the last handler is invoked
|
||
will the resulting message be forwarded on to the reply channel or the chain's output channel. Because of this
|
||
setup all handlers except the last required to implement the MessageProducer interface (which provides a
|
||
'setOutputChannel()' method). The last handler only needs an output channel if the outputChannel on the
|
||
MessageHandlerChain is set.
|
||
<note>
|
||
<para>
|
||
As with other endpoints, the <code>output-channel</code> is optional. If there is a reply Message at the end of the
|
||
chain, the output-channel takes precedence, but if not available, the chain handler will check for a
|
||
reply channel header on the inbound Message as a fallback.
|
||
</para>
|
||
</note>
|
||
</para>
|
||
<para>
|
||
In most cases there is no need to implement MessageHandlers yourself. The next section will focus on namespace
|
||
support for the chain element. Most Spring Integration endpoints, like Service Activators and Transformers, are
|
||
suitable for use within a <classname>MessageHandlerChain</classname>.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="chain-namespace">
|
||
<title>Configuring Chain</title>
|
||
<para>
|
||
The <chain> element provides an <code>input-channel</code> attribute, and if the last element in the chain is capable
|
||
of producing reply messages (optional), it also supports an <code>output-channel</code> attribute. The sub-elements are then
|
||
filters, transformers, splitters, and service-activators. The last element may also be a router or an outbound-channel-adapter.
|
||
<programlisting language="xml"><![CDATA[<int:chain input-channel="input" output-channel="output">
|
||
<int:filter ref="someSelector" throw-exception-on-rejection="true"/>
|
||
<int:header-enricher>
|
||
<int:header name="foo" value="bar"/>
|
||
</int:header-enricher>
|
||
<int:service-activator ref="someService" method="someMethod"/>
|
||
</int:chain>]]></programlisting>
|
||
</para>
|
||
<para>
|
||
The <header-enricher> element used in the above example will set a message header named "foo" with a value
|
||
of "bar" on the message. A header enricher is a specialization of <interfacename>Transformer</interfacename>
|
||
that touches only header values. You could obtain the same result by implementing a MessageHandler that did the
|
||
header modifications and wiring that as a bean, but the header-enricher is obviously a simpler option.
|
||
</para>
|
||
<para>
|
||
The <chain> can be configured as the last 'black-box' consumer of the message flow. For this solution it is
|
||
enough to put at the end of the <chain> some <outbound-channel-adapter>:
|
||
<programlisting language="xml"><![CDATA[<int:chain input-channel="input">
|
||
<si-xml:marshalling-transformer marshaller="marshaller" result-type="StringResult" />
|
||
<int:service-activator ref="someService" method="someMethod"/>
|
||
<int:header-enricher>
|
||
<int:header name="foo" value="bar"/>
|
||
</int:header-enricher>
|
||
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
|
||
</int:chain>]]></programlisting>
|
||
</para>
|
||
<para><emphasis>Disallowed Attributes and Elements</emphasis></para>
|
||
<para>
|
||
It is important to note that certain attributes, such as
|
||
<emphasis role="bold">order</emphasis> and <emphasis role="bold">input-channel</emphasis>
|
||
are not allowed to be specified on components used within a
|
||
<emphasis>chain</emphasis>. The same is true for the <emphasis role="bold">poller</emphasis>
|
||
sub-element.
|
||
</para>
|
||
<important>
|
||
<para>
|
||
For the <emphasis>Spring Integration</emphasis> core components, the
|
||
XML Schema itself will enforce some of these constraints. However, for non-core
|
||
components or your own custom components, these constraints are enforced
|
||
by the XML namespace parser, not by the XML Schema.
|
||
</para>
|
||
<para>
|
||
These XML namespace parser constraints were added with
|
||
<emphasis>Spring Integration 2.2</emphasis>. The XML namespace parser
|
||
will throw an <classname>BeanDefinitionParsingException</classname> if you try to use disallowed
|
||
attributes and elements.
|
||
</para>
|
||
</important>
|
||
<para>
|
||
The <emphasis>id</emphasis> attribute, however, is allowed to be specified.
|
||
In fact, the <link linkend='delayer'><emphasis>Delayer</emphasis></link>
|
||
component actually requires the <emphasis>id</emphasis> attribute to be present.
|
||
</para>
|
||
<para>
|
||
In most other cases, the <emphasis>id</emphasis> will generally be
|
||
ignored but may still add value for documentation purposes, and may also
|
||
be used for providing more meaningful log messages.
|
||
</para>
|
||
<note>
|
||
Currently, the XML Schema of the <emphasis>Spring Integration</emphasis> Core module
|
||
prevents you from setting the <emphasis>id</emphasis> attribute for
|
||
Core components within a Message Handler Chain. This may be relaxed in future,
|
||
to provide the benefits described above.
|
||
</note>
|
||
<para><emphasis>Calling a Chain from within a Chain</emphasis></para>
|
||
|
||
<para>
|
||
Sometimes you need to make a nested call to another chain from within a chain and then come
|
||
back and continue execution within the original chain.
|
||
To accomplish this you can utilize a Messaging Gateway by including a <gateway> element.
|
||
For example:
|
||
</para>
|
||
<programlisting language="xml"><![CDATA[<int:chain id="main-chain" input-channel="in" output-channel="out">
|
||
<int:header-enricher>
|
||
<int:header name="name" value="Many" />
|
||
</int:header-enricher>
|
||
<int:service-activator>
|
||
<bean class="org.foo.SampleService" />
|
||
</int:service-activator>
|
||
<int:gateway request-channel="inputA"/>
|
||
</int:chain>
|
||
|
||
<int:chain id="nested-chain-a" input-channel="inputA">
|
||
<int:header-enricher>
|
||
<int:header name="name" value="Moe" />
|
||
</int:header-enricher>
|
||
<int:gateway request-channel="inputB"/>
|
||
<int:service-activator>
|
||
<bean class="org.foo.SampleService" />
|
||
</int:service-activator>
|
||
</int:chain>
|
||
|
||
<int:chain id="nested-chain-b" input-channel="inputB">
|
||
<int:header-enricher>
|
||
<int:header name="name" value="Jack" />
|
||
</int:header-enricher>
|
||
<int:service-activator>
|
||
<bean class="org.foo.SampleService" />
|
||
</int:service-activator>
|
||
</int:chain>]]></programlisting>
|
||
<para>
|
||
In the above example the <emphasis>nested-chain-a</emphasis> will be called at the end of
|
||
<emphasis>main-chain</emphasis> processing by the 'gateway' element configured there. While in
|
||
<emphasis>nested-chain-a</emphasis> a call to a <emphasis>nested-chain-b</emphasis> will be made
|
||
after header enrichment and then it will come back to finish execution in <emphasis>nested-chain-b</emphasis>.
|
||
Finally the flow returns to the <emphasis>main-chain</emphasis>. When the nested version of a <gateway>
|
||
element is defined in the chain, it does not require the <code>service-interface</code> attribute.
|
||
Instead, it simple takes the message in its current state and places it on the channel defined via
|
||
the <code>request-channel</code> attribute. When the downstream flow initiated by that gateway completes,
|
||
a <interfacename>Message</interfacename> will be returned to the gateway and continue its journey within
|
||
the current chain.
|
||
</para>
|
||
</section>
|
||
|
||
</section>
|