How It Works
As you saw in the last recipe, it's easy to handle transactional resources with Spring Batch. When it
comes to transient or unreliable resources, a different tack is required. Such resources tend to be
distributed or manifest problems that eventually resolve themselves. Some (such as web services)
cannot inherently participate in a transaction because of their distributed nature. There are products
that can start a transaction on one server and propagate the transactional context to a distributed server
and complete it there, although this tends to be very rare and inefficient. Alternatively, there's good
support for distributed (“global” or XA) transactions if you can use it Sometimes, however, you may be
dealing with a resource that isn't either of those. A common example might be a call made to a remote
service, such as an RMI service or a REST endpoint. Some invocations will fail but may be retried with
some likelihood of success in a transactional scenario. For example, an update to the database resulting
in DeadLockLoserException might be usefully retried.
Configuring a Step
The simplest example is in the configuration of a step . Here, you can specify exception classes on which
to retry the operation. As with the rollback exceptions, you can delimit this list of exceptions with
newlines or commas:
<step id = "step23">
<chunk reader="reader" writer="writer"
Alternatively, you can leverage Spring Batch's support for retries and recovery in your own code. For
example, you can have a custom ItemWriter in which retry functionality is desired or even an entire
service interface for which retry support is desired.
Spring Batch supports these scenarios through the RetryTemplate that (much like its various other
Template cousins) isolates your logic from the nuances of retries and instead enables you to write the
code as though you were only going to attempt it once. Let Spring Batch handle everything else through
The RetryTemplate supports many use cases, with convenient APIs to wrap otherwise tedious
retry/fail/recover cycles in concise, single-method invocations.
Let's take a look at the modified version of a simple ItemWriter from Recipe 9.5 on how to write a
custom ItemWriter. The solution was simple enough and would ideally work all the time. It fails to
handle the error cases for the service, however. When dealing with RPC, always proceed as if there are
two things that could go wrong. The service itself may surface a semantic or system violation: i.e.,
duplicate database key, invalid credit card number, and so on. This is true whether the service is
distributed or in-VM, of course.