Files
spring-integration/src/reference/docbook/content-enrichment.xml
Gary Russell 836c8e2556 INT-2269 Add ReplyChannelRegistry
Allows reply channel resolution after a message has been serialized
somewhere in a flow. Previously, the reply channel was lost.

With this change, the reply channel can be registered and the header
becomes a string (channel name) that can be serialized.

JIRA: https://jira.springsource.org/browse/INT-2269

INT-2269 Rename Registry to HeaderChannelRegistry

- Extract interface
- DefaultHeaderChannelRgistry

INT-2269 Polishing; PR Comments

- Add errorChannel registration as well.

INT-2269 HeaderChannelRegistry - Fix

The internal BridgeHandler in MessagingGatewaySupport did not have
a channel resolver. When a reply was explicitly routed to the gateway's
reply channel, the String representation of the reply channel could
not be resolved to a channel.

Set the BeanFactory on the bridge handler.

Add tests.

INT-2269 HeaderChannelRegistry - Fix JMS/Enricher

The JMS inbound gateway and ContentEnricher instantiate a
MessagingGatewaySupport internally it does not get a
reference to the BeanFactory. This means that the internal
BridgeHandler cannot resolve the String representation of
the reply channel to a channel.

Make ChannelPublishingJmsMessageListener BeanFactory aware, and
propagate the bean factory to the MGS.

Set the MGS bean factory in the ContentEnricher (which is already
BFA).

INT-2269: Polishing
2013-11-15 15:23:05 +02:00

534 lines
28 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="content-enricher"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Content Enricher</title>
<section id="content-enricher-introduction">
<title>Introduction</title>
<para>
At times you may have a requirement to enhance a request with more
information than was provided by the target system. The
<ulink url="http://www.eaipatterns.com/DataEnricher.html">Content Enricher</ulink>
pattern describes various scenarios as well as the component
(Enricher), which allows you to address such requirements.
</para>
<para>
The Spring Integration <code>Core</code> module includes 2 enrichers:
</para>
<itemizedlist>
<listitem><link linkend='header-enricher'>Header Enricher</link></listitem>
<listitem><link linkend='payload-enricher'>Payload Enricher</link></listitem>
</itemizedlist>
<para>
Furthermore, several <emphasis>Adapter specific Header Enrichers</emphasis>
are included as well:
</para>
<itemizedlist>
<listitem><link linkend='xml-xpath-header-enricher'>XPath Header Enricher (XML Module)</link></listitem>
<listitem><link linkend='mail-namespace'>Mail Header Enricher (Mail Module)</link></listitem>
<listitem><link linkend='xmpp-message-outbound-channel-adapter'>XMPP Header Enricher (XMPP Module)</link></listitem>
</itemizedlist>
<para>
Please go to the adapter specific sections of this reference manual
to learn more about those adapters.
</para>
<para>
For more information regarding expressions support, please see <xref linkend="spel" />.
</para>
</section>
<section id="header-enricher">
<title>Header Enricher</title>
<para>
If you only need to add headers to a Message, and they are not
dynamically determined by the Message content, then referencing a
custom implementation of a Transformer may be overkill. For that reason,
Spring Integration provides support for the <emphasis>Header Enricher</emphasis>
pattern. It is exposed via the <code>&lt;header-enricher&gt;</code> element.
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" value="123"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>]]></programlisting>
<para>
The <emphasis>Header Enricher</emphasis> also provides helpful sub-elements
to set well-known header names.
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:error-channel ref="applicationErrorChannel"/>
<int:reply-channel ref="quoteReplyChannel"/>
<int:correlation-id value="123"/>
<int:priority value="HIGHEST"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>]]></programlisting>
<para>
In the above configuration you can clearly see that for well-known
headers such as <code>errorChannel</code>, <code>correlationId</code>,
<code>priority</code>, <code>replyChannel</code>etc., instead of
using generic <emphasis>&lt;header&gt;</emphasis> sub-elements where
you would have to provide both header 'name' and 'value', you can use
convenient sub-elements to set those values directly.
</para>
<para>
<emphasis role="bold">POJO Support</emphasis>
</para>
<para>
Often a header value cannot be defined statically and has to be
determined dynamically based on some content in the Message. That is why
<emphasis>Header Enricher</emphasis> allows you to also specify a bean
reference using the <code>ref</code> and <code>method</code> attribute.
The specified method will calculate the header value. Let's look at
the following configuration:
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" method="computeValue" ref="myBean"/>
</int:header-enricher>
<bean id="myBean" class="foo.bar.MyBean"/>]]></programlisting>
<programlisting language="java"><![CDATA[public class MyBean {
public String computeValue(String payload){
return payload.toUpperCase() + "_US";
}
}]]></programlisting>
<para>
You can also configure your POJO as inner bean:
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<bean class="org.MyEnricher"/>
</int:header>
</int:header-enricher>]]></programlisting>
<para>
as well as point to a Groovy script:
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<int-groovy:script location="org/SampleGroovyHeaderEnricher.groovy"/>
</int:header>
</int:header-enricher>]]></programlisting>
<para>
<emphasis role="bold">SpEL Support</emphasis>
</para>
<para>
In Spring Integration 2.0 we have introduced the convenience of the
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language (SpEL)</ulink>
to help configure many different components. The <emphasis>Header
Enricher</emphasis> is one of them.
Looking again at the POJO example above, you can see that the computation
logic to determine the header value is actually pretty simple. A natural
question would be: "is there a simpler way to accomplish this?". That
is where SpEL shows its true power.
</para>
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
</int:header-enricher>]]></programlisting>
<para>
As you can see, by using SpEL for such simple cases, we no longer have
to provide a separate class and configure it in the application context.
All we need is the <emphasis>expression</emphasis> attribute configured
with a valid SpEL expression. The 'payload' and 'headers' variables
are bound to the SpEL Evaluation Context, giving you full access to
the incoming Message.
</para>
<para><emphasis role="bold">Header Channel Registry</emphasis></para>
<para>
Starting with <emphasis>Spring Integration 3.0</emphasis>, a new sub-element
<code>&lt;int:header-channels-to-string/&gt;</code> is available; it has no attributes.
This converts existing <code>replyChannel</code> and <code>errorChannel</code>
headers (when they are a
<classname>MessageChannel</classname>) to a String and stores the channel(s) in
a registry for later resolution when it is time to send a reply, or handle an error.
This is useful
for cases where the headers might be lost; for example when
serializing a message into a message store or when transporting the message
over JMS. If the header does not already exist, or it
is not a <classname>MessageChannel</classname>, no changes are made.
</para>
<para>
Use of this functionality requires the presence of a <classname>HeaderChannelRegistry</classname>
bean. By default, the framework creates a <classname>DefaultHeaderChannelRegistry</classname>
with the default expiry (60 seconds). Channels
are removed from the registry after this time. To change this, simply define a bean
with id <code>integrationHeaderChannelRegistry</code> and configure the required delay using
a constructor argument (milliseconds).
</para>
<para>
The <classname>HeaderChannelRegistry</classname> has a <code>size()</code> method to
determine the current size of the registry. The <code>runReaper()</code> method
cancels the current scheduled task and runs the reaper immediately; the task is
then scheduled to run again based on the current delay. These methods can be invoked
directly by getting a reference to the registry, or you can send a message with, for example,
the following content to a control bus:
</para>
<programlisting><![CDATA["@integrationHeaderChannelRegistry.runReaper()"]]></programlisting>
<para>
This sub-element is a convenience only, and is the equivalent of specifying:
</para>
<programlisting language="xml"><![CDATA[<int:reply-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.replyChannel)"/>
<int:error-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"/>]]></programlisting>
<tip>
For more examples for configuring header enrichers, see
<ulink url="https://github.com/SpringSource/spring-integration/wiki/Header-Enricher-Advanced-Configuration">
Header Enricher Advanced Configuration</ulink>.
</tip>
</section>
<section id="payload-enricher">
<title>Payload Enricher</title>
<para>
In certain situations the Header Enricher, as discussed above, may
not be sufficient and payloads themselves may have to be enriched
with additional information. For example, order messages that enter
the Spring Integration messaging system have to look up the order's
customer based on the provided customer number and then enrich the original
payload with that information.
</para>
<para>
Since Spring Integration 2.1, the Payload Enricher is provided. A
Payload Enricher defines an endpoint that passes a <interfacename>
Message</interfacename> to the exposed request channel and then
expects a reply message. The reply message then becomes the root object
for evaluation of expressions to enrich the target payload.
</para>
<para>
The Payload Enricher provides full XML namespace support via the <code>enricher</code>
element. In order to send request messages, the payload enricher has a
<code>request-channel</code> attribute that allows you to dispatch
messages to a request channel.
</para>
<para>
Basically by defining the request channel, the Payload Enricher acts
as a Gateway, waiting for the message that were sent to the request
channel to return, and the Enricher then augments the message's payload with
the data provided by the reply message.
</para>
<para>
When sending messages to the request channel you also have the option
to only send a subset of the original payload using the
<code>request-payload-expression</code> attribute.
</para>
<para>
The enriching of payloads is configured through SpEL expressions,
providing users with a maximum degree of flexibility. Therefore, users
are not only able to enrich payloads with direct values from the reply channel's
<interfacename>Message</interfacename>, but they can use SpEL
expressions to extract a subset from that Message, only, or to apply
addtional inline transformations, allowing them to further manipulate
the data.
</para>
<para>
If you only need to enrich payloads with static values, you don't have
to provide the <code>request-channel</code> attribute.
</para>
<note>
Enrichers are a variant of Transformers and in many cases you could
use a Payload Enricher or a generic Transformer implementation to add
additional data to your messages payloads. Thus, familiarize yourself
with all transformation-capable components that are provided by Spring
Integration and carefully select the implementation that semantically
fits your business case best.
</note>
<section id="payload-enricher-configuration">
<title>Configuration</title>
<para>
Below, please find an overview of all available configuration options that
are available for the payload enricher:
</para>
<programlisting language="xml"><![CDATA[<int:enricher request-channel="" ]]><co id="payload-enricher01-co" linkends="payload-enricher01" /><![CDATA[
auto-startup="true" ]]><co id="payload-enricher02-co" linkends="payload-enricher02" /><![CDATA[
id="" ]]><co id="payload-enricher03-co" linkends="payload-enricher03" /><![CDATA[
order="" ]]><co id="payload-enricher04-co" linkends="payload-enricher04" /><![CDATA[
output-channel="" ]]><co id="payload-enricher05-co" linkends="payload-enricher05" /><![CDATA[
request-payload-expression="" ]]><co id="payload-enricher06-co" linkends="payload-enricher06" /><![CDATA[
reply-channel="" ]]><co id="payload-enricher07-co" linkends="payload-enricher07" /><![CDATA[
send-timeout="" ]]><co id="payload-enricher08-co" linkends="payload-enricher08" /><![CDATA[
should-clone-payload="false"> ]]><co id="payload-enricher09-co" linkends="payload-enricher09" /><![CDATA[
<int:poller></int:poller> ]]><co id="payload-enricher10-co" linkends="payload-enricher10" /><![CDATA[
<int:property name="" expression=""/> ]]><co id="payload-enricher11-co" linkends="payload-enricher11" /><![CDATA[
<int:property name="" value=""/>
<int:header name="" expression=""/> ]]><co id="payload-enricher12-co" linkends="payload-enricher12" /><![CDATA[
<int:header name="" value="" overwrite="" type=""/>
</int:enricher>]]></programlisting>
<para>
<calloutlist>
<callout arearefs="payload-enricher01-co" id="payload-enricher01">
<para>
Channel to which a Message will be sent to get the data to use for enrichment.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher02-co" id="payload-enricher02">
<para>
Lifecycle attribute signaling if this component should be
started during Application Context startup. Defaults to true.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher03-co" id="payload-enricher03">
<para>
Id of the underlying bean definition, which is either
an <classname>EventDrivenConsumer</classname> or a
<classname>PollingConsumer</classname>.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher04-co" id="payload-enricher04">
<para>
Specifies the order for invocation when this endpoint is
connected as a subscriber to a channel. This is particularly
relevant when that channel is using a "failover" dispatching
strategy. It has no effect when this endpoint itself is a
Polling Consumer for a channel with a queue.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher05-co" id="payload-enricher05">
<para>
Identifies the Message channel where a Message will
be sent after it is being processed by this endpoint.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher06-co" id="payload-enricher06">
<para>
By default the original message's payload will be used as
payload that will be send to the <code>request-channel</code>.
By specifying a SpEL expression as value for the
<code>request-payload-expression</code> attribute, a
subset of the original payload, a header value or any other
resolvable SpEL expression can be used as the basis for the
payload, that will be sent to the request-channel.
</para>
<para>
For the Expression evaluation the full message is available
as the 'root object'.
</para>
<para>
For instance the following SpEL expressions (among others)
are possible:
</para>
<itemizedlist>
<listitem>payload.foo</listitem>
<listitem>headers.foobar</listitem>
<listitem>new java.util.Date()</listitem>
<listitem>'foo' + 'bar'</listitem>
</itemizedlist>
<para>
If more sophisticated logic is required (e.g. changing the
message headers etc.) please use additional downstream transformers.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher07-co" id="payload-enricher07">
<para>
Channel where a reply Message is expected. This is optional; typically the auto-generated
temporary reply channel is sufficient.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher08-co" id="payload-enricher08">
<para>
Maximum amount of time in milliseconds to wait when
sending a message to the channel, if such channel may block.
</para>
<para>
For example, a Queue Channel can block until space is
available, if its maximum capacity has been reached. Internally
the send timeout is set on the <classname>MessagingTemplate</classname>
and ultimately applied when invoking the send operation on the
<interfacename>MessageChannel</interfacename>.
</para>
<para>
By default the send timeout is set to '-1', which may cause
the send operation on the <interfacename>MessageChannel</interfacename>,
depending on the implementation, to block indefinitely.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher09-co" id="payload-enricher09">
<para>
Boolean value indicating whether any payload that implements
<interfacename>Cloneable</interfacename> should be cloned
prior to sending the Message to the request chanenl for
acquiring the enriching data. The cloned version would be
used as the target payload for the ultimate reply.
Default is <code>false</code>.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher10-co" id="payload-enricher10">
<para>
Allows you to configure a Message Poller if this endpoint
is a Polling Consumer.
<emphasis>Optional</emphasis>.
</para>
</callout>
<callout arearefs="payload-enricher11-co" id="payload-enricher11">
<para>
Each <code>property</code> sub-element provides the
name of a property (via the mandatory <code>name</code>
attribute). That property should be settable on the
target payload instance. Exactly one of the <code>value</code>
or <code>expression</code> attributes must be provided
as well. The former for a literal value to set, and the
latter for a SpEL expression to be evaluated. The root
object of the evaluation context is the Message that was
returned from the flow initiated by this enricher, the
input Message if there is no request channel, or the
application context (using the '@&lt;beanName&gt;.&lt;beanProperty&gt;'
SpEL syntax).
</para>
</callout>
<callout arearefs="payload-enricher12-co" id="payload-enricher12">
<para>
Each <code>header</code> sub-element provides the
name of a Message header (via the mandatory <code>name</code>
attribute). Exactly one of the <code>value</code>
or <code>expression</code> attributes must be provided
as well. The former for a literal value to set, and the
latter for a SpEL expression to be evaluated. The root
object of the evaluation context is the Message that was
returned from the flow initiated by this enricher, the
input Message if there is no request channel, or the
application context (using the '@&lt;beanName&gt;.&lt;beanProperty&gt;'
SpEL syntax).
Note, similar to the <code>&lt;header-enricher&gt;</code>, the <code>&lt;enricher&gt;</code>'s
<code>header</code> element has <code>type</code> and <code>overwrite</code> attributes.
However, a difference is that, with the <code>&lt;enricher&gt;</code>,
the <code>overwrite</code> attribute is <code>true</code> by default,
to be consistent with <code>&lt;enricher&gt;</code>'s
<code>&lt;property&gt;</code> sub-element.
</para>
</callout>
</calloutlist>
</para>
</section>
<section id="payload-enricher-examples">
<title>Examples</title>
<para>
Below, please find several examples of using a Payload Enricher
in various situations.
</para>
<para>
In the following example, a <classname>User</classname> object is passed
as the payload of the <interfacename>Message</interfacename>. The
<classname>User</classname> has several properties but only the
<code>username</code> is set initially. The Enricher's
<code>request-channel</code> attribute below is configured to
pass the <classname>User</classname> on to the <code>findUserServiceChannel</code>.
</para>
<para>
Through the implicitly set <code>reply-channel</code> a
<classname>User</classname> object is returned and using the
<code>property</code> sub-element, properties from the reply are
extracted and used to enrich the original payload.
</para>
<programlisting language="xml"><![CDATA[<int:enricher id="findUserEnricher"
input-channel="findUserEnricherChannel"
request-channel="findUserServiceChannel">
<int:property name="email" expression="payload.email"/>
<int:property name="password" expression="payload.password"/>
</int:enricher>]]></programlisting>
<note>
The code samples shown here, are part of the <emphasis>Spring
Integration Samples</emphasis> project. Please feel free to
check it out at:
<ulink url="https://github.com/SpringSource/spring-integration-samples"/>
</note>
<para><emphasis>How do I pass only a subset of data to the request channel?</emphasis></para>
<para>
Using a <code>request-payload-expression</code> attribute
a single property of the payload can be passed on to the request
channel instead of the full message. In the example below on the
username property is passed on to the request channel. Keep in mind,
that alwhough only the username is passed on, the resulting message
send to the request channel will contain the full set of
<classname>MessageHeaders</classname>.
</para>
<programlisting language="xml"><![CDATA[<int:enricher id="findUserByUsernameEnricher"
input-channel="findUserByUsernameEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="email" expression="payload.email"/>
<int:property name="password" expression="payload.password"/>
</int:enricher>]]></programlisting>
<para><emphasis>How can I enrich payloads that consist of Collection data?</emphasis></para>
<para>
In the following example, instead of a <classname>User</classname> object,
a <interfacename>Map</interfacename> is passed in. The
<interfacename>Map</interfacename> contains the username under the map
key <code>username</code>. Only the <code>username</code> is passed on
to the request channel. The reply contains a full <classname>User</classname> object, which
is ultimately added to the <interfacename>Map</interfacename> under the
<code>user</code> key.
</para>
<programlisting language="xml"><![CDATA[<int:enricher id="findUserWithMapEnricher"
input-channel="findUserWithMapEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="user" expression="payload"/>
</int:enricher>]]></programlisting>
<para><emphasis>How can I enrich payloads with static information without using a request channel?</emphasis></para>
<para>
Here is an example that does not use a request channel at all,
but solely enriches the message's payload with static values. But please
be aware that the word 'static' is used loosly here. You can still use
SpEL expressions for setting those values.
</para>
<programlisting language="xml"><![CDATA[<int:enricher id="userEnricher"
input-channel="input">
<int:property name="user.updateDate" expression="new java.util.Date()"/>
<int:property name="user.firstName" value="foo"/>
<int:property name="user.lastName" value="bar"/>
<int:property name="user.age" value="42"/>
</int:enricher>]]></programlisting>
</section>
</section>
</section>