Files
spring-integration/src/reference/docbook/delayer.xml
Artem Bilan d0fa88ac72 INT-3417: Polishing Docs for MessageHeaders
JIRA: https://jira.spring.io/browse/INT-3417

Polishing

Add note about using the accessor.
2014-07-03 15:19:49 -04:00

160 lines
12 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="delayer"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Delayer</title>
<section id="delayer-introduction">
<title>Introduction</title>
<para>
A Delayer is a simple endpoint that allows a Message flow to be delayed by a certain interval. When
a Message is delayed, the original sender will not block. Instead, the delayed Messages will be
scheduled with an instance of <interfacename>org.springframework.scheduling.TaskScheduler</interfacename>
to be sent to the output channel after the delay has passed. This approach is scalable even for
rather long delays, since it does not result in a large number of blocked sender Threads. On the
contrary, in the typical case a thread pool will be used for the actual execution of releasing the
Messages. Below you will find several examples of configuring a Delayer.
</para>
</section>
<section id="delayer-namespace">
<title>Configuring Delayer</title>
<para>
The <code>&lt;delayer&gt;</code> element is used to delay the Message flow between two Message Channels.
As with the other endpoints, you can provide the 'input-channel' and 'output-channel' attributes,
but the delayer also has 'default-delay' and 'expression' attributes (and 'expression' sub-element) that are used to
determine the number of milliseconds that each Message should be delayed. The following delays all messages by 3 seconds:
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input"
default-delay="3000" output-channel="output"/>]]></programlisting>
If you need per-Message determination of the delay, then you can also provide the SpEL expression
using the 'expression' attribute:
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
default-delay="3000" expression="headers['delay']"/>]]></programlisting>
In the example above, the 3 second delay would only apply when the expression evaluates to
<emphasis>null</emphasis> for a given inbound Message. If you only want to apply a delay to Messages that have
a valid result of the expression evaluation, then you can use a 'default-delay' of 0 (the default).
For any Message that has a delay of 0 (or less), the Message will be sent immediately, on the calling Thread.
<tip>
The delay handler supports expression evaluation results that represent an interval in milliseconds (any
Object whose <methodname>toString()</methodname> method produces a value that can be parsed into a
Long) as well as <classname>java.util.Date</classname> instances representing an absolute time.
In the first case, the milliseconds will be counted from the current time (e.g. a value of 5000
would delay the Message for at least 5 seconds from the time it is received by the Delayer).
With a Date instance, the Message will not be released until the time represented by that Date object.
In either case, a value that equates to a non-positive delay, or a Date in the past, will
not result in any delay. Instead, it will be sent directly to the output channel on the original
sender's Thread. If the expression evaluation result is not a Date, and can not be parsed as a Long, the default
delay (if any) will be applied.
</tip>
<important>
The expression evaluation may throw an evaluation Exception for various reasons, including an invalid
expression, or other conditions. By default, such exceptions are ignored (logged at DEBUG level) and
the delayer falls back to the default delay (if any). You can modify this behavior by setting the
<code>ignore-expression-failures</code> attribute.
By default this attribute is set to <code>true</code> and the Delayer behavior is as described above.
However, if you wish to not ignore expression evaluation exceptions, and throw them to the delayer's caller,
set the <code>ignore-expression-failures</code> attribute to <code>false</code>.
</important>
</para>
<tip>
Notice in the example above that the delay expression is specified as <code>headers['delay']</code>.
This is the SpEL <classname>Indexer</classname> syntax to access a <interfacename>Map</interfacename> element
(<classname>MessageHeaders</classname> implements <interfacename>Map</interfacename>),
it invokes: <code>headers.get("delay")</code>. For simple map element names (that do not contain '.')
you can also use the SpEL <emphasis>dot accessor</emphasis> syntax, where the above header expression
can be specified as <code>headers.delay</code>. But, different results are achieved if the header is missing.
In the first case, the expression will evaluate to <code>null</code>; the second will result in
something like:
<programlisting language="java"><![CDATA[ org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 8):
Field or property 'delay' cannot be found on object of type 'org.springframework.messaging.MessageHeaders']]></programlisting>
So, if there is a possibility of the header being omitted, and you want to fall back to the default
delay, it is generally more efficient (and recommended) to use the
<emphasis>Indexer</emphasis> syntax instead of <emphasis>dot property accessor</emphasis> syntax, because detecting
the null is faster than catching an exception.
</tip>
<para>
The delayer delegates to an instance of Spring's <interfacename>TaskScheduler</interfacename> abstraction.
The default scheduler used by the delayer is the <classname>ThreadPoolTaskScheduler</classname> instance
provided by Spring Integration on startup: <xref linkend="namespace-taskscheduler"/>.
If you want to delegate to a different scheduler, you can provide a reference through the delayer element's
'scheduler' attribute:
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
expression="headers.delay"
scheduler="exampleTaskScheduler"/>
<task:scheduler id="exampleTaskScheduler" pool-size="3"/>]]></programlisting>
<tip>
If you configure an external <classname>ThreadPoolTaskScheduler</classname>
you can set on this scheduler property <code>waitForTasksToCompleteOnShutdown = true</code>.
It allows successful completion of 'delay' tasks, which already in the execution state (releasing the Message),
when the application is shutdown. Before Spring Integration 2.2 this property was available on
the <code>&lt;delayer&gt;</code> element, because <classname>DelayHandler</classname> could create its own
scheduler on the background. Since 2.2 delayer requires an external scheduler
instance and <code>waitForTasksToCompleteOnShutdown</code> was deleted; you should use the scheduler's own configuration.
</tip>
<tip>
Also keep in mind <classname>ThreadPoolTaskScheduler</classname> has a property <code>errorHandler</code> which
can be injected with some implementation of <classname>org.springframework.util.ErrorHandler</classname>.
This handler allows to process an <classname>Exception</classname> from the thread of the scheduled task sending
the delayed message.
By default it uses an <classname>org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler</classname>
and you will see a stack trace in the logs. You might want to consider using an
<classname>org.springframework.integration.channel.MessagePublishingErrorHandler</classname>,
which sends an <classname>ErrorMessage</classname> into an <code>error-channel</code>, either from the failed Message's header or
into the default <code>error-channel</code>.
</tip>
</para>
</section>
<section id="delayer-message-store">
<title>Delayer and Message Store</title>
<para>
The <classname>DelayHandler</classname> persists delayed Messages into the Message Group in the provided
<interfacename>MessageStore</interfacename>. (The 'groupId' is based on required 'id' attribute of <code>&lt;delayer&gt;</code> element.)
A delayed message is removed from the <interfacename>MessageStore</interfacename> by the scheduled task just before
the <classname>DelayHandler</classname> sends the Message to the <code>output-channel</code>. If the provided
<classname>MessageStore</classname> is persistent (e.g. <classname>JdbcMessageStore</classname>) it provides
the ability to not lose Messages on the application shutdown. After application startup, the
<classname>DelayHandler</classname> reads Messages from its Message Group in the <interfacename>MessageStore</interfacename>
and reschedules them with a delay based on the original arrival time of the Message (if the delay is numeric). For messages
where the delay header was a <classname>Date</classname>, that is used when rescheduling.
If a delayed Message remained in the <interfacename>MessageStore</interfacename> more
than its 'delay', it will be sent immediately after startup.
</para>
<para>
The <code>&lt;delayer&gt;</code> can be enriched with mutually exclusive sub-elements <code>&lt;transactional&gt;</code>
or <code>&lt;advice-chain&gt;</code>. The List of these AOP Advices is applied to the proxied internal
<classname>DelayHandler.ReleaseMessageHandler</classname>, which has the responsibility to release the Message, after the delay,
on a <classname>Thread</classname> of the scheduled task. It might be used, for example, when the downstream message flow throws an
Exception and the <classname>ReleaseMessageHandler</classname>'s transaction will be rolled back. In this case the delayed
Message will remain in the persistent <interfacename>MessageStore</interfacename>. You can use any custom
<interfacename>org.aopalliance.aop.Advice</interfacename> implementation within the <code>&lt;advice-chain&gt;</code>.
A sample configuration of the <code>&lt;delayer&gt;</code> may look like this:
<programlisting language="xml"><![CDATA[<int:delayer id="delayer" input-channel="input" output-channel="output"
expression="headers.delay"
message-store="jdbcMessageStore">
<int:advice-chain>
<beans:ref bean="customAdviceBean"/>
<tx:advice>
<tx:attributes>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
</int:advice-chain>
</int:delayer>]]></programlisting>
</para>
<para>
The <classname>DelayHandler</classname> can be exported as a JMX <code>MBean</code>
with managed operations <code>getDelayedMessageCount</code> and <code>reschedulePersistedMessages</code>,
which allows the rescheduling of delayed persisted Messages at runtime, for example, if the
<interfacename>TaskScheduler</interfacename> has previously been stopped.
These operations can be invoked via a <code>Control Bus</code> command:
<programlisting language="java"><![CDATA[Message<String> delayerReschedulingMessage =
MessageBuilder.withPayload("@'delayer.handler'.reschedulePersistedMessages()").build();
controlBusChannel.send(delayerReschedulingMessage);]]></programlisting>
</para>
<note>
For more information regarding the Message Store, JMX and the Control Bus, please read <xref linkend="system-management-chapter"/>.
</note>
</section>
</section>