JIRA: https://jira.spring.io/browse/INT-651 INT-651: Polishing according PR comments INT-651: Polishing #2 Doc Polishing
193 lines
9.3 KiB
XML
193 lines
9.3 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="splitter"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title>Splitter</title>
|
|
|
|
<section id="splitter-annotation">
|
|
<title>Introduction</title>
|
|
|
|
<para>The Splitter is a component whose role is to partition a message in
|
|
several parts, and send the resulting messages to be processed
|
|
independently. Very often, they are upstream producers in a pipeline that
|
|
includes an Aggregator.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Programming model</title>
|
|
|
|
<para>The API for performing splitting consists of one base class,
|
|
<classname>AbstractMessageSplitter</classname>, which is a
|
|
<interfacename>MessageHandler</interfacename> implementation,
|
|
encapsulating features which are common to splitters, such as filling in
|
|
the appropriate message headers CORRELATION_ID, SEQUENCE_SIZE, and
|
|
SEQUENCE_NUMBER on the messages that are produced. This enables tracking
|
|
down the messages and the results of their processing (in a typical
|
|
scenario, these headers would be copied over to the messages that are
|
|
produced by the various transforming endpoints), and use them, for
|
|
example, in a
|
|
<ulink url="http://www.eaipatterns.com/DistributionAggregate.html">
|
|
Composed Message Processor</ulink> scenario.</para>
|
|
|
|
<para>An excerpt from <classname>AbstractMessageSplitter</classname> can be seen below:</para>
|
|
|
|
<programlisting language="java"><![CDATA[public abstract class AbstractMessageSplitter
|
|
extends AbstractReplyProducingMessageConsumer {
|
|
...
|
|
protected abstract Object splitMessage(Message<?> message);
|
|
|
|
}]]></programlisting>
|
|
|
|
<para>To implement a specific Splitter in an application,
|
|
extend <classname>AbstractMessageSplitter</classname> and implement the <code>splitMessage</code> method,
|
|
which contains logic for splitting the messages. The return
|
|
value may be one of the following:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>A <interfacename>Collection</interfacename> or an array of Messages,
|
|
or an <interfacename>Iterable</interfacename> (or <interfacename>Iterator</interfacename>)
|
|
that iterates over Messages -
|
|
in this case the messages will be sent as such (after the
|
|
CORRELATION_ID, SEQUENCE_SIZE and SEQUENCE_NUMBER are populated).
|
|
Using this approach gives more control to the developer, for example
|
|
for populating custom message headers as part of the splitting
|
|
process.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>A <interfacename>Collection</interfacename> or an array of non-Message objects,
|
|
or an <interfacename>Iterable</interfacename> (or <interfacename>Iterator</interfacename>)
|
|
that iterates over non-Message objects - works like the prior case, except that each collection
|
|
element will be used as a Message payload. Using this approach allows
|
|
developers to focus on the domain objects without having to consider
|
|
the Messaging system and produces code that is easier to test.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>a <interfacename>Message</interfacename> or non-Message object
|
|
(but not a Collection or an Array) - it works like the previous cases,
|
|
except a single message will be sent out.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>In Spring Integration, any POJO can implement the splitting
|
|
algorithm, provided that it defines a method that accepts a single
|
|
argument and has a return value. In this case, the return value of the
|
|
method will be interpreted as described above. The input argument might
|
|
either be a <interfacename>Message</interfacename> or a simple POJO.
|
|
In the latter case, the splitter will receive the payload of the incoming message.
|
|
Since this decouples the code from the Spring Integration API and will typically be easier
|
|
to test, it is the recommended approach.</para>
|
|
<para>
|
|
<emphasis role="bold">Splitter and Iterators</emphasis>
|
|
</para>
|
|
<para>
|
|
Starting with <emphasis>version 4.1</emphasis>, the <classname>AbstractMessageSplitter</classname>
|
|
supports the <interfacename>Iterator</interfacename> type for the <code>value</code> to split.
|
|
Note, in the case of an <interfacename>Iterator</interfacename>
|
|
(or <interfacename>Iterable</interfacename>), we don't have access to the number of underlying items and the
|
|
<code>SEQUENCE_SIZE</code> header is set to <code>0</code>. This means that the default
|
|
<classname>SequenceSizeReleaseStrategy</classname> of an <code><aggregator></code> won't work and the
|
|
group for the <code>CORRELATION_ID</code> from the <code>splitter</code> won't be released; it will remain
|
|
as <code>incomplete</code>. In this case you should use an appropriate custom
|
|
<interfacename>ReleaseStrategy</interfacename> or rely on <code>send-partial-result-on-expiry</code>
|
|
together with <code>group-timeout</code> or a <classname>MessageGroupStoreReaper</classname>.
|
|
</para>
|
|
<para>
|
|
An <interfacename>Iterator</interfacename> object is useful to avoid the need for building an entire
|
|
collection in the memory before splitting. For example, when underlying items are populated from some external system
|
|
(e.g. DataBase or FTP <code>MGET</code>) using iterations or streams.
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
<section id="splitter-config">
|
|
<title>Configuring Splitter</title>
|
|
<section>
|
|
<title>Configuring a Splitter using XML</title>
|
|
|
|
<para>A splitter can be configured through XML as follows:<programlisting language="xml"><int:channel id="inputChannel"/>
|
|
|
|
<int:splitter id="splitter" <co id="split1" />
|
|
ref="splitterBean" <co id="split2" />
|
|
method="split" <co id="split3" />
|
|
input-channel="inputChannel" <co id="split4" />
|
|
output-channel="outputChannel" <co id="split5" />/>
|
|
|
|
<int:channel id="outputChannel"/>
|
|
|
|
<beans:bean id="splitterBean" class="sample.PojoSplitter"/></programlisting><calloutlist>
|
|
<callout arearefs="split1">
|
|
<para>The id of the splitter is
|
|
<emphasis>optional</emphasis>.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="split2">
|
|
<para>A reference to a bean defined in the application context. The
|
|
bean must implement the splitting logic as described in the section
|
|
above .<emphasis>Optional</emphasis>.
|
|
If reference to a bean is not provided, then it is assumed that the <emphasis>payload</emphasis>
|
|
of the Message that arrived on the <code>input-channel</code> is
|
|
an implementation of <interfacename>java.util.Collection</interfacename>
|
|
and the default splitting logic will be applied to the Collection,
|
|
incorporating each individual element into a Message and sending it to the <code>output-channel</code>.
|
|
</para>
|
|
</callout>
|
|
|
|
<callout arearefs="split3">
|
|
<para>The method (defined on the bean specified above) that
|
|
implements the splitting logic.
|
|
<emphasis>Optional</emphasis>.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="split4">
|
|
<para>The input channel of the splitter.
|
|
<emphasis>Required</emphasis>.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="split5">
|
|
<para>The channel to which the splitter will send the results of
|
|
splitting the incoming message. <emphasis>Optional (because incoming
|
|
messages can specify a reply channel themselves)</emphasis>.</para>
|
|
</callout>
|
|
</calloutlist></para>
|
|
<para>
|
|
Using a <code>ref</code> attribute is generally recommended if the custom splitter implementation may be referenced in other
|
|
<code><splitter></code> definitions. However if the custom splitter handler implementation should be scoped to a
|
|
single definition of the <code><splitter></code>, configure an inner bean definition:
|
|
<programlisting language="xml"><![CDATA[<int:splitter id="testSplitter" input-channel="inChannel" method="split"
|
|
output-channel="outChannel">
|
|
<beans:bean class="org.foo.TestSplitter"/>
|
|
</int:spliter>]]></programlisting>
|
|
</para>
|
|
<note>
|
|
<para>
|
|
Using both a <code>ref</code> attribute and an inner handler definition in the same <code><int:splitter></code>
|
|
configuration is not allowed, as it creates an ambiguous condition and will result in an Exception being thrown.
|
|
</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Configuring a Splitter with Annotations</title>
|
|
|
|
<para>The <interfacename>@Splitter</interfacename> annotation is
|
|
applicable to methods that expect either the
|
|
<interfacename>Message</interfacename> type or the message payload type,
|
|
and the return values of the method should be a <interfacename>Collection</interfacename> of any type.
|
|
If the returned values are not actual <interfacename>Message</interfacename>
|
|
objects, then each item will be wrapped in a Message as its payload. Each
|
|
message will be sent to the designated output channel for the endpoint
|
|
on which the <interfacename>@Splitter</interfacename> is defined.
|
|
<programlisting language="java">@Splitter
|
|
List<LineItem> extractItems(Order order) {
|
|
return order.getItems()
|
|
}</programlisting></para>
|
|
<para>
|
|
Also see <xref linkend="advising-with-annotations"/>.
|
|
</para>
|
|
</section>
|
|
</section>
|
|
</section>
|