JIRA: https://jira.spring.io/browse/INT-3417 Polishing Add note about using the accessor.
160 lines
12 KiB
XML
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><delayer></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><delayer></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><delayer></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><delayer></code> can be enriched with mutually exclusive sub-elements <code><transactional></code>
|
|
or <code><advice-chain></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><advice-chain></code>.
|
|
A sample configuration of the <code><delayer></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>
|