BATCH-1056: minor fixes

This commit is contained in:
dhgarrette
2009-02-04 16:50:44 +00:00
parent 747517384b
commit a9d1ac3018
3 changed files with 187 additions and 209 deletions

View File

@@ -8,38 +8,39 @@
<title>RepeatTemplate</title>
<para>Batch processing is about repetitive actions - either as a simple
optimisation, or as part of a job. To strategie and generalise the
repetition, and provide what amounts to an iterator framework, Spring
Batch has the <classname>RepeatOperations</classname> interface. The
<classname>RepeatOperations</classname> interface looks like this:</para>
optimisation, or as part of a job. To strategize and generalize the
repetition as well as to provide what amounts to an iterator framework,
Spring Batch has the <classname>RepeatOperations</classname> interface.
The <classname>RepeatOperations</classname> interface looks like
this:</para>
<para><programlisting>public interface RepeatOperations {
ExitStatus iterate(RepeatCallback callback) throws RepeatException;
RepeatStatus iterate(RepeatCallback callback) throws RepeatException;
}</programlisting>where the callback is a simple interface that allows you to
insert some business logic to be repeated</para>
}</programlisting>The callback is a simple interface that allows you to insert
some business logic to be repeated:</para>
<para><programlisting>public interface RepeatCallback {
ExitStatus doInIteration(RepeatContext context) throws Exception;
RepeatStatus doInIteration(RepeatContext context) throws Exception;
}</programlisting>The callback is executed repeatedly, until the
implementation decides that the iteration should end. The return value in
these interfaces is a special form of extendable enumeration (not a true
enumeration because users are free to create new values). An
<classname>ExitStatus</classname> is immutable and conveys information to
the caller of the repeat operations about whether there is any more work
to do. Generally speaking, implementations of
}</programlisting>The callback is executed repeatedly until the implementation
decides that the iteration should end. The return value in these
interfaces is an enumeration that can either be
<code>RepeatStatus.CONTINUABLE</code> or
<code>RepeatStatus.FINISHED</code>. A <classname>RepeatStatus</classname>
conveys information to the caller of the repeat operations about whether
there is any more work to do. Generally speaking, implementations of
<classname>RepeatOperations</classname> should inspect the
<classname>ExitStatus</classname> and use it as part of the decision to
<classname>RepeatStatus</classname> and use it as part of the decision to
end the iteration. Any callback that wishes to signal to the caller that
there is no more work to do can return
<code>ExitStatus.FINISHED</code>.</para>
<code>RepeatStatus.FINISHED</code>.</para>
<para>The simplest general purpose implementation of
<classname>RepeatOperations</classname> is
<classname>RepeatTemplate</classname>. It could be used like this</para>
<classname>RepeatTemplate</classname>. It could be used like this:</para>
<programlisting>RepeatTemplate template = new RepeatTemplate();
@@ -54,13 +55,13 @@ template.iterate(new RepeatCallback() {
});</programlisting>
<para>In the example we return <code>ExitStatus.CONTINUABLE</code> to show
that there is more work to do. The callback can also return
<para>In the example we return <code>RepeatStatus.CONTINUABLE</code> to
show that there is more work to do. The callback can also return
<code>ExitStatus.FINISHED</code> if it wants to signal to the caller that
there is no more work to do. Some iterations can be terminated by
considerations intrinsic to the work being done in the callback, others
are effectively infinite loops as far as the callback is concerned, and
the completion decision is delegated to an external policy as in the case
are effectively infinite loops as far as the callback is concerned and the
completion decision is delegated to an external policy as in the case
above.</para>
<section>
@@ -82,77 +83,44 @@ template.iterate(new RepeatCallback() {
</section>
<section>
<title>ExitStatus</title>
<title>RepeatStatus</title>
<para><classname>ExitStatus</classname> is used by Spring Batch to
indicate whether processing has finished, and if so whether or not is
was successful. It is also used to carry textual information about the
end state of a batch or iteration, in the form of an exit code and a
description of the status in freeform text. These are the properties of
an <classname>ExitStatus</classname>:</para>
<para><classname>RepeatStatus</classname> is an enumeration used by
Spring Batch to indicate whether processing has finished. These are
possible <classname>RepeatStatus</classname> values:</para>
<table>
<title>ExitStatus properties</title>
<tgroup cols="3">
<tgroup cols="2">
<tbody>
<row>
<entry>Property Name</entry>
<entry><emphasis role="bold">Value</emphasis></entry>
<entry>Type</entry>
<entry>Description</entry>
<entry><emphasis role="bold">Description</emphasis></entry>
</row>
<row>
<entry>continuable</entry>
<entry>CONTINUABLE</entry>
<entry>boolean</entry>
<entry>true if there is more work to do</entry>
<entry>There is more work to do.</entry>
</row>
<row>
<entry>exitCode</entry>
<entry>FINISHED</entry>
<entry>String</entry>
<entry>Short code describing the exit status, e.g. CONTINUABLE,
FINISHED, FAILED</entry>
</row>
<row>
<entry>exitDescription</entry>
<entry>String</entry>
<entry>Long description of the exit status, could be a stack
trace for example.</entry>
<entry>No more repetitions should take place.</entry>
</row>
</tbody>
</tgroup>
</table>
<para><classname>ExitStatus</classname> values are designed to be
flexible, so that they can be created with any code and description the
user needs. Spring Batch comes with some standard values out of the box,
to support common use cases, but users are free to create their own
values, as long as the semantics of the <code>continuable</code>
property are honoured.</para>
<para>ExitStatus values can also be combined with various operators
built into the class as methods. You can add an exit code, or
description, or combine the continuable values with logical AND using
methods in ExitStatus. You can also combine two ExitStatus values with
the and method taking ExitStatus as a parameter. The effect of this is
to do a logical AND on the continuable flag, concatenate the
descriptions and replace the exit code with the new value, as long as
the result is continuable, or the input is not continuable. This has the
effect of maintaining the semantics of the continuable flag, but not
making any "surprising" changes to the exit code (e.g. it never becomes
CONTINUABLE when it was already FINISHED, unless someone does something
wilful, like pass in a value that is not continuable, but with a code of
CONTINUABLE).</para>
<para><classname>RepeatStatus</classname> values can also be combined
with a logical AND operation using the <methodname>and</methodname>()
method in <classname>RepeatStatus</classname>. The effect of this is to
do a logical AND on the continuable flag. In other words, if either
status is <code>FINISHED</code>, then the result will be
<code>FINISHED</code>.</para>
</section>
</section>
@@ -167,22 +135,22 @@ template.iterate(new RepeatCallback() {
current policy to create a <classname>RepeatContext</classname> and pass
that in to the <classname>RepeatCallback</classname> at every stage in the
iteration. After a callback completes its
<methodname>doInIteration</methodname> the
<methodname>doInIteration</methodname>, the
<classname>RepeatTemplate</classname> has to make a call to the
<classname>CompletionPolicy</classname> to ask it to update its state
(which will be stored in the <classname>RepeatContext</classname>), then
(which will be stored in the <classname>RepeatContext</classname>). Then
it asks the policy if the iteration is complete.</para>
<para>Spring Batch provides some simple general purpose implementations of
<classname>CompletionPolicy</classname>, for example the
<classname>SimpleCompletionPolicy</classname> used in the example above.
The <classname>SimpleCompletionPolicy</classname> just allows an execution
up to a fixed number of times (with <code>ExitStatus.FINISHED</code>
<classname>CompletionPolicy</classname>. The
<classname>SimpleCompletionPolicy</classname> just allows an execution up
to a fixed number of times (with <code>RepeatStatus.FINISHED</code>
forcing early completion at any time).</para>
<para>Users might need to implement their own completion policies for more
complicated decisions, e.g. a batch processing window that prevents batch
jobs from executing once the online systems are in use.</para>
complicated decisions. For example, a batch processing window that
prevents batch jobs from executing once the online systems are in use
would require a custom policy.</para>
</section>
<section>
@@ -227,7 +195,7 @@ template.iterate(new RepeatCallback() {
interface. The <classname>RepeatTemplate</classname> allows users to
register <classname>RepeatListener</classname>s, and they will be given
callbacks with the <classname>RepeatContext</classname> and
<classname>ExitStatus</classname> where available during the
<classname>RepeatStatus</classname> where available during the
iteration.</para>
<para>The interface looks like this:</para>
@@ -235,7 +203,7 @@ template.iterate(new RepeatCallback() {
<para><programlisting>public interface RepeatListener {
void before(RepeatContext context);
void after(RepeatContext context, ExitStatus result);
void after(RepeatContext context, RepeatStatus result);
void open(RepeatContext context);
@@ -245,13 +213,13 @@ template.iterate(new RepeatCallback() {
}
</programlisting>The <methodname>open</methodname> and
<methodname>close</methodname> callbacks come before and after the entire
iteration, and <methodname>before</methodname>,
<methodname>after</methodname> and <methodname>onError</methodname> apply
to the individual RepeatCallback calls.</para>
iteration. <methodname>before</methodname>, <methodname>after</methodname>
and <methodname>onError</methodname> apply to the individual
RepeatCallback calls.</para>
<para>Note that when there is more than one listener, they are in a list,
so there is an order. In this case <methodname>open</methodname> and
<methodname>before</methodname> are called in the same order, and
<methodname>before</methodname> are called in the same order while
<methodname>after</methodname>, <methodname>onError</methodname> and
<methodname>close</methodname> are called in reverse order.</para>
</section>
@@ -262,7 +230,7 @@ template.iterate(new RepeatCallback() {
<para>Implementations of <classname>RepeatOperations</classname> are not
restricted to executing the callback sequentially. It is quite important
that some implementations are able to execute their callbacks in parallel.
To this end Spring Batch provides the
To this end, Spring Batch provides the
<classname>TaskExecutorRepeatTemplate</classname>, which uses the Spring
<classname>TaskExecutor</classname> strategy to run the
<classname>RepeatCallback</classname>. The default is to use a

View File

@@ -10,12 +10,13 @@
<para>To make processing more robust and less prone to failure, sometimes
it helps to automatically retry a failed operation in case it might
succeed on a subsequent attempt. Errors that are susceptible to this kind
of treatment are transient in nature, for example a remote call to a web
service or RMI service that fails because of a network glitch, or a
<classname>DeadLockLoserException</classname> in a database update. To
automate the retry of such operations Spring Batch has the
<classname>RetryOperations</classname> strategy. The
<classname>RetryOperations</classname> interface looks like this:</para>
of treatment are transient in nature. For example a remote call to a web
service or RMI service that fails because of a network glitch or a
<classname>DeadLockLoserException</classname> in a database update may
resolve themselves after a short wait. To automate the retry of such
operations Spring Batch has the <classname>RetryOperations</classname>
strategy. The <classname>RetryOperations</classname> interface looks like
this:</para>
<para><programlisting>public interface RetryOperations {
@@ -30,8 +31,8 @@
&lt;T&gt; T execute(RetryCallback&lt;T&gt; retryCallback, RecoveryCallback&lt;T&gt; recoveryCallback,
RetryState retryState) throws Exception;
}</programlisting>where the basic callback is a simple interface that allows
you to insert some business logic to be retried</para>
}</programlisting>The basic callback is a simple interface that allows you to
insert some business logic to be retried:</para>
<para><programlisting>public interface RetryCallback&lt;T&gt; {
@@ -86,9 +87,9 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<para>When a retry is exhausted the
<classname>RetryOperations</classname> can pass control to a different
callback, the <classname>RetryCallback</classname>. To use this feature
clients just pass in the callbacks together to the same method, for
example:</para>
callback, the <classname>RecoveryCallback</classname>. To use this
feature clients just pass in the callbacks together to the same method,
for example:</para>
<para><programlisting>Foo foo = template.execute(new RetryCallback&lt;Foo&gt;() {
public Foo doWithRetry(RetryContext context) {
@@ -106,7 +107,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<section>
<title>Stateless Retry</title>
<para>In the simplest case a retry is just a while loop - the
<para>In the simplest case, a retry is just a while loop: the
<classname>RetryTemplate</classname> can just keep trying until it
either succeeds or fails. The <classname>RetryContext</classname>
contains some state to determine whether to retry or abort, but this
@@ -123,12 +124,12 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<title>Stateful Retry</title>
<para>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.</para>
invalid, there are some special considerations. This does not apply to a
simple remote call because there is 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.</para>
<para>In these cases a stateless retry is not good enough because the
re-throw and roll back necessarily involve leaving the
@@ -142,20 +143,21 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<classname>Map</classname>. Advanced usage with multiple processes in a
clustered environment might also consider implementing the
<classname>RetryContextCache</classname> with a cluster cache of some
sort (even in a clustered environment this might be overkill).</para>
sort (though, even in a clustered environment this might be
overkill).</para>
<para>Part of the responsibility of the
<classname>RetryOperations</classname> is to recognise the failed
<classname>RetryOperations</classname> is to recognize 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
<classname>RetryState</classname> abstraction. This works in conjunction
with a special <classname>execute</classname> methods in the
<classname>RetryOperations</classname>.</para>
<para>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 <classname>RetryState</classname> object, and
this is responsible for returning a unique key identifying the item. The
<para>The way the failed operations are recognized is by identifying the
state across multiple invocations of the retry. To identify the state,
the user can provide an <classname>RetryState</classname> object that is
responsible for returning a unique key identifying the item. The
identifier is used as a key in the
<classname>RetryContextCache</classname>.</para>
@@ -176,9 +178,8 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<classname>RetryOperations</classname>.</para>
<para>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 <classname>RetryPolicy</classname> (see
below).</para>
<classname>RetryPolicy</classname>, so the usual concerns about limits
and timeouts can be injected there (see below).</para>
</section>
</section>
@@ -199,8 +200,8 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
another attempt can be made. If another attempt cannot be made (e.g. a
limit is reached or a timeout is detected) then the policy is also
responsible for handling the exhausted state. Simple implementations will
just throw <classname>RetryExhaustedException</classname>, and any
enclosing transaction will be rolled back. More sophisticated
just throw <classname>RetryExhaustedException</classname> which will cause
any enclosing transaction to be rolled back. More sophisticated
implementations might attempt to take some recovery action, in which case
the transaction can remain intact.</para>
@@ -210,9 +211,8 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
doesn't help to retry it. So don't retry on all exception types - try to
focus on only those exceptions that you expect to be retryable. It's not
usually harmful to the business logic to retry more aggressively, but
it's wasteful because if a failure is deterministic there could be a
very tight loop retrying something that you know in advance is
fatal.</para>
it's wasteful because if a failure is deterministic there will be time
spent retrying something that you know in advance is fatal.</para>
</tip>
<para>Spring Batch provides some simple general purpose implementations of
@@ -224,8 +224,8 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
<para>The <classname>SimpleRetryPolicy</classname> 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.</para>
this list overrides the retryable list so that it can be used to give
finer control over the retry behavior:</para>
<programlisting>SimpleRetryPolicy policy = new SimpleRetryPolicy(5);
// Retry on all exceptions (this is the default)
@@ -244,7 +244,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
<para>There is also a more flexible implementation called
<classname>ExceptionClassifierRetryPolicy</classname>, which allows the
user to configure different retry behaviour for an arbitrary set of
user to configure different retry behavior for an arbitrary set of
excecption types though the <classname>ExceptionClassifier</classname>
abstraction. The policy works by calling on the classifier to convert an
exception into a delegate <classname>RetryPolicy</classname>, so for
@@ -252,8 +252,9 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
another by mapping it to a different policy.</para>
<para>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.</para>
customized decisions. For instance, if there is a well-known,
solution-specific, classification of exceptions into retryable and not
retryable.</para>
</section>
<section>
@@ -306,16 +307,16 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
}
</programlisting>The <methodname>open</methodname> and
<methodname>close</methodname> callbacks come before and after the entire
retry in the simplest case, and <methodname>onError</methodname> applies
to the individual RetryCallback calls. The <methodname>close</methodname>
method might also receive a <classname>Throwable</classname>, if there has
been an error it is the last one thrown by the
<classname>RetryCallback</classname>.</para>
retry in the simplest case and <methodname>onError</methodname> applies to
the individual <classname>RetryCallback</classname> calls. The
<methodname>close</methodname> method might also receive a
<classname>Throwable</classname>; if there has been an error it is the
last one thrown by the <classname>RetryCallback</classname>.</para>
<para>Note that when there is more than one listener, they are in a list,
so there is an order. In this case <methodname>open</methodname> is called
in the same order, and <methodname>onError</methodname> and
<methodname>close</methodname> are called in reverse order.</para>
so there is an order. In this case <methodname>open</methodname> will be
called in the same order while <methodname>onError</methodname> and
<methodname>close</methodname> will be called in reverse order.</para>
</section>
<section>

View File

@@ -32,7 +32,7 @@
<section id="chunkOrientedProcessing">
<title>Chunk-Oriented Processing</title>
<para>Spring Batch uses a 'Chunk Oriented' processing style within it's
<para>Spring Batch uses a 'Chunk Oriented' processing style within its
most common implementation. Chunk oriented processing refers to reading
the data one at a time, and creating 'chunks' that will be written out,
within a transaction boundary. One item is read in from an
@@ -61,7 +61,8 @@
<programlisting>
List items = new Arraylist();
for(int i = 0; i &lt; commitInterval; i++){
Object processedItem = itemProcessor.process(itemReader.read());
Object item = itemReader.read()
Object processedItem = itemProcessor.process(item);
items.add(processedItem);
}
itemWriter.write(items);
@@ -120,8 +121,9 @@
<para>It should be noted that, job-repository defaults to
"jobRepository" and transaction-manager defaults to "transactionManger".
Furthermore, the ItemProcessor is not required, since the item could be
directly passed from the reader to the writer.</para>
Furthermore, the <classname>ItemProcessor</classname> is optional, not
required, since the item could be directly passed from the reader to the
writer.</para>
</section>
<section>
@@ -130,13 +132,14 @@
<para>As mentioned above, a step reads in and writes out items,
periodically committing using the supplied
<classname>PlatformTransactionManager</classname>. With a
commit-interval of 1, it will commit after writing only one item. This
is less than ideal in many situations, since beginning and committing a
transaction is expensive. Ideally, it is preferable to process as many
items as possible in each transaction, which is completely dependent
upon the type of data being processed and the resources with which the
step is interacting. For this reason, the number of items that are
processed within a commit can be configured.</para>
commit-interval of 1, it will commit after writing each individual item.
This is less than ideal in many situations, since beginning and
committing a transaction is expensive. Ideally, it is preferable to
process as many items as possible in each transaction, which is
completely dependent upon the type of data being processed and the
resources with which the step is interacting. For this reason, the
number of items that are processed within a commit can be
configured.</para>
<programlisting>
&lt;job id="sampleJob"&gt;
@@ -168,14 +171,15 @@
<title>Setting a StartLimit</title>
<para>There are many scenarios where you may want to control the
number of times a <classname>Step</classname> may be started. An
example is a <classname>Step</classname> that may be run only once,
usually because it invalidates some resource that must be fixed
manually before it can be run again. This is configurable on the step
level, since different steps have different requirements. One Step
that may only be executed once can exist as part of the same
<classname>Job</classname> as <classname>Step</classname> that can be
run infinitely. Below is an example start limit configuration:</para>
number of times a <classname>Step</classname> may be started. For
example, a particular <classname>Step</classname> might need to be
configured so that it only runs once because it invalidates some
resource that must be fixed manually before it can be run again. This
is configurable on the step level, since different steps may have
different requirements. A <classname>Step</classname> that may only be
executed once can exist as part of the same <classname>Job</classname>
as a <classname>Step</classname> that can be run infinitely. Below is
an example start limit configuration:</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -323,7 +327,7 @@
<listitem>
<para>playerSummarization is not start, and the job is immediately
killed, since this is the third execution of playerSummarization,
and it's limit is only 2. The limit must either be raised, or the
and its limit is only 2. The limit must either be raised, or the
<classname>Job</classname> must be executed as a new
<classname>JobInstance</classname>.</para>
</listitem>
@@ -361,15 +365,15 @@
<classname>FlatFileParseException</classname> is thrown, it will be
skipped and counted against the total skip limit of 10. It should be
noted that any failures encountered while reading will not count against
the commit interval. In other words, the commit interval is only
incremented on writes (regardless of success or failure).</para>
the skip limit. In other words, the skip limit is only incremented on
writes (regardless of success or failure).</para>
</section>
<section>
<para>One problem with the example above is that any other exception
besides a <classname>FlatFileParseException</classname> will cause the
<classname>Job</classname> to fail. In certain scenarios this may be the
correct behaviour, however, in certain scenarios it may be easier to
correct behavior. However, in other scenarios it may be easier to
identify which exceptions should cause failure and skip everything
else:<programlisting>
&lt;step name="step1"&gt;
@@ -398,8 +402,8 @@
<para>In most cases you want an exception to cause either a skip or
<classname>Step</classname> failure. However, not all exceptions are
deterministic. If a <classname>FlatFileParseException</classname> is
encountered while reading, it will always be thrown for that record.
Resetting the <classname>ItemReader</classname> will not help. However,
encountered while reading, it will always be thrown for that record;
resetting the <classname>ItemReader</classname> will not help. However,
for other exceptions, such as a
<classname>DeadlockLoserDataAccessException</classname>, which indicates
that the current process has attempted to update a record that another
@@ -438,7 +442,8 @@
the <classname>Step</classname> can be configured with a list of
exceptions that should not cause rollback. The transaction-attribute
attribute is a comma-separated list. Prefixing a class name with the "+"
symbol will indicate that exception should not cause rollback.</para>
symbol will indicate that that exception should not cause
rollback.</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -450,8 +455,8 @@
</programlisting>
<para>Transaction attributes can be used to control multiple other
settings such as isolation and propagation behaviour. More information
on setting transaction attributes can be found in the spring core
settings such as isolation and propagation behavior. More information on
setting transaction attributes can be found in the spring core
documentation.</para>
<section id="transactionalReaders">
@@ -483,21 +488,21 @@
<para>The step has to take care of <classname>ItemStream</classname>
callbacks at the necessary points in its lifecycle. (for more
information on the ItemStream interface, please refer to <xref
linkend="itemStream" />) This is vital if a step fails, and might need
to be restarted, because the <classname>ItemStream</classname> interface
is where the step gets the information it needs about persistent state
between executions.</para>
information on the <classname>ItemStream</classname> interface, please
refer to <xref linkend="itemStream" />) This is vital if a step fails,
and might need to be restarted, because the
<classname>ItemStream</classname> interface is where the step gets the
information it needs about persistent state between executions.</para>
<para>If the <classname>ItemReader</classname>,
<classname>ItemProcessor</classname>, or
<classname>ItemWriter</classname> itself implements the
<classname>ItemStream</classname> interface, then these will be
registered automatically. Any other streams need to be registered
separately. This is often the case where there are indirect
dependencies, like delegates being injected into the reader and writer.
To a stream it can be injected into the <classname>Step</classname>
through the 'streams' element, as illustrated below:</para>
separately. This is often the case where there are indirect dependencies
such as delegates being injected into the reader and writer. A stream
can be registered on the <classname>Step</classname> through the
'streams' element, as illustrated below:</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -526,11 +531,11 @@
<classname>ItemStream</classname>, but both of its delegates are.
Therefore, both delegate writers must be explicitly registered as
streams in order for the framework to handle them correctly. The
<classname>ItemReader</classname> does not need to explicitly registered
as a stream because it is a direct property of the
<classname>ItemReader</classname> does not need to be explicitly
registered as a stream because it is a direct property of the
<classname>Step</classname>. The step will now be restartable and the
state of the reader and writer will be correctly persisted in case of a
failure.</para>
state of the reader and writer will be correctly persisted in the event
of a failure.</para>
</section>
<section>
@@ -560,7 +565,7 @@
</programlisting>
<para>In addition to the <classname>StepListener</classname> interfaces,
annotations are provided address the same concerns.</para>
annotations are provided to address the same concerns.</para>
<section>
<title>StepExecutionListener</title>
@@ -763,9 +768,10 @@
<section>
<title>SkipListener</title>
<para>Both <classname>ItemReadListener</classname> and
<classname>ItemWriteListner</classname> provide a mechanism for being
notified of errors, but neither one will inform you that a record has
<para><classname>ItemReadListener</classname>,
<classname>ItemProcessListener</classname>, and
<classname>ItemWriteListner</classname> all provide mechanisms for
being notified of errors, but none will inform you that a record has
actually been skipped. <methodname>onWriteError</methodname>, for
example, will be called even if an item is retried and successful. For
this reason, there is a separate interface for tracking skipped
@@ -776,9 +782,9 @@
void onSkipInRead(Throwable t);
void onSkipInWrite(S item, Throwable t);
void onSkipInProcess(T item, Throwable t);
void onSkipInWrite(S item, Throwable t);
}
</programlisting>
@@ -814,8 +820,8 @@
<classname>SkipListener</classname> is to log out a skipped item, so
that another batch process or even human process can be used to
evaluate and fix the issue leading to the skip. Because there are
many cases in which the original trasaction may be rolledback,
Spring Batch makes two garantees:</para>
many cases in which the original transaction may be rolled back,
Spring Batch makes two guarantees:</para>
<orderedlist>
<listitem>
@@ -863,8 +869,8 @@
</programlisting>
<note>
<para>TaskletStep will automatically register the tasklet as
<classname>StepExecutionListener</classname> if it implements this
<para><classname>TaskletStep</classname> will automatically register the
tasklet as <classname>StepListener</classname> if it implements this
interface</para>
</note>
@@ -896,7 +902,7 @@
<title>Example Tasklet implementation</title>
<para>Many batch jobs contain steps that must be done before the main
processing begins in order to set up various resources, or after
processing begins in order to set up various resources or after
processing has completed to cleanup those resources. In the case of a
job that works heavily with files, it is often necessary to delete
certain files locally after they have been uploaded successfully to
@@ -962,8 +968,8 @@
<para>With the ability to group steps together within an owning job, comes
the need to be able to control how the job 'flows' from one step to
another. The failure of a <classname>Step</classname> doesn't necessarily
mean that the <classname>Job</classname> should fail. Further, there may
be more than one type of 'success', which determines which
mean that the <classname>Job</classname> should fail. Furthermore, there
may be more than one type of 'success', which determines which
<classname>Step</classname> should be executed next. Depending upon how a
group of Steps is configured, certain steps may not even be processed at
all.</para>
@@ -1082,7 +1088,7 @@
<classname>ExitStatus</classname>. <classname>BatchStatus</classname>
is an enumeration that is a property of both
<classname>JobExecution</classname> and
<classname>StepExecution</classname>, and is used by the framework to
<classname>StepExecution</classname> and is used by the framework to
record the status of a <classname>Job</classname> or
<classname>Step</classname>. It can be one of the following values:
COMPLETED, STARTING, STARTED, FAILED, STOPPING, STOPPED, or UNKNOWN.
@@ -1098,18 +1104,19 @@
<para>At first glance, it would appear that the 'on' attribute
references the <classname>BatchStatus</classname> of the
<classname>Step</classname> it belongs to. However, it references the
<classname>ExitStatus</classname> of the <classname>Step</classname>.
As the name implies, <classname>ExitStatus</classname> represents the
status of a <classname>Step</classname> after it finishes execution.
More specifically, the 'next' element above references the
<classname>Step</classname> to which it belongs. However, it
references the <classname>ExitStatus</classname> of the
<classname>Step</classname>. As the name implies,
<classname>ExitStatus</classname> represents the status of a
<classname>Step</classname> after it finishes execution. More
specifically, the 'next' element above references the
<classname>ExitCode</classname> of the
<classname>ExitStatus</classname>. To write it in English, it says:
"go to stepB if the exit code is FAILED". By default, the exit code is
always the same as the <classname>BatchStatus</classname> for the
Step, which is why the entry above works. However, what if the exit
code needs to be different? A good example comes from the skip sample
job, within the samples project:</para>
job within the samples project:</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -1138,7 +1145,7 @@
</listitem>
</orderedlist>
<para>The above configuration will work, however, something needs to
<para>The above configuration will work. However, something needs to
change the exit code based on the condition of the execution having
skipped records:</para>
@@ -1160,8 +1167,8 @@
that first checks to make sure the <classname>Step</classname> was
successful, and next if the skip count on the
<classname>StepExecution</classname> is higher than 0. If both
conditions are met, a new ExitStatus with an exit code of "COMPLETED
WITH SKIPS" is returned.</para>
conditions are met, a new <classname>ExitStatus</classname> with an
exit code of "COMPLETED WITH SKIPS" is returned.</para>
</section>
</section>
@@ -1199,8 +1206,9 @@
<section>
<title>Programmatic flow decisions</title>
<para>In some situations, more information than the exit status may be
required to decide which step to execute next. In this case, a
<para>In some situations, more information than the
<classname>ExitStatus</classname> may be required to decide which step
to execute next. In this case, a
<classname>JobExecutionDecider</classname> can be used to assist in the
decision.</para>
@@ -1261,10 +1269,10 @@
</programlisting>
<para>The above <classname>Resource</classname> will load the file from
the file system, at the location specified. Note that absolute locations
have to start with a double slash ("//"). In most spring applications,
this solution is good enough because the names of these are known at
compile time. However, in batch scenarios, the file name may need to be
the file system location specified. Note that absolute locations have to
start with a double slash ("//"). In most spring applications, this
solution is good enough because the names of these are known at compile
time. However, in batch scenarios, the file name may need to be
determined at runtime as a parameter to the job. This could be solved
using '-D' parameters, i.e. a system property:</para>
@@ -1284,10 +1292,11 @@
filters and does placeholder replacement on system properties.)</para>
<para>Often in a batch setting it is preferable to parameterize the file
name in the <link linkend="jobParameters">JobParameters</link> of the
job, instead of through system properties, and access them that way. To
allow for this, Spring Batch allows for the late binding of various Job
and Step attributes:</para>
name in the <link
linkend="jobParameters"><classname>JobParameters</classname></link> of
the job, instead of through system properties, and access them that way.
To accomplish this, Spring Batch allows for the late binding of various
Job and Step attributes:</para>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
@@ -1305,7 +1314,7 @@
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{<emphasis role="bold">jobExecutionContext</emphasis>[input.file.name]}" /&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;/bean&gt;
</programlisting>
@@ -1313,7 +1322,7 @@
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{<emphasis role="bold">stepExecutionContext</emphasis>[input.file.name]}" /&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;/bean&gt;
</programlisting>
@@ -1333,7 +1342,7 @@
</programlisting>
<para>Using a scope of <classname>Step</classname> is required in
order to use late binding, since the bean cannot actually be
order to use late binding since the bean cannot actually be
instantiated until the <classname>Step</classname> starts, which
allows the attributes to be found. Because it is not part of the
Spring container by default, it must be added explicitly:</para>