* Introduce XSD 'id' attribute for all `<chain>` sub-components * Register `<chain>` sub-components as Beans with names based on `<chain>` 'id' * Generate aliases for sub-components based on element 'id' * Add `<chain>` parser test to check sub-components as Beans within `BeanFactory` * Ignore `NestedChainParserTests` as abnormal and confused for SI-configs * Now `<chain>` sub-components are trackable by `MessageHistory` * Now `<chain>` sub-components are eligible for JMX export * Polishing some failed tests https://jira.springsource.org/browse/INT-2755 https://jira.springsource.org/browse/INT-2321 INT-2755: Register as Beans only if 'id' provided INT-2755: Set 'componentName' for handlers with 'id' When components within `<chain>` are configured with an 'id' attribute the 'componentName' of the handler is constructed as: chainHandler.componentName + '.' + handler.componentName. Otherwise it is as was before. INT-2755: Polishing INT-2755: changes according PR's comments * Add INFO about preference of `id` attribute for `<chain>` in the `ChainParser` * Change `<chain>`'s sub-components `id` generation algorithm * Configure `<chain>`'s sub-component `componentName` in the `ChainParser` instead of in the `MessageHandlerChain` * Add `componentName` property to the `JpaOutboundGatewayFactoryBean` * Add a note to the Reference Manual INT-2755: Localize duplicate 'id' within chain INT-2755: remove child beans aliasing * Polishing tests * Polishing docs INT-2755 Polishing: Handle Nested Chain Bean Names Previously named beans within a nested chain did not get unique bean names. INT-2755: Polishing nested chains Doc Polishing
216 lines
12 KiB
XML
216 lines
12 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 a 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><emphasis>'id' Attribute</emphasis></para>
|
||
<para>
|
||
Beginning with Spring Integration 3.0, if a chain element is given an <emphasis>id</emphasis>, the
|
||
bean name for the element is a combination of the chain's <emphasis>id</emphasis> and the <emphasis>id</emphasis>
|
||
of the element itself. Elements without an <emphasis>id</emphasis> are not registered
|
||
as beans, but they are given <code>componentName</code>s that include the chain id. For example:
|
||
<programlisting language="xml"><![CDATA[<int:chain id="fooChain" input-channel="input">
|
||
<int:service-activator id="fooService" ref="someService" method="someMethod"/>
|
||
<int:object-to-json-transformer/>
|
||
</int:chain>]]></programlisting>
|
||
<itemizedlist>
|
||
<listitem>
|
||
The <code><chain></code> root element has an <emphasis>id</emphasis> 'fooChain'. So, the
|
||
<classname>AbstractEndpoint</classname> implementation (<classname>PollingConsumer</classname> or
|
||
<classname>EventDrivenConsumer</classname>, depending on the <emphasis>input-channel</emphasis> type)
|
||
bean takes this value as it's bean name.
|
||
</listitem>
|
||
<listitem>
|
||
The <classname>MessageHandlerChain</classname> bean acquires a bean alias 'fooChain.handler', which allows
|
||
direct access to this bean from the <interfacename>BeanFactory</interfacename>.
|
||
</listitem>
|
||
<listitem>
|
||
The <code><service-activator></code> is not a fully-fledged Messaging Endpoint (<classname>PollingConsumer</classname>
|
||
or <classname>EventDrivenConsumer</classname>) - it is simply a
|
||
<interfacename>MessageHandler</interfacename> within the <code><chain></code>. In this case,
|
||
the bean name registered with the <interfacename>BeanFactory</interfacename>
|
||
is 'fooChain$child.fooService.handler'.
|
||
</listitem>
|
||
<listitem>
|
||
The <emphasis>componentName</emphasis> of this <classname>ServiceActivatingHandler</classname> takes the
|
||
same value, but without the '.handler' suffix - 'fooChain$child.fooService'.
|
||
</listitem>
|
||
<listitem>
|
||
The last <code><chain></code> sub-component, <code><object-to-json-transformer></code>, doesn't have
|
||
an <emphasis>id</emphasis> attribute. Its <emphasis>componentName</emphasis> is based on its
|
||
position in the <code><chain></code>. In this case, it is 'fooChain$child#1'.
|
||
(The final element of the name is the order within the chain, beginning with '#0').
|
||
Note, this transformer isn't registered as a bean within the application context,
|
||
so, it doesn't get a <emphasis>beanName</emphasis>, however its <emphasis>componentName</emphasis> has
|
||
a value which is useful for logging etc.
|
||
</listitem>
|
||
</itemizedlist>
|
||
</para>
|
||
<para>
|
||
The <emphasis>id</emphasis> attribute for <code><chain></code> elements allows them to be eligible for
|
||
<link linkend='jmx-mbean-exporter'>JMX export</link> and they are trackable via <link linkend='message-history'>Message History</link>.
|
||
They can also be accessed from the <interfacename>BeanFactory</interfacename> using the appropriate bean name
|
||
as discussed above.
|
||
</para>
|
||
<tip>
|
||
<para>
|
||
It is useful to provide an explicit <emphasis>id</emphasis> attribute on <code><chain></code>s
|
||
to simplify the identification of sub-components in logs, and to provide
|
||
access to them from the <interfacename>BeanFactory</interfacename> etc.
|
||
</para>
|
||
</tip>
|
||
<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>
|