diff --git a/docs/src/site/docbook/reference/retry.xml b/docs/src/site/docbook/reference/retry.xml
index 5bc066325..413c6e041 100644
--- a/docs/src/site/docbook/reference/retry.xml
+++ b/docs/src/site/docbook/reference/retry.xml
@@ -17,37 +17,51 @@
RetryOperations strategy. The
RetryOperations interface looks like this:
- public interface RetryOperations {
+ T execute(RetryCallback retryCallback) throws Exception;
-}where the callback is a simple interface that allows you to
- insert some business logic to be retried
+ T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback)
+ throws Exception;
- public interface RetryCallback {
+ T execute(RetryCallback retryCallback, RetryState retryState)
+ throws Exception, ExhaustedRetryException;
- Object doWithRetry(RetryContext context) throws Throwable;
+ T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback,
+ RetryState retryState) throws Exception;
-}The callback is executed and if it fails (by throwing an
+}]]>where the basic callback is a simple interface that
+ allows you to insert some business logic to be retried
+
+ {
+
+ T doWithRetry(RetryContext context) throws Throwable;
+
+}]]>The callback is executed and if it fails (by throwing an
Exception), it will be retried until either it is
- successful, or the implementation decides to abort.
+ successful, or the implementation decides to abort. There are a number of
+ overloaded execute methods in the
+ RetryOperations interface dealing with various use
+ cases for recovery when all retry attempts are exhausted, and also with
+ retry state, which allows clients and implementations to store information
+ between calls (more on this later).
The simplest general purpose implementation of
RetryOperations is
RetryTemplate. It could be used like this
- RetryTemplate template = new RetryTemplate();
+ () {
- public Object doWithRetry(RetryContext context) {
+ public Foo doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return result;
}
-});
+});]]>
In the example we execute a web service call and return the result
to the user. If that call fails then it is retried until a timeout is
@@ -66,6 +80,105 @@ Object result = template.execute(new RetryCallback() {
context is occasionally useful for storing data that need to be shared
between calls to execute.
+
+
+ RecoveryCallback
+
+ When a retry is exhausted the
+ RetryOperations can pass control to a different
+ callback, the RetryCallback. To use this feature
+ clients just pass in the callbacks together to the same method, for
+ example:
+
+ () {
+ public Foo doWithRetry(RetryContext context) {
+ // business logic here
+ },
+ new RecoveryCallback() {
+ // recover logic here
+ }
+});]]>If the business logic does not succeed before the
+ template decides to abort, then the client is given the chance to do
+ some alternate processing through the recovery callback.
+
+
+
+ Stateless Retry
+
+ In the simplest case a retry is just a while loop - the
+ RetryTemplate can just keep trying until it
+ either succeeds or fails. The RetryContext
+ contains some state to determine whether to retry or abort, but this
+ state is on the stack and there is no need to store it anywhere
+ globally, so we call this stateless retry. The distinction between
+ stateless and stateful retry is contained in the implementation of the
+ RetryPolicy (the
+ RetryTemplate can handle both). In a stateless
+ retry, the callback is always executed in the same thread on retry as
+ when it failed.
+
+
+
+ Stateful Retry
+
+ Where the failure has caused a transactional resource to become
+ invalid there are some special considerations. This does not apply to a
+ simple remote call because there was no transactional resource
+ (usually), but it does sometimes apply to a database update, especially
+ when using Hibernate. In this case it only makes sense to rethrow the
+ exception that called the failure immediately, so that the transaction
+ can roll back, and we can start a new valid one.
+
+ In these cases a stateless retry is not good enough because the
+ re-throw and roll back necessarily involve leaving the
+ RetryOperations.execute() method and potentially losing the
+ context that was on the stack. To avoid losing it we have to introduce a
+ storage strategy to lift it off the stack and put it (at a minimum) in
+ heap storage. For this purpose Spring Batch provides a storage strategy
+ RetryContextCache which can be injected into the
+ RetryTemplate. The default implementation of the
+ RetryContextCache is in memory, using a simple
+ Map. Advanced usage with multiple processes in a
+ clustered environment might also consider implementing the
+ RetryContextCache with a cluster cache of some
+ sort (even in a clustered environment this might be overkill).
+
+Part of the reponsibility of the
+ RetryOperations is to recognise the failed
+ operations when they come back in a new execution (and usually wrapped
+ in a new transaction). To facilitate this, Spring Batch provides the
+ RetryState abstraction. This works in
+ conjunction with a special execute methods in
+ the RetryOperations.
+
+ The way the failed operations are recognised is by identifying
+ the state across multiple invocations of the retry. To identify the
+ state the user can provide an RetryState
+ object, and this is responsible for returning a unique key identifying
+ the item. The identifier is used as a key in the
+ RetryContextCache.
+
+
+ Be very careful with the implementation of
+ Object.equals() and Object.hashCode() in
+ the key returned by RetryState. The best
+ advice is to use a business key to identify the items. In the case
+ of a JMS message the message ID can be used.
+
+
+ When the retry is exhausted there is also the option to handle
+ the failed item in a different way, instead of calling the
+ RetryCallback (which is presumed now to be
+ likely to fail). Just like in the stateless case, this option is
+ provided by the RecoveryCallback, which can be
+ provided by passing it in to the execute method
+ of RetryOperations.
+
+ The decision to retry or not is actually delegated to a regular
+ retry policy, so the usual concerns about limits and timeouts can be
+ injected through the RetryPolicy (see
+ below).
+
@@ -100,34 +213,19 @@ Object result = template.execute(new RetryCallback() {
tight loop retrying something that you know in advance is fatal.
-
- Stateless Retry
+ Spring Batch provides some simple general purpose implementations of
+ stateless RetryPolicy, for example a
+ SimpleRetryPolicy, and the
+ TimeoutRetryPolicy used in the example
+ above.
- In the simplest case a retry is just a while loop - the
- RetryTemplate can just keep trying until it
- either succeeds or fails. The RetryContext
- contains some state to determine whether to retry or abort, but this
- state is on the stack and there is no need to store it anywhere
- globally, so we call this stateless retry. The distinction between
- stateless and stateful retry is contained in the implementation of the
- RetryPolicy (the
- RetryTemplate can handle both). In a stateless
- retry, the callback is always executed in the same thread on retry as
- when it failed.
+ The SimpleRetryPolicy just allows a retry on
+ any of a named list of exception types, up to a fixed number of times. It
+ also has a list of "fatal" exceptions that should never be retried, and
+ this list overrides the retryable list, so it can be used to give finer
+ control over the retry behaviour, e.g.
- Spring Batch provides some simple general purpose implementations
- of stateless RetryPolicy, for example a
- SimpleRetryPolicy, and the
- TimeoutRetryPolicy used in the example
- above.
-
- The SimpleRetryPolicy just allows a retry
- on any of a named list of exception types, up to a fixed number of
- times. It also has a list of "fatal" exceptions that should never be
- retried, and this list overrides the retryable list, so it can be used
- to give finer control over the retry behaviour, e.g.
-
- SimpleRetryPolicy policy = new SimpleRetryPolicy(5);
+ () {
+ public Foo doWithRetry(RetryContext context) {
// business logic here
}
-});
+});]]>
- There is also a more flexible implementation called
- ExceptionClassifierRetryPolicy, which allows the
- user to configure different retry behaviour for an arbitrary set of
- excecption types though the ExceptionClassifier
- abstraction. The policy works by calling on the classifier to convert an
- exception into a delegate RetryPolicy, so for
- example, one exception type can be retried more times before failure
- than another by mapping it to a different policy.
+ There is also a more flexible implementation called
+ ExceptionClassifierRetryPolicy, which allows the
+ user to configure different retry behaviour for an arbitrary set of
+ excecption types though the ExceptionClassifier
+ abstraction. The policy works by calling on the classifier to convert an
+ exception into a delegate RetryPolicy, so for
+ example, one exception type can be retried more times before failure than
+ another by mapping it to a different policy.
- Users might need to implement their own retry policies for more
- customized decisions, e.g. if there is a well-known solution-specific
- classification of exceptions into retryable and not retryable.
-
-
-
- Stateful Retry
-
- Where the failure has caused a transactional resource to become
- invalid there are some special considerations. This does not apply to a
- simple remote call because there was no transactional resource
- (usually), but it does sometimes apply to a database update, especially
- when using Hibernate. In this case it only makes sense to rethrow the
- exception that called the failure immediately, so that the transaction
- can roll back, and we can start a new valid one.
-
- In these cases a stateless retry is not good enough because the
- re-throw and roll back necessarily involve leaving the
- RetryOperations.execute() method and potentially losing the
- context that was on the stack. To avoid losing it we have to introduce a
- storage strategy to lift it off the stack and put it (at a minimum) in
- heap storage. For this purpose Spring Batch provides a storage strategy
- RetryContextCache. The default implementation of
- the RetryContextCache is in memory, using a
- simple Map. Advanced usage with multiple
- processes in a clustered environment might also consider implementing
- the RetryContextCache with a cluster cache of
- some sort (even in a clustered environment this might be
- overkill).
-
-
- Item processing and stateful retry
-
- Part of the reponsibility of a stateful retry policy is to
- recognise the failed operations when they come back in a new
- transaction. To facilitate this in the commonest case where an object
- (like a message or message payload) is being processed, Spring Batch
- provides the ItemWriterRetryPolicy. This works
- in conjunction with a special RetryCallback
- implementation ItemWriterRetryCallback, which
- in turn relies on the user providing an
- ItemWriter. This callback implements the common
- pattern where it passes the item to a writer.
-
- The way the failed operations are recognised in this
- implementation is by identifying the item across multiple invocations
- of the retry. To identify the item the user can provide an
- ItemKeyGenerator strategy, and this is
- responsible for returning a unique key identifying the item. The
- identifier is used as a key in the
- RetryContextCache. An
- ItemKeyGenerator can be provided either by
- injecting it directly into the
- ItemWriterRetryCallback, or by implementing the
- interface in the ItemWriter, or by accepting
- the default which is to simply use the item itself as a key.
-
-
- If you use the default item key generation strategy be very
- careful with the implementation of Object.equals() and
- Object.hashCode() in your item class. In particular, if
- the ItemWriter is going to insert the item
- into a database and update a primary key field it is not a good idea
- to use the primary key in the equals and
- hashCode implementations, because their
- values will change before and after the call to teh
- ItemWriter. The best advice is to use a
- business key to identify the items.
-
-
- When the retry is exhausted, because a stateful retry is always
- in a fresh transaction, there is also the option to handle the failed
- item in a different way, instead of calling the
- RetryCallback (which is presumed now to be
- likely to fail). This option is provided by the
- ItemRecoverer strategy. Like the key generator,
- it can be directly injected or provided by implementing the interface
- in the ItemWriter.
-
- The decision to retry or not is actually delegated to a regular
- stateless retry policy, so the usual concerns about limits and
- timeouts can be injected into the
- ItemWriterRetryPolicy through the delegate
- property.
-
-
+ Users might need to implement their own retry policies for more
+ customized decisions, e.g. if there is a well-known solution-specific
+ classification of exceptions into retryable and not retryable.
@@ -249,20 +264,20 @@ template.execute(new RetryCallback() {
RetryTemplate can pause execution according to the
BackoffPolicy in place.
- public interface BackoffPolicy {
+ A BackoffPolicy is free to implement
- the backOff in any way it chooses. The policies provided by Spring Batch
- out of the box all use Object.wait(). A common use case is to
- backoff with an exponentially increasing wait period, to avoid two retries
- getting into lock step and both failing - this is a lesson learned from
- the ethernet. For this purpose Spring Batch provides the
- ExponentialBackoffPolicy.
+}]]>A BackoffPolicy is free to
+ implement the backOff in any way it chooses. The policies provided by
+ Spring Batch out of the box all use Object.wait(). A common
+ use case is to backoff with an exponentially increasing wait period, to
+ avoid two retries getting into lock step and both failing - this is a
+ lesson learned from the ethernet. For this purpose Spring Batch provides
+ the ExponentialBackoffPolicy.
@@ -279,15 +294,15 @@ template.execute(new RetryCallback() {
The interface looks like this:
- public interface RetryListener {
+ callback);
- void onError(RetryContext context, RetryCallback callback, Throwable e);
+ void onError(RetryContext context, RetryCallback callback, Throwable e);
- void close(RetryContext context, RetryCallback callback, Throwable e);
+ void close(RetryContext context, RetryCallback callback, Throwable e);
}
-The open and
+]]>The open and
close callbacks come before and after the entire
retry in the simplest case, and onError applies
to the individual RetryCallback calls. The close
@@ -318,16 +333,16 @@ template.execute(new RetryCallback() {
remoteCall (for more detail on how to configure
AOP interceptors see the Spring User Guide):
- <aop:config>
- <aop:pointcut id="transactional"
- expression="execution(* com...*Service.remoteCall(..))" />
- <aop:advisor pointcut-ref="transactional"
- advice-ref="retryAdvice" order="-1"/>
-</aop:config>
+
+
+
+
-<bean id="retryAdvice"
- class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/>
-
+
+]]>
The example above uses a default
RetryTemplate inside the interceptor. To change the