Files
spring-integration/src/reference/docbook/handler-advice.xml
Gary Russell 573c692957 INT-2994 Advice Chain Config via Annotations
Allow configuration of request handler advice chain using

- ServiceActivator
- Filter
- Splitter
- Transformer

annotations.

Also, with splitter, allow setting discardWithinAdvice (See
INT-2938).

INT-2994 Polishing: PR Comments

- Change adviceChain attribute to an array
- Use a boolean for the discardWithinAdvice attribute

INT-2994 Handler Advice Doc Polishing

Add a paragraph about Advice Order.
2013-08-20 12:10:25 -04:00

520 lines
28 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<section version="5.0" xml:id="message-handler-advice-chain" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1999/xhtml"
xmlns:ns4="http://www.w3.org/1998/Math/MathML"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Adding Behavior to Endpoints</title>
<para>
Prior to Spring Integration 2.2, you could add behavior to an entire Integration flow by adding
an AOP Advice to a poller's &lt;advice-chain /&gt; element. However, let's say
you want to retry, say, just a ReST Web Service call, and not any downstream endpoints.
</para>
<para>
For example, consider the following flow:
</para>
<para>
<emphasis>inbound-adapter->poller->http-gateway1->http-gateway2->jdbc-outbound-adapter</emphasis>
</para>
<para>
If you configure some retry-logic into an advice chain on the poller, and, the call to
<emphasis>http-gateway2</emphasis> failed because of a network glitch, the retry would cause
both <emphasis>http-gateway1</emphasis> and <emphasis>http-gateway2</emphasis> to be called
a second time. Similarly, after a transient failure in the
<emphasis>jdbc-outbound-adapter</emphasis>, both http-gateways
would be called a second time before again calling the <emphasis>jdbc-outbound-adapter</emphasis>.
</para>
<para>
Spring Integration 2.2 adds the ability to add behavior to individual endpoints. This is achieved
by the addition of the &lt;request-handler-advice-chain /&gt; element to many endpoints. For example:
</para>
<para><programlisting language="xml"><![CDATA[<int-http:outbound-gateway id="withAdvice"
url-expression="'http://localhost/test1'"
request-channel="requests"
reply-channel="nextChannel">
<int:request-handler-advice-chain>
<ref bean="myRetryAdvice" />
</request-handler-advice-chain>
</int-http:outbound-gateway>]]></programlisting></para>
<para>
In this case, <emphasis>myRetryAdvice</emphasis> will only be applied locally to this gateway and
will not apply to further actions taken downstream after the reply is sent to the
<emphasis>nextChannel</emphasis>. The scope of the advice is limited to the endpoint itself.
</para>
<important>
<para>
At this time, you cannot advise an entire &lt;chain/&gt; of endpoints. The schema does not allow
a &lt;request-handler-advice-chain/&gt; as a child element of the chain itself.
</para>
<para>
However, a &lt;request-handler-advice-chain/&gt; can be added to individual reply-producing endpoints
<emphasis>within</emphasis> a &lt;chain/&gt; element.
An exception is that, in a chain that produces no reply, because the last element in the chain is an
<emphasis>outbound-channel-adapter</emphasis>, that <emphasis>last</emphasis> element cannot be advised. If you
need to advise such an element, it must be moved outside of the chain (with the
<emphasis>output-channel</emphasis> of the chain being the <emphasis>input-channel</emphasis> of
the adapter. The adapter can then be advised as normal. For chains that produce a reply, every child
element can be advised.
</para>
</important>
<section id="advice-classes">
<title>Provided Advice Classes</title>
<para>
In addition to providing the general mechanism to apply AOP Advice classes in this way, three
standard Advices are provided:
</para>
<itemizedlist>
<listitem>RequestHandlerRetryAdvice</listitem>
<listitem>RequestHandlerCircuitBreakerAdvice</listitem>
<listitem>ExpressionEvaluatingRequestHandlerAdvice</listitem>
</itemizedlist>
<para>
These are each described in detail in the following sections.
</para>
<section id="retry-advice">
<title>Retry Advice</title>
<para>
The retry advice (<classname>o.s.i.handler.advice.RequestHandlerRetryAdvice</classname>)
leverages the rich retry mechanisms provided by the
<ulink url="https://github.com/SpringSource/spring-retry">spring-retry</ulink> project. The core component
of <emphasis>spring-retry</emphasis> is the <classname>RetryTemplate</classname>, which allows configuration
of sophisticated retry scenarios, including <classname>RetryPolicy</classname> and <classname>BackoffPolicy</classname>
strategies, with a number of implementations,
as well as a <classname>RecoveryCallback</classname> strategy to determine the action to take when retries
are exhausted.
</para>
<para><emphasis>Stateless Retry</emphasis></para>
<para>
Stateless retry is the case where the retry activity is handled entirely within the advice, where the thread
pauses (if so configured) and retries the action.
</para>
<para><emphasis>Stateful Retry</emphasis></para>
<para>
Stateful retry is the case where the retry state is managed within the advice, but where an exception is thrown
and the caller resubmits the request. An example for stateful retry is when we want the message originator
(e.g. JMS) to be responsible for resubmitting, rather than performing it on the current thread. Stateful retry
needs some mechanism to detect a retried submission.
</para>
<para><emphasis>Further Information</emphasis></para>
<para>
For more information on <emphasis>spring-retry</emphasis>, refer to the project's javadocs, as well as the
reference documentation for <ulink url="http://static.springsource.org/spring-batch/reference/html/retry.html">
Spring Batch</ulink>, where <emphasis>spring-retry</emphasis> originated.
</para>
<caution>
The default back off behavior is no back off - retries are attempted immediately.
Using a back off policy that causes threads to pause between attempts may cause performance issues, including
excessive memory use and thread starvation. In high volume environments, back off policies should be used
with caution.
</caution>
<section id="retry-config">
<title>Configuring the Retry Advice</title>
<para>
The following examples use a simple &lt;service-activator />&gt; that always throws an exception:
<programlisting language="java"><![CDATA[public class FailingService {
public void service(String message) {
throw new RuntimeException("foo");
}
}]]></programlisting>
</para>
<para><emphasis>Simple Stateless Retry</emphasis></para>
<para>
This example uses the default <emphasis>RetryTemplate</emphasis> which has a
<emphasis>SimpleRetryPolicy</emphasis> which tries 3 times. There is no <emphasis>BackoffPolicy</emphasis>
so the 3 attempts are made back-to-back-to-back with no delay between attempts. There is no
<emphasis>RecoveryCallback</emphasis> so, the result is to throw the
exception to the caller after the final failed retry occurs. In a <emphasis>Spring Integration</emphasis>
environment, this final exception might be handled using an <emphasis>error-channel</emphasis> on
the inbound endpoint.
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice"/>
</request-handler-advice-chain>
</int:service-activator>
DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
DEBUG [task-scheduler-2]Retry: count=0
DEBUG [task-scheduler-2]Checking for rethrow: count=1
DEBUG [task-scheduler-2]Retry: count=1
DEBUG [task-scheduler-2]Checking for rethrow: count=2
DEBUG [task-scheduler-2]Retry: count=2
DEBUG [task-scheduler-2]Checking for rethrow: count=3
DEBUG [task-scheduler-2]Retry failed last attempt: count=3]]></programlisting></para>
<para><emphasis>Simple Stateless Retry with Recovery</emphasis></para>
<para>
This example adds a <classname>RecoveryCallback</classname> to the
above example; it uses a <classname>ErrorMessageSendingRecoverer</classname>
to send an <emphasis>ErrorMessage</emphasis> to a channel.
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
<property name="recoveryCallback">
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="myErrorChannel" />
</bean>
</property>
</bean>
</request-handler-advice-chain>
</int:int:service-activator>
DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
DEBUG [task-scheduler-2]Retry: count=0
DEBUG [task-scheduler-2]Checking for rethrow: count=1
DEBUG [task-scheduler-2]Retry: count=1
DEBUG [task-scheduler-2]Checking for rethrow: count=2
DEBUG [task-scheduler-2]Retry: count=2
DEBUG [task-scheduler-2]Checking for rethrow: count=3
DEBUG [task-scheduler-2]Retry failed last attempt: count=3
DEBUG [task-scheduler-2]Sending ErrorMessage :failedMessage:[Payload=...]]]></programlisting></para>
<para><emphasis>Stateless Retry with Customized Policies, and Recovery</emphasis></para>
<para>
For more sophistication, we can provide the advice with a customized <emphasis>RetryTemplate</emphasis>.
This example continues to use the <classname>SimpleRetryPolicy</classname> but it
increases the attempts to 4. It also adds an <classname>ExponentialBackoffPolicy</classname>
where the first retry waits 1 second, the second waits 5 seconds and the third waits 25 (for 4
attempts in all).
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
<property name="recoveryCallback">
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="myErrorChannel" />
</bean>
</property>
<property name="retryTemplate" ref="retryTemplate" />
</bean>
</request-handler-advice-chain>
</int:service-activator>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="4" />
</bean>
</property>
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="1000" />
<property name="multiplier" value="5" />
</bean>
</property>
</bean>
27.058 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=...]
27.071 DEBUG [task-scheduler-1]Retry: count=0
27.080 DEBUG [task-scheduler-1]Sleeping for 1000
28.081 DEBUG [task-scheduler-1]Checking for rethrow: count=1
28.081 DEBUG [task-scheduler-1]Retry: count=1
28.081 DEBUG [task-scheduler-1]Sleeping for 5000
33.082 DEBUG [task-scheduler-1]Checking for rethrow: count=2
33.082 DEBUG [task-scheduler-1]Retry: count=2
33.083 DEBUG [task-scheduler-1]Sleeping for 25000
58.083 DEBUG [task-scheduler-1]Checking for rethrow: count=3
58.083 DEBUG [task-scheduler-1]Retry: count=3
58.084 DEBUG [task-scheduler-1]Checking for rethrow: count=4
58.084 DEBUG [task-scheduler-1]Retry failed last attempt: count=4
58.086 DEBUG [task-scheduler-1]Sending ErrorMessage :failedMessage:[Payload=...]]]></programlisting></para>
<para><emphasis>Simple Stateful Retry with Recovery</emphasis></para>
<para>
To make retry stateful, we need to provide the Advice with a RetryStateGenerator
implementation. This class is used to identify a message as being a resubmission
so that the <emphasis>RetryTemplate</emphasis> can determine the current state of retry
for this message. The framework provides a <classname>SpelExpressionRetryStateGenerator</classname>
which determines the message identifier using a SpEL expression.
This is shown below; this example again uses the default policies (3 attempts with no back off); of
course, as with stateless retry, these policies can be customized.
</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean class="o.s.i.handler.advice.RequestHandlerRetryAdvice">
<property name="retryStateGenerator">
<bean class="o.s.i.handler.advice.SpelExpressionRetryStateGenerator">
<constructor-arg value="headers['jms_messageId']" />
</bean>
</property>
<property name="recoveryCallback">
<bean class="o.s.i.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="myErrorChannel" />
</bean>
</property>
</bean>
</int:request-handler-advice-chain>
</int:service-activator>
24.351 DEBUG [Container#0-1]preSend on channel 'input', message: [Payload=...]
24.368 DEBUG [Container#0-1]Retry: count=0
24.387 DEBUG [Container#0-1]Checking for rethrow: count=1
24.387 DEBUG [Container#0-1]Rethrow in retry for policy: count=1
24.387 WARN [Container#0-1]failure occurred in gateway sendAndReceive
org.springframework.integration.MessagingException: Failed to invoke handler
...
Caused by: java.lang.RuntimeException: foo
...
24.391 DEBUG [Container#0-1]Initiating transaction rollback on application exception
...
25.412 DEBUG [Container#0-1]preSend on channel 'input', message: [Payload=...]
25.412 DEBUG [Container#0-1]Retry: count=1
25.413 DEBUG [Container#0-1]Checking for rethrow: count=2
25.413 DEBUG [Container#0-1]Rethrow in retry for policy: count=2
25.413 WARN [Container#0-1]failure occurred in gateway sendAndReceive
org.springframework.integration.MessagingException: Failed to invoke handler
...
Caused by: java.lang.RuntimeException: foo
...
25.414 DEBUG [Container#0-1]Initiating transaction rollback on application exception
...
26.418 DEBUG [Container#0-1]preSend on channel 'input', message: [Payload=...]
26.418 DEBUG [Container#0-1]Retry: count=2
26.419 DEBUG [Container#0-1]Checking for rethrow: count=3
26.419 DEBUG [Container#0-1]Rethrow in retry for policy: count=3
26.419 WARN [Container#0-1]failure occurred in gateway sendAndReceive
org.springframework.integration.MessagingException: Failed to invoke handler
...
Caused by: java.lang.RuntimeException: foo
...
26.420 DEBUG [Container#0-1]Initiating transaction rollback on application exception
...
27.425 DEBUG [Container#0-1]preSend on channel 'input', message: [Payload=...]
27.426 DEBUG [Container#0-1]Retry failed last attempt: count=3
27.426 DEBUG [Container#0-1]Sending ErrorMessage :failedMessage:[Payload=...]]]></programlisting></para>
<para>
Comparing with the stateless examples, you can see that with stateful retry, the
exception is thrown to the caller on each failure.
</para>
</section>
</section>
<section id="circuit-breaker-advice">
<title>Circuit Breaker Advice</title>
<para>
The general idea of the Circuit Breaker Pattern is that, if a service is not currently available, then
don't waste time (and resources) trying to use it. The
<classname>o.s.i.handler.advice.RequestHandlerCircuitBreakerAdvice</classname>
implements this pattern. When the circuit breaker is in the <emphasis>closed</emphasis> state,
the endpoint will attempt to invoke the
service. The circuit breaker goes to the <emphasis>open</emphasis> state
if a certain number of consecutive attempts fail; when it is in the <emphasis>open</emphasis> state, new requests will
"fail fast" and no attempt will be made to invoke the service until some time has expired.
</para>
<para>
When that time has expired, the circuit breaker is set to the <emphasis>half-open</emphasis> state. When in this state,
if even a single attempt fails, the breaker will immediately
go to the <emphasis>open</emphasis> state; if the attempt succeeds, the breaker will go to the
<emphasis>closed</emphasis> state,
in which case, it won't go to the <emphasis>open</emphasis> state again until the configured number of consecutive failures
again occur. Any successful attempt resets the state to zero failures for the purpose of determining when the
breaker might go to the <emphasis>open</emphasis> state again.
</para>
<para>
Typically, this Advice might be used for external services, where it might take some
time to fail (such as a timeout attempting to make a network connection).
</para>
<para>The <classname>RequestHandlerCircuitBreakerAdvice</classname> has two properties:
<classname>threshold</classname> and <classname>halfOpenAfter</classname>. The <emphasis>threshold</emphasis>
property represents the number of consecutive failures that need to occur before the breaker goes
<emphasis>open</emphasis>. It defaults to 5. The <emphasis>halfOpenAfter</emphasis> property represents
the time after the last failure that the breaker will wait before attempting another request. Default is
1000 milliseconds.
</para>
<para>Example:</para>
<para><programlisting language="xml"><![CDATA[<int:service-activator input-channel="input" ref="failer" method="service">
<int:request-handler-advice-chain>
<bean class="o.s.i.handler.advice.RequestHandlerCircuitBreakerAdvice">
<property name="threshold" value="2" />
<property name="halfOpenAfter" value="12000" />
</bean>
</int:request-handler-advice-chain>
</int:service-activator>
05.617 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=...]
05.638 ERROR [task-scheduler-1]org.springframework.integration.MessageHandlingException: java.lang.RuntimeException: foo
...
10.598 DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
10.600 ERROR [task-scheduler-2]org.springframework.integration.MessageHandlingException: java.lang.RuntimeException: foo
...
15.598 DEBUG [task-scheduler-3]preSend on channel 'input', message: [Payload=...]
15.599 ERROR [task-scheduler-3]org.springframework.integration.MessagingException: Circuit Breaker is Open for ServiceActivator
...
20.598 DEBUG [task-scheduler-2]preSend on channel 'input', message: [Payload=...]
20.598 ERROR [task-scheduler-2]org.springframework.integration.MessagingException: Circuit Breaker is Open for ServiceActivator
...
25.598 DEBUG [task-scheduler-5]preSend on channel 'input', message: [Payload=...]
25.601 ERROR [task-scheduler-5]org.springframework.integration.MessageHandlingException: java.lang.RuntimeException: foo
...
30.598 DEBUG [task-scheduler-1]preSend on channel 'input', message: [Payload=foo...]
30.599 ERROR [task-scheduler-1]org.springframework.integration.MessagingException: Circuit Breaker is Open for ServiceActivator]]></programlisting></para>
<para>
In the above example, the threshold is set to 2 and halfOpenAfter is set to 12 seconds; a
new request arrives every 5 seconds. You can see that
the first two attempts invoked the service; the third and fourth failed with an exception indicating the
circuit breaker is open. The fifth request was attempted because the request was 15 seconds after the last
failure; the sixth attempt fails immediately because the breaker immediately went to <emphasis>open</emphasis>.
</para>
</section>
<section id="expression-advice">
<title>Expression Evaluating Advice</title>
<para>
The final supplied advice class is the
<classname>o.s.i.handler.advice.ExpressionEvaluatingRequestHandlerAdvice</classname>.
This advice is more general than the other two advices. It provides a mechanism to evaluate an expression on the
original inbound message sent to the endpoint. Separate expressions are available to be evaluated, either after
success, or failure. Optionally, a message containing the evaluation result, together with the input message,
can be sent to a message channel.
</para>
<para>
A typical use case for this advice might be with an &lt;ftp:outbound-channel-adapter /&gt;, perhaps to move
the file to one directory if the transfer was successful, or to another directory if it fails:
</para>
<para>
The Advice has properties to set an expression when successful, an expression for failures,
and corresponding channels for each. For the successful case, the message sent to the
<emphasis>successChannel</emphasis> is an <classname>AdviceMessage</classname>, with
the payload being the result of the expression evaluation, and an additional property
<code>inputMessage</code> which contains the original message sent to the handler. A message
sent to the <emphasis>failureChannel</emphasis> (when the handler throws an excecption)
is an ErrorMessage with a payload of <classname>MessageHandlingExpressionEvaluatingAdviceException</classname>.
Like all <classname>MessagingException</classname>s, this payload has <code>failedMessage</code>
and <code>cause</code> properties, as well as an additional property <code>evaluationResult</code>,
containing the result of the expression evaluation.
</para>
</section>
</section>
<section id="custom-advice">
<title>Custom Advice Classes</title>
<para>
In addition to the provided Advice classes above, you can implement your own Advice classes. While you can
provide any implementation of <classname>org.aopalliance.aop.Advice</classname>, it is generally recommended
that you subclass <classname>o.s.i.handler.advice.AbstractRequestHandlerAdvice</classname>.
This has the benefit of avoiding writing low-level <emphasis>Aspect Oriented Programming</emphasis> code as well
as providing a starting point that is specifically tailored for use in this environment.
</para>
<para>
Subclasses need to implement the doInvoke() method:
</para>
<para><programlisting language="java"><![CDATA[/**
* Subclasses implement this method to apply behavior to the {@link MessageHandler} callback.execute()
* invokes the handler method and returns its result, or null).
* @param callback Subclasses invoke the execute() method on this interface to invoke the handler method.
* @param target The target handler.
* @param message The message that will be sent to the handler.
* @return the result after invoking the {@link MessageHandler}.
* @throws Exception
*/
protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception;]]></programlisting>
</para>
<para>
The <emphasis>callback</emphasis> parameter is simply a convenience to avoid subclasses dealing with AOP directly; invoking the
<classname>callback.execute()</classname>
method invokes the message handler.
</para>
<para>
The <emphasis>target</emphasis> parameter is provided for those subclasses that need to maintain state for a
specific handler, perhaps by maintaining that state in a <classname>Map</classname>, keyed by the target.
This allows the same advice to be applied to multiple handlers. The
<classname>RequestHandlerCircuitBreakerAdvice</classname> uses this to
keep circuit breaker state for each handler.
</para>
<para>
The <emphasis>message</emphasis> parameter is the message that will be sent to the handler.
While the advice cannot modify the message
before invoking the handler, it can modify the payload (if it has mutable properties). Typically, an advice would
use the message for logging and/or to send a copy of the message somewhere before or after invoking the
handler.
</para>
<para>
The return value would normally be the value returned by <classname>callback.execute()</classname>;
but the advice does have the
ability to modify the return value. Note that only <classname>AbstractReplyProducingMessageHandler</classname>s
return a value.
</para>
<para><programlisting language="java"><![CDATA[public class MyAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
// add code before the invocation
Object result = callback.execute();
// add code after the invocation
return result;
}
}]]></programlisting></para>
<note>
<para>
In addition to the <code>execute()</code> method, the <interfacename>ExecutionCallback</interfacename> provides
an additional method <code>cloneAndExecute()</code>. This method must be used in cases where the invocation
might be called multiple times within a single execution of <code>doInvoke()</code>, such as in the
<classname>RequestHandlerRetryAdvice</classname>. This is required because the Spring AOP
<classname>org.springframework.aop.framework.ReflectiveMethodInvocation</classname> object maintains state of which
advice in a chain was last invoked; this state must be reset for each call.
</para>
<para>
For more information, see the
<ulink url="http://static.springsource.org/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/ReflectiveMethodInvocation.html">
ReflectiveMethodInvocation</ulink> JavaDocs.
</para>
</note>
</section>
<section id="other-advice">
<title>Other Advice Chain Elements</title>
<para>
While the abstract class mentioned above is provided as a convenience, you can add any <classname>
Advice</classname> to the chain, including a transaction advice.
</para>
</section>
<section id="advising-filters">
<title>Advising Filters</title>
<para>
There is an additional consideration when advising <classname>Filter</classname>s. By default, any discard
actions (when the filter returns false) are performed <emphasis>within</emphasis> the scope of the
advice chain. This could include all the flow downstream of the <emphasis>discard channel</emphasis>.
So, for example if an element downstream of the <emphasis>discard-channel</emphasis> throws an exception,
and there is a retry advice, the process will be retried. This is also the case if
<emphasis>throwExceptionOnRejection</emphasis> is set to true (the exception is thrown within the
scope of the advice).
</para>
<para>
Setting <emphasis>discard-within-advice</emphasis> to "false" modifies this behavior and the discard
(or exception) occurs after the advice chain is called.
</para>
</section>
<section id="advising-with-annotations">
<title>Advising Endpoints Using Annotations</title>
<para>
When configuring certain endpoints using annotations (<code>@Filter</code>, <code>@ServiceActivator</code>,
<code>@Splitter</code>, and <code>@Transformer</code>), you can supply a bean name for the advice
chain in the <code>adviceChain</code> attribute. In addition, the <code>@Filter</code> annotation
also has the <code>discardWithinAdvice</code> attribute, which can be used to configure the discard
behavior as discussed in <xref linkend="advising-filters"/>. An example with the discard being
performed after the advice is shown below.
</para>
<programlisting language="java"><![CDATA[@MessageEndpoint
public class MyAdvisedFilter {
@Filter(inputChannel="input", outputChannel="output",
adviceChain="adviceChain", discardWithinAdvice="false")
public boolean filter(String s) {
return s.contains("good");
}
}]]></programlisting>
</section>
<section id="Advice Order">
<title>Ordering Advices within an Advice Chain</title>
<para>
Advice classes are "around" advices and are applied in a nested fashion. The first advice is the
outermost, the last advice the innermost (closest to the handler being advised). It is important
to put the advice classes in the correct order to achieve the functionality you desire.
</para>
<para>
For example, let's say you want to add a retry advice and a transaction advice.
You may want to place the retry advice advice first, followed by the transaction advice.
Then, each retry will be performed in a new transaction. On the other hand, if you want all the attempts,
and any recovery operations (in the retry <classname>RecoveryCallback</classname>), to be scoped within
the transaction, you would put the transaction advice first.
</para>
</section>
</section>