BATCH-1063: Fixed image sizing in both pdf and html

This commit is contained in:
lucasward
2009-02-10 03:27:18 +00:00
parent 0d39705748
commit 52628ea904
8 changed files with 912 additions and 1187 deletions

View File

@@ -258,7 +258,7 @@
in the event of a restart. In order to make this class restartable, the
<classname>ItemStream</classname> interface should be implemented along
with the methods <methodname>open</methodname> and
<methodname>update</methodname>: </para>
<methodname>update</methodname>:</para>
<programlisting> private static final String TOTAL_AMOUNT_KEY = "total.amount";
@@ -306,12 +306,11 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/drivingQueryExample.png"
width="50%" />
scale="80" width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/drivingQueryExample.png"
<imagedata align="center" fileref="images/drivingQueryExample.png"
width="66%" />
</imageobject>
</mediaobject>
@@ -326,12 +325,11 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/drivingQueryJob.png"
width="66%" />
scale="80" width="66%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/drivingQueryJob.png"
<imagedata align="center" fileref="images/drivingQueryJob.png"
width="66%" />
</imageobject>
</mediaobject>

View File

@@ -21,8 +21,8 @@
</listitem>
<listitem>
<para>simple and default implementations that allow for quick adoption
and ease of use out-of-the-box</para>
<para>simple and default implementations that allowed for quick
adoption and ease of use out-of-the-box</para>
</listitem>
<listitem>
@@ -47,13 +47,13 @@
<imageobject role="fo">
<imagedata align="center"
fileref="images/spring-batch-reference-model.png"
format="PNG" />
format="PNG" scale="70" />
</imageobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/spring-batch-reference-model.png"
format="PNG" scale="80" />
format="PNG" scale="85" />
</imageobject>
<caption><para>Figure 2.1: Batch Stereotypes</para></caption>
@@ -63,7 +63,7 @@
language of batch. A Job has one to many steps, which has exactly one
ItemReader, ItemProcessor, and ItemWriter. A job needs to be launched
(JobLauncher), and meta data about the currently running process needs to be
stored (JobRepository).</para>
stored (JobRepository)</para>
<section id="job">
<title id="jobStereotypes">Job</title>
@@ -78,12 +78,12 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/job-heirarchy.png"
scale="80" />
scale="90" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-heirarchy.png" />
<imagedata align="center" fileref="images/job-heirarchy.png"
scale="80" />
</imageobject>
</mediaobject>
@@ -133,11 +133,11 @@
case of this job, there will be one logical
<classname>JobInstance</classname> per day. For example, there will be a
January 1st run, and a January 2nd run. If the January 1st run fails the
first time and is run again the next day, it is still the January 1st
run. (Usually this corresponds with the data it is processing as well,
meaning the January 1st run processes data for January 1st, etc).
Therefore, each <classname>JobInstance</classname> can have multiple
executions (<classname>JobExecution</classname> is discussed in more
first time and is run again the next day, it's still the January 1st
run. (Usually this corresponds with the data its processing as well,
meaning the January 1st run processes data for January 1st, etc) That is
to say, each <classname>JobInstance</classname> can have multiple
executions. (<classname>JobExecution</classname> is discussed in more
detail below) and only one <classname>JobInstance</classname>
corresponding to a particular <classname>Job</classname> can be running
at a given time. The definition of a <classname>JobInstance</classname>
@@ -151,11 +151,11 @@
likely be a business decision, it is left up to the
<classname>ItemReader</classname> to decide. What using the same
<classname>JobInstance</classname> will determine, however, is whether
or not the 'state' (i.e. the <classname>ExecutionContext</classname>,
which is discussed below) from previous executions will be used. Using a
new <classname>JobInstance</classname> will mean 'start from the
beginning' and using an existing instance will generally mean 'start
from where you left off'.</para>
or not the 'state' (i.e. The ExecutionContext, which is discussed below)
from previous executions will be used. Using a new
<classname>JobInstance</classname> will mean 'start from the beginning'
and using an existing instance will generally mean 'start from where you
left off'.</para>
</section>
<section id="jobParameters">
@@ -165,20 +165,21 @@
differs from <classname>Job</classname>, the natural question to ask is:
"how is one <classname>JobInstance</classname> distinguished from
another?" The answer is: <classname>JobParameters</classname>.
<classname>JobParameters</classname> is a set of parameters used to
start a batch job. They can be used for identification or even as
<classname>JobParameters</classname> are any set of parameters used to
start a batch job, which can be used for identification or even as
reference data during the run:</para>
<para><mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/job-stereotypes-parameters.png"
scale="80" />
scale="90" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-heirarchy.png" />
fileref="images/job-stereotypes-parameters.png"
scale="80" />
</imageobject>
</mediaobject></para>
@@ -203,10 +204,10 @@
will not be considered complete unless the execution completes
successfully. Using the EndOfDay <classname>Job</classname> described
above as an example, consider a <classname>JobInstance</classname> for
01-01-2008 that failed the first time it was run. If it is run again
01-01-2008 that failed the first time it was run. If it is ran again,
with the same job parameters as the first run (01-01-2008), a new
<classname>JobExecution</classname> will be created. However, there will
still be only one <classname>JobInstance</classname>.</para>
JobExecution will be created. However, there will still be only one
<classname>JobInstance</classname>.</para>
<para>A <classname>Job</classname> defines what a job is and how it is
to be executed, and <classname>JobInstance</classname> is a purely
@@ -225,9 +226,9 @@
<entry>status</entry>
<entry>A <classname>BatchStatus</classname> object that
indicates the status of the execution. While running, it's
BatchStatus.STARTED, if it fails, it's BatchStatus.FAILED, and
if it finishes successfully, it's BatchStatus.COMPLETED</entry>
indicates the status of the execution. While it's running, it's
BatchStatus.STARTED, if it fails it's BatchStatus.FAILED, and if
it finishes successfully it's BatchStatus.COMPLETED</entry>
</row>
<row>
@@ -258,19 +259,18 @@
<entry>createTime</entry>
<entry>A <classname>java.util.Date</classname> representing the
current system time when the <classname>JobExecution</classname>
was first persisted. The job may not have been started yet (and
thus has no start time), but it will always have a createTime,
which is required by the framework for managing job level
<classname>ExecutionContext</classname>s.</entry>
current system time when the JobExecution was first persisted.
The job may not have been started yet (and thus has no start
time), but it will always have a createTime, which is required
by the framework for managing job level
ExecutionContexts.</entry>
</row>
<row>
<entry>lastUpdated</entry>
<entry>A <classname>java.util.Date</classname> representing the
last time a <classname>JobExecution</classname> was
persisted.</entry>
last time a JobExecution was persisted.</entry>
</row>
<row>
@@ -389,17 +389,17 @@
will be kicked off again for 01-01, starting where it left off and
completing successfully at 9:30. Because it's now the next day, the
01-02 job must be run as well, which is kicked off just afterwards at
9:31, and completes in its normal one hour time at 10:30. There is no
9:31, and completes in it's normal one hour time at 10:30. There is no
requirement that one <classname>JobInstance</classname> be kicked off
after another, unless there is potential for the two jobs to attempt to
access the same data, causing issues with locking at the database level.
It is entirely up to the scheduler to determine when a
<classname>Job</classname> should be run. Since they're separate
<classname>JobInstance</classname>s, Spring Batch will make no attempt
to stop them from being run concurrently. (Attempting to run the same
JobInstances, Spring Batch will make no attempt to stop them from being
run concurrently. (Attempting to run the same
<classname>JobInstance</classname> while another is already running will
result in a <classname>JobExecutionAlreadyRunningException</classname>
being thrown). There should now be an extra entry in both the
being thrown) There should now be an extra entry in both the
<classname>JobInstance</classname> and
<classname>JobParameters</classname> tables, and two extra entries in
the <classname>JobExecution</classname> table:</para>
@@ -554,12 +554,12 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/jobHeirarchyWithSteps.png"
scale="80" />
scale="90" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/jobHeirarchyWithSteps.png" />
<imagedata align="center" fileref="images/jobHeirarchyWithSteps.png"
scale="90" />
</imageobject>
</mediaobject>
@@ -568,12 +568,12 @@
<para>A <classname>StepExecution</classname> represents a single attempt
to execute a <classname>Step</classname>. A new
<classname>StepExecution</classname> will be created each time a
<classname>Step</classname> is run, similar to
<classname>JobExecution</classname>. However, if a step fails to execute
because the step before it fails, there will be no execution persisted
for it. A <classname>StepExecution</classname> will only be created when
its <classname>Step</classname> is actually started.</para>
<classname>StepExecution</classname> will be created each time a Step is
run, similar to <classname>JobExecution</classname>. However, if a step
fails to execute because the step before it fails, there will be no
execution persisted for it. A <classname>StepExecution</classname> will
only be created when it's <classname>Step</classname> is actually
started.</para>
<para>Step executions are represented by objects of the
<classname>StepExecution</classname> class. Each execution contains a
@@ -596,8 +596,8 @@
<entry>A <classname>BatchStatus</classname> object that
indicates the status of the execution. While it's running, the
status is BatchStatus.STARTED, if it fails, the status is
BatchStatus.FAILED, and if it finishes successfully, the status
status is BatchStatus.STARTED, if it fails the status is
BatchStatus.FAILED, and if it finishes successfully the status
is BatchStatus.COMPLETED</entry>
</row>
@@ -670,15 +670,15 @@
<row>
<entry>processSkipCount</entry>
<entry>The number of times <methodname>process</methodname> has
failed, resulting in a skipped item.</entry>
<entry>The number of times process has failed, resulting in a
skipped item.</entry>
</row>
<row>
<entry>filterCount</entry>
<entry>The number of items that have been 'filtered' by the
<classname>ItemProcessor</classname>.</entry>
ItemProcessor</entry>
</row>
<row>
@@ -848,14 +848,14 @@
this scenario. This value will be updated just before each commit by the
framework, and can contain multiple rows corresponding to entries within
the <classname>ExecutionContext</classname>. Being notified before a
commit requires one of the various <classname>StepListener</classname>s,
or an <classname>ItemStream</classname>, which are discussed in more
detail later in this guide. As with the previous example, it is assumed
that the <classname>Job</classname> is restarted the next day. When it is
restarted, the values from the <classname>ExecutionContext</classname> of
the last run are reconstituted from the database, and when the
<classname>ItemReader</classname> is opened, it can check to see if it has
any stored state in the context, and initialize itself from there:</para>
commit requires one of the various StepListeners, or an
<classname>ItemStream</classname>, which are discussed in more detail
later in this guide. As with the previous example, it is assumed that the
Job is restarted the next day. When it is restarted, the values from the
<classname>ExecutionContext</classname> of the last run are reconstituted
from the database, and when the <classname>ItemReader</classname> is
opened, it can check to see if it has any stored state in the context, and
initialize itself from there:</para>
<programlisting> if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
log.debug("Initializing for restart. Restart data is: " + executionContext);
@@ -898,7 +898,7 @@
exists per <classname>StepExecution</classname> at any given time. Clients
of the <classname>ExecutionContext</classname> should be careful because
this creates a shared keyspace, so care should be taken when putting
values in to ensure no data is overwritten. However, the
values in to ensure no data is overwritten, however, the
<classname>Step</classname> stores absolutely no data in the context, so
there is no way to adversely affect the framework.</para>
@@ -915,7 +915,7 @@
</programlisting>
<para>As noted in the comment, ecStep will not equal ecJob; they are two
<para>As noted in the comment, ecStep will not equal ecJob, they are two
different <classname>ExecutionContext</classname>s. The one scoped to the
<classname>Step</classname> will be saved at every commit point in the
<classname>Step</classname>, whereas the one scoped to the
@@ -981,7 +981,7 @@
the output of a <classname>Step</classname>, one item at a time.
Generally, an item writer has no knowledge of the input it will receive
next, only the item that was passed in its current invocation. More
details about the <classname>ItemWriter</classname> interface and its
details about the <classname>ItemWriter</classname> interface and it's
various implementations can be found in <xref
linkend="readersAndWriters" /></para>
</section>
@@ -994,9 +994,9 @@
<classname>ItemReader</classname> reads one item, and the
<classname>ItemWriter</classname> writes them, the
<classname>ItemProcessor</classname> provides access to transform or apply
other business processing. If, while processing the item, it is determined
that the item is not valid, returning null indicates that the item should
not be written out. More details about the ItemProcessor interface can be
found in <xref linkend="readersAndWriters" />.</para>
other business processing. If while processing the item it's determined
that it's not valid, returning null indicates that it should not be
written out. More details about the ItemProcessor interface can be found
in <xref linkend="readersAndWriters" />.</para>
</section>
</chapter>

View File

@@ -10,31 +10,30 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/spring-batch-reference-model.png" scale="80" />
fileref="images/spring-batch-reference-model.png" scale="90" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/spring-batch-reference-model.png"
fileref="images/spring-batch-reference-model.png" scale="70"
width="75%" />
</imageobject>
</mediaobject>
<para>While the <classname>Job</classname> object may seem like a simple
container for steps, there are many configuration options of which a
developers must be aware . Furthermore, there are many considerations for
how a <classname>Job</classname> will be run and how its meta-data will be
stored during that run. This chapter will explain the various configuration
options and runtime concerns of a <classname>Job</classname>.</para>
<para>While the Job object may seem like a simple container for steps, there
are many configuration options that developers should be aware of.
Furthermore, there are many considerations for how a
<classname>Job</classname> will be run and how its meta data will be stored
during that run. This chapter will explain the various configuration options
and runtime concerns of a <classname>Job</classname>.</para>
<section>
<title>Configuring a Job</title>
<para>There are multiple implementations of the <link
linkend="job"><classname>Job</classname></link> interface, however, the
namespace abstracts away the differences in configuration. It has only
three required dependencies: a name, <classname>JobRepository</classname>,
and a list of <classname>Step</classname>s.</para>
linkend="job">Job</link> interface, however, the namespace abstracts away
the differences in configuration. It has only three required dependencies:
a name, <classname>JobRepository</classname>, and a list of Steps.</para>
<programlisting>
&lt;job id="footballJob"&gt;
@@ -58,26 +57,20 @@
</programlisting>
<para>Note that if the job repository's default id is overridden, it must
be explicitly specified on <emphasis role="bold">every</emphasis>
<classname>Step</classname> <emphasis role="bold">as well as</emphasis> on
the <classname>Job</classname>.</para>
<section>
<title>Restartability</title>
<para>One key issue when execution a batch job concerns the behavior of
a <classname>Job</classname> when it is restarted? The launching of a
<classname>Job</classname> is considered to be a 'restart' if a
<classname>JobExecution</classname> already exists for the particular
<classname>JobInstance</classname>. Ideally, all jobs should be able to
start up where they left off, but there are scenarios where this is not
possible. <emphasis role="bold">It is entirely up to the developer to
ensure that a new JobInstance is created in this scenario</emphasis>.
However, Spring Batch does provide some help. If a
<classname>Job</classname> should never be restarted, but should always
be run as part of a new <classname>JobInstance</classname>, then the
restartable property may be set to 'false':</para>
<para>One key concern when execution a batch job, is what happens when a
failed job is restarted? A Job is considered to have been 'restarted' if
the same <classname>JobInstance</classname> has more than one
JobExecution. Ideally, all jobs should be able to start up where they
left off, but there are scenarios where this is not possible. <emphasis
role="bold">It is entirely up to the developer to ensure that a new
instance is always created in this scenario</emphasis>. However, Spring
Batch does provide some help. If a Job should never be restarted, but
should always be run as part of a new
<classname>JobInstance</classname>, then the restartable property may be
set to 'false':</para>
<programlisting>
&lt;job id="footballJob" <emphasis role="bold">restartable="false"</emphasis>&gt;
@@ -138,9 +131,8 @@
</programlisting>
<para><classname>JobListener</classname>s can be added to a
<classname>SimpleJob</classname> via the listeners element on the
job:</para>
<para>Listeners can be added to a <classname>SimpleJob</classname> via
the setJobListeners property:</para>
<programlisting>
&lt;job id="footballJob"&gt;
@@ -154,17 +146,17 @@
</programlisting>
<para>It should be noted that <methodname>afterJob</methodname> will be
called regardless of the success or failure of the
<classname>Job</classname>. If success or failure needs to be determined
it can be obtained from the <classname>JobExecution</classname>:</para>
<para>It should be noted that afterJob will be called regardless of the
success or failure of the <classname>Job</classname>. If success or
failure needs to be determined it can be obtained from the
<classname>JobExecution</classname>:</para>
<programlisting>
void afterJob(JobExecution jobExecution){
if( jobExecution.getStatus() == BatchStatus.COMPLETED ){
if( jobExecution.getStatus = BatchStatus.COMPLETED ){
//job success
}
else if(jobExecution.getStatus() == BatchStatus.FAILED){
else if(jobExecution.getStatus = BatchStatus.FAILED){
//job failure
}
}
@@ -176,14 +168,14 @@
<title>JobFactory and Stateful Components in Steps</title>
<para>Unlike many traditional Spring applications, many of the
components of a batch application are stateful; the file readers and
components of a batch application are stateful, the file readers and
writers are obvious examples. The recommended way to deal with this is
to create a fresh <classname>ApplicationContext</classname> for each job
execution. If the <classname>Job</classname> is launched from the
command line with <classname>CommandLineJobRunner</classname>, this is
trivial. For more complex launching scenarios where jobs are executed in
parallel or serially from the same process, some extra steps have to be
taken to ensure that the <classname>ApplicationContext</classname> is
command line with <classname>CommandLineJobRunner</classname> this is
trivial. For more complex launching scenarios, where jobs are executed
in parallel or serially from the same process, some extra steps have to
be taken to ensure that the <classname>ApplicationContext</classname> is
refreshed. This is preferable to using prototype scope for the stateful
beans because then they would not receive lifecycle callbacks from the
container at the end of use. (e.g. through destroy-method in XML)</para>
@@ -204,13 +196,12 @@
<title>Configuring a JobRepository</title>
<para>As described in earlier, the <link
linkend="jobRepository"><classname>JobRepository</classname></link> is
used for basic CRUD operations of the various persisted domain objects
within Spring Batch, such as <classname>JobExecution</classname> and
<classname>StepExecution</classname>. It is required by many of the major
framework features, such as the <classname>JobLauncher</classname>,
linkend="jobRepository">JobRepository</link> is used for basic CRUD
operations of the various persisted domain objects within Spring Batch,
such as JobExecution and StepExecution. It is required by many of the
major framework features, such as the <classname>JobLauncher</classname>,
<classname>Job</classname>, and <classname>Step</classname>. The batch
namespace abstracts away many of the implementation details of the
namespace abstract much of the implementation details of the
<classname>JobRepository</classname> implementations and their
collaborators. However, there are still a few configuration options
available:</para>
@@ -235,7 +226,7 @@
<para>If the namespace is used, transactional advice will be
automatically created around the repository. This is to ensure that the
batch meta data, including state that is necessary for restarts after a
failure, is persisted correctly. The behavior of the framework is not
failure, is persisted correctly. The behaviour of the framework is not
well defined if the repository methods are not transactional. The
isolation level in the <code>create*</code> method attributes is
specified separately to ensure that when jobs are launched, if two
@@ -255,7 +246,7 @@
</programlisting></para>
<para>If the namespace or factory beans aren't used then it is also
essential to configure the transactional behavior of the repository
essential to configure the transactional behaviour of the repository
using AOP:</para>
<para><programlisting>
@@ -275,7 +266,7 @@
<para>This fragment can be used as is, with almost no changes. Remember
also to include the appropriate namespace declarations and to make sure
spring-tx and spring-aop (or the whole of spring) are on the
spring-tx and spring-aop (or the whole of spring) is on the
classpath.</para>
</section>
@@ -303,7 +294,7 @@
as SYSTEM.TEST_JOB_EXECUTION.</para>
<note>
<para>Only the table prefix is configurable. The table and column
<para>Only the table prefix is configurable, the table and column
names are not.</para>
</note>
</section>
@@ -312,11 +303,10 @@
<title>In-Memory Repository</title>
<para>There are scenarios in which you may not want to persist your
domain objects to the database. One reason may be speed; storing domain
domain objects to the database. One reason may be speed, storing domain
objects at each commit point takes extra time. Another reason may be
that you just don't need to persist status for a particular job. For
this reason, Spring batch provides an in-memory Map version of the job
respository:</para>
that you just don't need to persist status for a particular job. Spring
batch provides a solution:</para>
<programlisting> &lt;bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" /&gt;</programlisting>
@@ -328,7 +318,7 @@
<para>The most basic implementation of the
<classname>JobLauncher</classname> interface is the
<classname>SimpleJobLauncher</classname>. Its only required dependency is
<classname>SimpleJobLauncher</classname>. It's only required dependency is
a <classname>JobRepository</classname>, in order to obtain an
execution:</para>
@@ -337,45 +327,44 @@
&lt;property name="jobRepository" ref="jobRepository" /&gt;
&lt;/bean&gt;</programlisting>
<para>Once a <link
linkend="jobExecution"><classname>JobExecution</classname></link> is
obtained, it is passed to the execute method of
<classname>Job</classname>, ultimately returning the
<classname>JobExecution</classname> to the caller:</para>
<para>Once a <link linkend="jobExecution">JobExecution</link> is obtained,
it is passed to the execute method of <classname>Job</classname>,
ultimately returning the <classname>JobExecution</classname> to the
caller:</para>
<mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/job-launcher-sequence-sync.png" scale="80"
fileref="images/job-launcher-sequence-sync.png" scale="90"
width="66%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-launcher-sequence-sync.png"
scale="80" width="66%" />
fileref="images/job-launcher-sequence-sync.png" scale="70"
width="66%" />
</imageobject>
</mediaobject>
<para>The sequence is straightforward and works well when launched from a
scheduler. However, issues arise when trying to launch from an HTTP
request. In this scenario, the launching needs to be done asynchronously
so that the <classname>SimpleJobLauncher</classname> returns immediately
to its caller. This is because it is not good practice to keep an HTTP
request open for the amount of time needed by long running processes such
as batch. An example sequence is below:</para>
<para>The sequence is straightforward, and works well when launched from a
scheduler, but causes issues when trying to launch from an HTTP request.
In this scenario, the launching needs to be done asynchronously, so that
the <classname>SimpleJobLauncher</classname> returns immediately to it's
caller. This is because it is not good practice to keep an HTTP request
open for the amount of time needed by long running processes such as
batch. An example sequence is below:</para>
<mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/job-launcher-sequence-async.png" scale="80"
fileref="images/job-launcher-sequence-async.png" scale="90"
width="66%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-launcher-sequence-async.png"
scale="80" width="66%" />
fileref="images/job-launcher-sequence-async.png" scale="70"
width="66%" />
</imageobject>
</mediaobject>
@@ -399,16 +388,16 @@
<section>
<title>Running a Job</title>
<para>At a minimum, launching a batch job requires two things: the
<classname>Job</classname> to be launched and a
<classname>JobLauncher</classname>. Both can be contained within the same
context or different contexts. For example, if launching a job from the
command line, a new JVM will be instantiated for each Job, and thus every
job will have its own <classname>JobLauncher</classname>. However, if
running from within a web container within the scope of an
<classname>HttpRequest</classname>, there will usually be one
<classname>JobLauncher</classname>, configured for asynchronous job
launching, that multiple requests will invoke to launch their jobs.</para>
<para>At a minimum, launching a batch job requires two things: the Job to
be launched and a <classname>JobLauncher</classname>. Both can be
contained within the same context or different contexts. For example, if
launching a job from the command line, a new JVM will be instantiated for
each Job, and thus every job will have it's own
<classname>JobLauncher</classname>. However, if running from within a web
container within the scope of an <classname>HttpRequest</classname>, there
will usually be one <classname>JobLauncher</classname>, configured for
asynchronous job launching, that multiple requests will invoke to launch
their jobs.</para>
<section>
<title>Running Jobs from the Command Line</title>
@@ -432,32 +421,29 @@
<classname>CommandLineJobRunner</classname>. It's important to note
that this is just one way to bootstrap your application, but there are
many ways to launch a Java process, and this class should in no way be
viewed as definitive. The <classname>CommandLineJobRunner</classname>
performs four tasks:</para>
viewed as definitive. It performs four tasks:</para>
<itemizedlist>
<listitem>
<para>Load the appropriate
<classname>ApplicationContext</classname></para>
<para>Loads the appropriate Application Context</para>
</listitem>
<listitem>
<para>Parse command line arguments into
<classname>JobParameters</classname></para>
<para>Parses command line arguments into JobParameters</para>
</listitem>
<listitem>
<para>Locate the appropriate job based on arguments</para>
<para>Locates the appropriate job based on arguments</para>
</listitem>
<listitem>
<para>Use the <classname>JobLauncher</classname> provided in the
application context to launch the job.</para>
<para>Uses the JobLauncher provided in the application context to
launch the job.</para>
</listitem>
</itemizedlist>
<para>All of these tasks are accomplished using only the arguments
passed in. The following are required arguments:</para>
<para>All of these tasks are accomplished based completely upon the
arguments passed in. The following are required arguments:</para>
<table>
<title>CommandLineJobRunner arguments</title>
@@ -493,21 +479,22 @@
This example is using the same 'EndOfDay' example from <xref
linkend="domain" />. The first argument is 'endOfDayJob.xml', which is
the Spring <classname>ApplicationContext</classname> containing the
<classname>Job</classname>. The second argument, 'endOfDay' represents
the job name. The final argument, 'schedule.date(date)=2008/01/01'
will be converted into <classname>JobParameters</classname>. An
example of the XML configuration is below:</para>
Job. The second argument, 'endOfDay' represents the job name. The
final argument, 'schedule.date=01-01-2008' will be converted into
<classname>JobParameters</classname>. An example of the XML
configuration is below:</para>
<programlisting> &lt;job id="endOfDay"&gt;
&lt;steps&gt;
&lt;step name="step1" parent="simpleStep" /&gt;
<programlisting> &lt;bean id="endOfDay"
class="org.springframework.batch.core.job.SimpleJob"&gt;
&lt;property name="steps"&gt;
&lt;bean id="step1" parent="simpleStep" /&gt;
&lt;!-- Step details removed for clarity --&gt;
&lt;/steps&gt;
&lt;/job&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;!-- Launcher details removed for clarity --&gt;
&lt;beans: bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher" /&gt;</programlisting>
&lt;bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher" /&gt;</programlisting>
<para>This example is overly simplistic, since there are many more
requirements to a run a batch job in Spring Batch in general, but it
@@ -520,23 +507,22 @@
<section>
<title>ExitCodes</title>
<para>When launching a batch job from the command-line, an enterprise
scheduler is often used. Most schedulers are fairly dumb and work only
at the process level. This means that they only know about some
<para>When launching a batch job from the command-line, it is often
from an enterprise scheduler. Most schedulers are fairly dumb, and
work only at the process level. Meaning, they only know about some
operating system process such as a shell script that they're invoking.
In this scenario, the only way to communicate back to the scheduler
about the success or failure of a job is through return codes. A
return code is a number that is returned to a scheduler by the process
that indicates the result of the run. In the simplest case: 0 is
success and 1 is failure. However, there may be more complex
scenarios: If job A returns 4 kick off job B, and if it returns 5 kick
off job C. This type of behavior is configured at the scheduler level,
but it is important that a processing framework such as Spring Batch
provide a way to return a numeric representation of the 'Exit Code'
for a particular batch job. In Spring Batch this is encapsulated
within an <classname>ExitStatus</classname>, which is covered in more
detail in Chapter 5. For the purposes of discussing exit codes, the
only important thing to know is that an
number is returned to a scheduler that is told how to interpret the
result. In the simple case: 0 is success and 1 is failure. However,
there may be scenarios such as: If job A returns 4 kick off job B, if
it returns 5 kick off job C. This type of behavior is configured at
the scheduler level, but it is important that a processing framework
such as Spring Batch provide a way to return a numeric representation
of of the 'Exit Code' for a particular batch job. In Spring Batch this
is encapsulated within an <classname>ExitStatus</classname>, which is
covered in more detail in Chapter 5. For the purposes of discussing
exit codes, the only important thing to know is that an
<classname>ExitStatus</classname> has an exit code property that is
set by the framework (or the developer) and is returned as part of the
<classname>JobExecution</classname> returned from the
@@ -568,7 +554,7 @@
it will be injected into the runner after the context is created. All
that needs to be done to provide your own
<classname>ExitCodeMapper</classname> is to declare the implementation
as a root level bean and ensure that it is part of the
as a root level bean, and ensure it's part of the
<classname>ApplicationContext</classname> that is loaded by the
runner.</para>
</section>
@@ -595,20 +581,19 @@
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-repository.png"
<imagedata align="center" fileref="images/job-repository.png"
scale="80" width="40%" />
</imageobject>
</mediaobject>
<para>A <classname>JobLauncher</classname> uses the
<classname>JobRepository</classname> to create new
<classname>JobExecution</classname> objects and run them.
<classname>JobExecution</classname> objects, and run them.
<classname>Job</classname> and <classname>Step</classname> implementations
later use the same <classname>JobRepository</classname> for basic updates
of the same executions during the running of a <classname>Job</classname>.
The basic operations suffice for simple scenarios, but in a large batch
environment with hundreds of batch jobs and complex scheduling
The basic operations suffice for simple scenarios. However, in a large
batch environment with hundreds of batch jobs and complex scheduling
requirements, more advanced access of the meta data is required:</para>
<mediaobject>
@@ -618,9 +603,8 @@
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-repository-advanced.png"
scale="80" width="65%" />
<imagedata align="center" fileref="images/job-repository-advanced.png"
scale="70" width="65%" />
</imageobject>
</mediaobject>
@@ -668,10 +652,9 @@
<para><link linkend="repositoryTablePrefix">Earlier in this
chapter</link>, it was mentioned that the table prefix of the
<classname>JobRepository</classname> can be modified to allow for
different versions or schemas. Because the
<classname>JobExplorer</classname> is working with the same tables, it
too needs the ability to set a prefix:</para>
JobRepository can be modified to allow for different versions or
schemas. Because the JobExplorer is working with the same tables, it too
needs the ability to set a prefix:</para>
<programlisting>
&lt;bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
@@ -756,43 +739,35 @@
self-explanatory, and more detailed explanations can be found on the
<ulink
url="http://static.springframework.org/spring-batch/apidocs/org/springframework/batch/core/launch/JobOperator.html">javadoc
of the interface</ulink>. However, the
<methodname>startNextInstance</methodname> method is worth noting. This
method will always start a new instance of a <classname>Job</classname>.
of the interface</ulink>. However, the 'startNextInstance' method is
worth noting. This method will always start a new instance of a Job.
This can be extremely useful if there are serious issues in a
<classname>JobExecution</classname> and the <classname>Job</classname>
<classname>Job</classname>Execution, and the <classname>Job</classname>
needs to be started over again from the beginning. Unlike
<classname>JobLauncher</classname> though, which requires a new
<classname>JobParameters</classname> object that will trigger a new
<classname>JobInstance</classname> if the parameters are different from
any previous set of parameters, the
<methodname>startNextInstance</methodname> method will use the
<classname>JobParametersIncrementer</classname> tied to the
<classname>Job</classname> to force the <classname>Job</classname> to a
new instance:</para>
<classname>JobParameters</classname> that will trigger a new JobInstance
if they are different than any previous one, the startNextInstance
method will use the JobParametersIncrementer tied to the Job to force
the <classname>Job</classname> to a new instance:</para>
<programlisting>
public interface JobParametersIncrementer {
JobParameters getNext(JobParameters parameters);
}
</programlisting>
<para>The contract of <classname>JobParametersIncrementer</classname> is
that, given a <link
linkend="jobParameters"><classname>JobParameters</classname></link>
object, it will return the 'next' <classname>JobParameters</classname>
object by incrementing any necessary values it may contain. This
strategy is useful because the framework has no way of knowing what
changes to the <classname>JobParameters</classname> make it the 'next'
instance. For example, if the only value in
<classname>JobParameters</classname> is a date, and the next instance
should be created, should that value be incremented by one day? Or one
week (if the job is weekly for instance)? The same can be said for any
numerical values that help to identify the <classname>Job</classname>,
as shown below:</para>
that, given a <link linkend="jobParameters">JobParameters</link>, it
will return the 'next' parameter by incrementing any values it may
contain. This strategy is useful because the framework has no way of
knowing what changes to the JobParameters make it the 'next' instance.
For example, if the only value in JobParameters is a date, and the next
instance should be created, should that value be incremented by one day?
Or one week? (if the job is weekly for instance) The same can be said
for any numerical values that help to identify the Job, as shown
below:</para>
<programlisting>
public class SampleIncrementer implements JobParametersIncrementer {
@@ -809,13 +784,12 @@
</programlisting>
<para>In this example, the value with a key of 'run.id' is used to
discriminate between <classname>JobInstances</classname>. If the
<classname>JobParameters</classname> passed in is null, it can be
assumed that the <classname>Job</classname> has never been run before
and thus its initial state can be returned. However, if not, the old
value is obtained, incremented by one, and returned. An incrementer can
be associated with <classname>Job</classname> via the 'incrementer'
attribute in the namespace:</para>
discriminate between JobInstances. If the JobParameters passed in is
null, it can be assumed that the Job has never been run before and thus
it's initial state can be returned. However, if not, the old value is
obtained, incremented by one, and returned. An incrementer can be
associated with Job via the 'incrementer' attribute in the
namespace:</para>
<programlisting>
&lt;job id="footballJob" <emphasis role="bold">incrementer="sampleIncrementer"</emphasis>&gt;
@@ -843,8 +817,8 @@
<para>The shutdown is not immediate, since there is no way to force
immediate shutdown, especially if the execution is currently in
developer code that the framework has no control over, such as a
business service. However, as soon as control is returned back to the
framework, it will set the status of the current
business service. What it does mean, is that as soon as control is
returned back to the framework, it will set the status of the current
<classname>StepExecution</classname> to
<classname>BatchStatus.STOPPED</classname>, save it, then do the same
for the <classname>JobExecution</classname> before finishing.</para>

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
respectively. <classname>ExecutionContext</classname> maps to both
BATCH_JOB_EXECUTION_CONTEXT and BATCH_STEP_EXECUTION_CONTEXT. The
<classname>JobRepository</classname> is responsible for saving and storing
each Java object into its correct table. The following appendix describes
each Java object into it's correct table. The following appendix describes
the meta-data tables in detail, along with many of the design decisions
that were made when creating them. When viewing the various table creation
statements below, it is important to realize that the data types used are
@@ -26,15 +26,14 @@
their relationships to one another:</para>
<mediaobject>
<imageobject role="fo">
<imageobject role="html">
<imagedata align="center" fileref="images/meta-data-erd.png"
width="45%" />
scale="80" width="" />
</imageobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/meta-data-erd.png"
width="45%" />
<imagedata align="center" fileref="images/meta-data-erd.png"
scale="80" width="45%" />
</imageobject>
</mediaobject>
@@ -49,7 +48,7 @@
to try and save the value, if the version number has change it will
throw <classname>OptimisticLockingFailureException</classname>,
indicating there has been an error with concurrent access. This check is
necessary since, even though different batch jobs may be running in
necessary, since even though different batch jobs may be running in
different machines, they are all using the same database tables.</para>
</section>
@@ -57,11 +56,11 @@
<title>Identity</title>
<para>BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, and BATCH_STEP_EXECUTION
each contain columns ending in _ID. These fields act as primary keys for
their respective tables. However, they are not database generated keys,
but rather they are generated by separate sequences. This is necessary
because after inserting one of the domain objects into the database, the
key it is given needs to be set on the actual object so that they can be
each contain columns ending in _ID, which act as primary keys for their
respective tables. However, they are not database generated keys, but
rather are generated by separate sequences. This is necessary because
after inserting one of the domain objects into the database, the key it
is given needs to be set on the actual object, so that they can be
uniquely identified in Java. Newer database drivers (Jdbc 3.0 and up)
support this feature with database generated keys, but rather than
requiring it, sequences were used. Each variation of the schema will
@@ -431,8 +430,8 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
<para>The BATCH_JOB_EXECUTION_CONTEXT table holds all information relevant
to an <classname>Job</classname>'s
<classname>ExecutionContext</classname>. There is exactly one
<classname>Job</classname> <classname>ExecutionContext</classname> per
<classname>JobExecution</classname>, and it contains all of the job-level
<classname>ExecutionContext</classname> per
<classname>StepExecution</classname>, and it contains all of the job-level
data that is needed for a particular job execution. This data typically
represents the state that must be retrieved after a failure so that a
<classname>JobInstance</classname> can 'start from where it left

View File

@@ -173,14 +173,13 @@
architecture that supports the extensibility and ease of use for end-user
developers. <mediaobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/spring-batch-layers.png"
format="PNG" width="338" />
<imagedata align="center" fileref="images/spring-batch-layers.png"
format="PNG" scale="80" width="35%" />
</imageobject>
<imageobject role="html">
<imagedata align="center" fileref="images/spring-batch-layers.png"
format="PNG" scalefit="" width="35%" />
format="PNG" scale="80" scalefit="" width="35%" />
</imageobject>
<caption><para>Figure 1.1: Spring Batch Layered

View File

@@ -23,16 +23,15 @@
</imageobject>
<imageobject role="fo">
<imagedata align="center" contentwidth="480"
fileref="images/step.png"
scale="60" width="75%" />
<imagedata align="center" contentwidth="480" fileref="images/step.png"
scale="50" width="75%" />
</imageobject>
</mediaobject>
<section id="chunkOrientedProcessing">
<title>Chunk-Oriented Processing</title>
<para>Spring Batch uses a 'Chunk Oriented' processing style within its
<para>Spring Batch uses a 'Chunk Oriented' processing style within it's
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
@@ -44,13 +43,13 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/chunk-oriented-processing.png" scale="75"
fileref="images/chunk-oriented-processing.png" scale="95"
width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/chunk-oriented-processing.png"
fileref="images/chunk-oriented-processing.png" scale="85"
width="75%" />
</imageobject>
</mediaobject>
@@ -61,8 +60,7 @@
<programlisting>
List items = new Arraylist();
for(int i = 0; i &lt; commitInterval; i++){
Object item = itemReader.read()
Object processedItem = itemProcessor.process(item);
Object processedItem = itemProcessor.process(itemReader.read());
items.add(processedItem);
}
itemWriter.write(items);
@@ -121,9 +119,8 @@
<para>It should be noted that, job-repository defaults to
"jobRepository" and transaction-manager defaults to "transactionManger".
Furthermore, the <classname>ItemProcessor</classname> is optional, not
required, since the item could be directly passed from the reader to the
writer.</para>
Furthermore, the ItemProcessor is not required, since the item could be
directly passed from the reader to the writer.</para>
</section>
<section>
@@ -132,14 +129,13 @@
<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 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>
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>
<programlisting>
&lt;job id="sampleJob"&gt;
@@ -171,15 +167,14 @@
<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. 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>
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>
<programlisting>
&lt;step name="step1"&gt;
@@ -327,7 +322,7 @@
<listitem>
<para>playerSummarization is not start, and the job is immediately
killed, since this is the third execution of playerSummarization,
and its limit is only 2. The limit must either be raised, or the
and it's 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>
@@ -365,15 +360,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 skip limit. In other words, the skip limit is only incremented on
writes (regardless of success or failure).</para>
the commit interval. In other words, the commit interval 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 behavior. However, in other scenarios it may be easier to
correct behaviour, however, in certain scenarios it may be easier to
identify which exceptions should cause failure and skip everything
else:<programlisting>
&lt;step name="step1"&gt;
@@ -402,8 +397,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
@@ -442,8 +437,7 @@
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 that exception should not cause
rollback.</para>
symbol will indicate that exception should not cause rollback.</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -455,8 +449,8 @@
</programlisting>
<para>Transaction attributes can be used to control multiple other
settings such as isolation and propagation behavior. More information on
setting transaction attributes can be found in the spring core
settings such as isolation and propagation behaviour. More information
on setting transaction attributes can be found in the spring core
documentation.</para>
<section id="transactionalReaders">
@@ -469,8 +463,8 @@
top of a transactional resource, such as a JMS queue. In this case,
since the queue is tied to the transaction that is rolled back, the
messages that have been pulled from the queue will be put back on. For
this reason, the step can be configured to not buffer the
items:</para>
this reason, the step can be configured to not buffer the items:
</para>
<programlisting>
&lt;step name="step1"&gt;
@@ -488,21 +482,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 <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>
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>
<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
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>
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>
<programlisting>
&lt;step name="step1"&gt;
@@ -531,11 +525,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 be explicitly
registered as a stream because it is a direct property of the
<classname>ItemReader</classname> does not need to 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 the event
of a failure.</para>
state of the reader and writer will be correctly persisted in case of a
failure.</para>
</section>
<section>
@@ -565,7 +559,7 @@
</programlisting>
<para>In addition to the <classname>StepListener</classname> interfaces,
annotations are provided to address the same concerns.</para>
annotations are provided address the same concerns.</para>
<section>
<title>StepExecutionListener</title>
@@ -768,10 +762,9 @@
<section>
<title>SkipListener</title>
<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
<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
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
@@ -782,9 +775,9 @@
void onSkipInRead(Throwable t);
void onSkipInProcess(T item, Throwable t);
void onSkipInWrite(S item, Throwable t);
void onSkipInProcess(T item, Throwable t);
}
</programlisting>
@@ -820,8 +813,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 transaction may be rolled back,
Spring Batch makes two guarantees:</para>
many cases in which the original trasaction may be rolledback,
Spring Batch makes two garantees:</para>
<orderedlist>
<listitem>
@@ -869,8 +862,8 @@
</programlisting>
<note>
<para><classname>TaskletStep</classname> will automatically register the
tasklet as <classname>StepListener</classname> if it implements this
<para>TaskletStep will automatically register the tasklet as
<classname>StepExecutionListener</classname> if it implements this
interface</para>
</note>
@@ -902,7 +895,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
@@ -958,6 +951,27 @@
&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
</section>
<section>
<title>Executing System Commands</title>
<para>Many batch jobs may require that an external command be called
from within the batch job. Such a process could be kicked off separately
by the scheduler, but the advantage of common meta-data about the run
would be lost. Furthermore, a multi-step job would also need to be split
up into multiple jobs as well. Because the need is so common, Spring
Batch provides a <classname>Tasklet</classname> implementation for
calling system commands:</para>
<programlisting>
&lt;bean class="org.springframework.batch.sample.tasklet.SystemCommandTasklet"&gt;
&lt;property name="command" value="echo hello" /&gt;
&lt;!-- 5 second timeout for the command to complete --&gt;
&lt;property name="timeout" value="5000" /&gt;
&lt;/bean&gt;
</programlisting>
</section>
</section>
@@ -965,11 +979,11 @@
<section id="controllingStepFlow">
<title>Controlling Step Flow</title>
<para>With the ability to group steps together within an owning job comes
<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. Furthermore, there
may be more than one type of 'success' which determines which
mean that the <classname>Job</classname> should fail. Further, 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>
@@ -987,14 +1001,13 @@
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/sequential-flow.png"
width="40%" />
<imagedata align="center" fileref="images/sequential-flow.png"
scale="80" width="40%" />
</imageobject>
</mediaobject>
<para>This can be achieved using the 'next' attribute of the step
element:</para>
<para>This can be achieved using the 'next' attribute of
<classname>Step</classname>:</para>
<para><programlisting>
&lt;job id="job"&gt;
@@ -1003,85 +1016,47 @@
&lt;step name="stepC" /&gt;
&lt;/job&gt;
</programlisting>In the scenario above, 'step A' will execute first because it
is the first <classname>Step</classname> listed. If 'step A' completes
normally, then 'step B' will execute, and so on. However, if 'step A'
fails, then the entire <classname>Job</classname> will fail and 'step B'
will not execute.</para>
<note>
<para>With the Spring Batch namespace, the first step listed in the
configuration will <emphasis>always</emphasis> be the first step
executed by the <classname>Job</classname>. The order of the other
step elements does not matter, but the first step must always appear
first in the xml.</para>
</note>
</programlisting>In the scenario above, 'step A' will execute first. If 'step
A' completes normally, then 'step B' will execute and so on. However, if
'step A' fails, then the entire <classname>Job</classname> will fail and
'step B' will not execute.</para>
</section>
<section>
<title>Conditional Flow</title>
<para>In the example above, there are only two possibilities:</para>
<para>In the example above, there's only two possibilities:</para>
<orderedlist>
<listitem>
<para>The <classname>Step</classname> is successful and the next
<classname>Step</classname> should be executed.</para>
<para>The Step is successful and the next Step should be
executed</para>
</listitem>
<listitem>
<para>The <classname>Step</classname> failed and thus the
<classname>Job</classname> should fail.</para>
<para>The Step failed and thus the Job should fail.</para>
</listitem>
</orderedlist>
<para>In many cases, this may be sufficient. However, what about a
scenario in which the failure of a <classname>Step</classname> should
trigger a different <classname>Step</classname>, rather than causing
failure? <mediaobject>
<para>In many cases this may be sufficient. However, what about a
scenario in which the failure of a Step should trigger a different Step,
rather than causing failure? <mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/conditional-flow.png"
scale="" width="40%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/conditional-flow.png"
width="40%" />
<imagedata align="center" fileref="images/conditional-flow.png"
scale="80" width="40%" />
</imageobject>
</mediaobject></para>
<para id="nextElement">In order to handle more complex scenarios, the
Spring Batch namespace allows transition elements to be defined within
the step element. One such transition is the "next" element. Like the
"next" attribute, the "next" element will tell the
<classname>Job</classname> which <classname>Step</classname> to execute
next. However, unlike the attribute, any number of "next" elements are
allowed on a given <classname>Step</classname>, and there is no default
behavior the the case of failure. This means that if transition elements
are used, then all of the behavior for the <classname>Step</classname>'s
transitions must be defined explicitly. Note also that a single step
cannot have both a "next" attribute and a transtion element.</para>
<para>The next element specifies a pattern to match and the step to
execute next:</para>
<para><programlisting>
&lt;job id="job"&gt;
&lt;step name="stepA"&gt;
&lt;next on="FAILED" to="stepB" /&gt;
&lt;next on="*" to="stepC" /&gt;
&lt;/step&gt;
&lt;step name="stepB" next="stepC" /&gt;
&lt;step name="stepC" /&gt;
&lt;/job&gt;
</programlisting></para>
<para>The "on" attribute of a transition element uses a simple
pattern-matching scheme to match the <classname>ExitStatus</classname>
that results from the exeution of the <classname>Step</classname>. Only
two special characters are allowed in the pattern:</para>
<para>In order to handle this scenario, the next step can be determined
based on the result of the step by adding a next element to the Step.
The "on" attribute uses a simple pattern-matching scheme to match the
exit code of the Step to the various next elements declared. Only two
special characters are allowed:</para>
<itemizedlist>
<listitem>
@@ -1096,17 +1071,27 @@
<para>For example, "c*t" will match "cat" and "count", while "c?t" will
match "cat" but not "count".</para>
<para>While there is no limit to the number of transition elements on a
<classname>Step</classname>, if the <classname>Step</classname>'s
execution results in an <classname>ExitStatus</classname> that is not
covered by an element, then the framework will throw an exception and
the <classname>Job</classname> will fail. It is important to note that
the framework will automatically order transitions from most specific to
least specific. This means that even if the elements were swapped for
"stepA" in the example above, an <classname>ExitStatus</classname> of
"FAILED" would still go to "stepB".</para>
<para>Any number of "next" elements is allowed, but if the step has an
exit code that is not covered by a "next" element, then the framework
will throw an exception and the job will fail. It is important to note
that the framework will automatically order transitions from most
specific to least specific. So even if the "next" elements were swapped
for "stepA" below, an exit status of "FAILED" would still go to
"stepB".</para>
<section id="batchStatusVsExitStatus" label="">
<para><programlisting>
&lt;job id="job"&gt;
&lt;step name="stepA"&gt;
&lt;next on="FAILED" to="stepB" /&gt;
&lt;next on="*" to="stepC" /&gt;
&lt;/step&gt;
&lt;step name="stepB" next="stepC" /&gt;
&lt;step name="stepC" /&gt;
&lt;/job&gt;
</programlisting></para>
<section>
<title>Batch Status vs. Exit Status</title>
<para>When configuring a <classname>Job</classname> for conditional
@@ -1115,11 +1100,11 @@
<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.
Most of them are self explanatory: COMPLETED is the status set when a
Most of them are self explanatory, COMPLETED is the status set when a
step or job has completed successfully, FAILED is set when it fails,
and so on. The example above contains the following 'next'
element:</para>
@@ -1131,18 +1116,18 @@
<para>At first glance, it would appear that the 'on' attribute
references the <classname>BatchStatus</classname> of the
<classname>Step</classname> to which it belongs. However, it actually
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 exit code 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>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;
@@ -1157,189 +1142,83 @@
<orderedlist>
<listitem>
<para>The <classname>Step</classname> failed, in which case the
job should fail.</para>
<para>The step failed, in which case the job should fail.</para>
</listitem>
<listitem>
<para>The <classname>Step</classname> completed
successfully.</para>
<para>The Step completed successfully.</para>
</listitem>
<listitem>
<para>The <classname>Step</classname> completed successfully, but
with an exit code of 'COMPLETED WITH SKIPS'. In this case, a
different step should be run to handle the errors.</para>
<para>The Step completed successfully, but with an exit code of
'COMPLETED WITH SKIPS'. In this case, a different step should be
run to handle the errors.</para>
</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>
<programlisting> public class SkipCheckingListener extends StepExecutionListenerSupport {
<programlisting>public class SkipCheckingListener implements StepExecutionListener {
public ExitStatus afterStep(StepExecution stepExecution) {
if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
&amp;&amp; stepExecution.getSkipCount() &gt; 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
} else {
return null;
}
public ExitStatus afterStep(StepExecution stepExecution) {
if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
&amp;&amp; stepExecution.getSkipCount() &gt; 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
} else {
return null;
}
}
}</programlisting>
...
}</programlisting>
<para>The above code is a <classname>StepExecutionListener</classname>
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 <classname>ExitStatus</classname> with an
exit code of "COMPLETED WITH SKIPS" is returned.</para>
conditions are met, a new ExitStatus with an exit code of "COMPLETED
WITH SKIPS" is returned.</para>
</section>
</section>
<section>
<title>Configuring for Stop</title>
<para>After the discussion of <link
linkend="batchStatusVsExitStatus"><classname>BatchStatus</classname> and
<classname>ExitStatus</classname></link>, one might wonder how the
<classname>BatchStatus</classname> and <classname>ExitStatus</classname>
are determined for the <classname>Job</classname>. While these statuses
are determined for the <classname>Step</classname> by the code that is
executed, the statuses for the <classname>Job</classname> will be
determined based on the configuration.</para>
<para>If it is desired that the batch job stop under certain conditions,
then either the "stop" tag or the "end" tag may be used.</para>
<para>So far, all of the job configurations discussed have had at least
one final <classname>Step</classname> with no transitions. For example,
after the following step executes, the <classname>Job</classname> will
end:</para>
<para>The "stop" tag indicates the job should stop processing with an
exit status of "STOPPED". The "to" attribute tells the framework which
step should be first when the job is subsequently restarted. This
mechanism allows the job to pause temporarily.</para>
<para><programlisting> &lt;step name="stepC" /&gt;</programlisting></para>
<para>On the other hand, the "end" tag will stop the job but does not
allow for a "to" attribute. The "status" attribute is optional. It will
determine the exit status of the step if the flow ends at that location.
The only legal values for the "status" are "COMPLETED", "FAILED", and
"STOPPED". If no status is specified, then the default is
"COMPLETED".</para>
<para>If no transitions are defined for a <classname>Step</classname>,
then the <classname>Job</classname>'s statuses will be defined as
follows:</para>
<itemizedlist>
<listitem>
<para>If the <classname>Step</classname> ends with
<classname>ExitStatus</classname> FAILED, then the
<classname>Job</classname>'s <classname>BatchStatus</classname> and
<classname>ExitStatus</classname> will both be FAILED.</para>
</listitem>
<listitem>
<para>Otherwise, the the <classname>Job</classname>'s
<classname>BatchStatus</classname> and
<classname>ExitStatus</classname> will both be COMPLETED.</para>
</listitem>
</itemizedlist>
<para>While this method of terminating a batch job is sufficient for
some batch jobs, such as a simple sequential step job, custom defined
job-stopping scenarios may be required. For this purpose, Spring Batch
provides three transition elements to stop a <classname>Job</classname>
(in addition to the <link linkend="nextElement">"next" element</link>
that we discussed previously). Each of these stopping elements will stop
a <classname>Job</classname> with a particular
<classname>BatchStatus</classname>. It is important to note that the
stop transition elements will have no effect on either the
<classname>BatchStatus</classname> or <classname>ExitStatus</classname>
of any <classname>Step</classname>s in the <classname>Job</classname>:
these elements will only affect the final statuses of the
<classname>Job</classname>. For example, it is possible for every step
in a job to have a status of FAILED but the job to have a status of
COMPLETED, or vise versa.</para>
<section>
<title>The 'End' Element</title>
<para>The 'end' element instructs a <classname>Job</classname> to stop
with a <classname>BatchStatus</classname> of COMPLETED. A
<classname>Job</classname> that has finished with status COMPLETED
cannot be restarted (the framework will throw a
<classname>JobInstanceAlreadyCompleteException</classname>). The 'end'
element also allows for an optional 'status' attribute that can be
used to customize the <classname>ExitStatus</classname> of the
<classname>Job</classname>. If no 'status' attribute is given, then
the <classname>ExitStatus</classname> will be "COMPLETED" by default,
to match the <classname>BatchStatus</classname>.</para>
<para>In the following scenario, if step2 fails, then the
<classname>Job</classname> will stop with a
<classname>BatchStatus</classname> of COMPLETE and an
<classname>ExitStatus</classname> of "COMPLETED" and step3 will not
execute; otherwise, execution will move to step3. Additionally, if
step2 fails, the <classname>Job</classname> will not be
restartable.</para>
<programlisting> &lt;step name="step1" next="step2"&gt;
<para><programlisting>
&lt;step name="step1"&gt;
&lt;stop on="COMPLETED" to="step2"/&gt;
&lt;/step&gt;
&lt;step name="step2"&gt;
&lt;end on="FAILED"/&gt;
&lt;next on="*" to="step3"/&gt;
&lt;next on="FOO" to="step3"/&gt;
&lt;end on="*" status="FAILED"/&gt;
&lt;/step&gt;
&lt;step name="step3"&gt;</programlisting>
</section>
&lt;step name="step3" /&gt;
<section>
<title>The 'Fail' Element</title>
<para>The 'fail' element instructs a <classname>Job</classname> to
stop with a <classname>BatchStatus</classname> of FAILED. Unlike the
'end' element, the 'fail' element will not prevent the
<classname>Job</classname> from being restarted. The 'fail' element
also allows for an optional 'status' attribute that can be used to
customize the <classname>ExitStatus</classname> of the
<classname>Job</classname>. If no 'status' attribute is given, then
the <classname>ExitStatus</classname> will be "FAILED" by default, to
match the <classname>BatchStatus</classname>.</para>
<para>In the following scenario, if step2 fails, then the
<classname>Job</classname> will stop with a
<classname>BatchStatus</classname> of FAILED and an
<classname>ExitStatus</classname> of "EARLY TERMINATION" and step3
will not execute; otherwise, execution will move to step3.
Additionally, if step2 fails, and the <classname>Job</classname> is
restarted, then execution will begin again on step2.</para>
<programlisting> &lt;step name="step1" next="step2"&gt;
&lt;step name="step2"&gt;
&lt;fail on="FAILED" status="EARLY TERMINATION"/&gt;
&lt;next on="*" to="step3"/&gt;
&lt;/step&gt;
&lt;step name="step3"&gt;</programlisting>
</section>
<section>
<title>The 'Pause' Element</title>
<para>The 'pause' element instructs a <classname>Job</classname> to
stop with a <classname>BatchStatus</classname> of STOPPED. Pausing a
<classname>Job</classname> is a meant to be a temporary break in
processing so that the operator can take some action before restarting
the <classname>Job</classname>. The 'pause' element requires a 'to'
attribute that specifies the step where execution should pick up once
the <classname>Job</classname>.</para>
<para>In the following scenario, if step1 finsihes with COMPLETE, then
the job will then stop. Once it is restarted, execution will begin on
step2.</para>
<para><programlisting> &lt;step name="step1"&gt;
&lt;pause on="COMPLETED" to="step2"/&gt;
&lt;/step&gt;
&lt;step name="step2"/&gt;</programlisting></para>
</section>
</programlisting></para>
</section>
<section>
<title>Programmatic flow decisions</title>
<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
<para>In some situations, more information than the exit status 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>
@@ -1381,43 +1260,16 @@
</section>
<section>
<title>Split Flows</title>
<title>Late binding of Job and Step Attributes</title>
<para>Every scenario described so far has involved a
<classname>Job</classname> that executes its
<classname>Step</classname>s one at a time in a linear fashion. In
addition to this typical style, the Spring Batch namespace also allows
for a job to be configured with parallel flows using the 'split'
element. As is seen below, the 'split' element contains one or more
'flow' elements, where entire separate flows can be defined. A 'split'
element may also contain any of the previously discussed transition
elements such as the 'next' attribute or the 'next', 'end', 'fail', or
'pause' elements. </para>
<para>Both the XML and Flat File examples above use the Spring
<classname>Resource</classname> abstraction to obtain a file . This
works because <classname>Resource</classname> has a
<markup>getFile</markup> method, which returns a
<classname>java.io.File</classname>. Both XML and Flat File resources
can be configured using standard Spring constructs:</para>
<programlisting> &lt;split id="split1" next="step4"&gt;
&lt;flow&gt;
&lt;step name="step1" next="step2"/&gt;
&lt;step name="step2"/&gt;
&lt;/flow&gt;
&lt;flow&gt;
&lt;step name="step3"/&gt;
&lt;/flow&gt;
&lt;/split&gt;
&lt;step name="step4"/&gt;</programlisting>
</section>
</section>
<section>
<title>Late binding of Job and Step Attributes</title>
<para>Both the XML and Flat File examples above use the Spring
<classname>Resource</classname> abstraction to obtain a file . This works
because <classname>Resource</classname> has a <markup>getFile</markup>
method, which returns a <classname>java.io.File</classname>. Both XML and
Flat File resources can be configured using standard Spring
constructs:</para>
<programlisting>
<programlisting>
&lt;bean id="flatFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource"
@@ -1426,15 +1278,15 @@
</programlisting>
<para>The above <classname>Resource</classname> will load the file from
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>
<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
determined at runtime as a parameter to the job. This could be solved
using '-D' parameters, i.e. a system property:</para>
<programlisting>
<programlisting>
&lt;bean id="flatFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="${input.file.name}" /&gt;
@@ -1442,21 +1294,20 @@
</programlisting>
<para>All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that although
a <classname>PropertyPlaceholderConfigurer</classname> can be used here,
it is not necessary if the system property is always set because the
<classname>ResourceEditor</classname> in Spring already filters and does
placeholder replacement on system properties.)</para>
<para>All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that
although a <classname>PropertyPlaceholderConfigurer</classname> can be
used here, it is not necessary if the system property is always set
because the <classname>ResourceEditor</classname> in Spring already
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"><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>
<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>
<programlisting>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobParameters[input.file.name]}</emphasis>" /&gt;
@@ -1464,34 +1315,34 @@
</programlisting>
<para>Both the <classname>JobExecution</classname> and
<classname>StepExecution</classname> level
<classname>ExecutionContext</classname> can be accessed in the same
way:</para>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;/bean&gt;
</programlisting>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext[input.file.name]}</emphasis>" /&gt;
&lt;/bean&gt;
</programlisting>
<section>
<title>Step Scope</title>
<para>All of the late binding examples from above have a scope of "step"
declared on the bean definition:</para>
<para>Both the <classname>JobExecution</classname> and
<classname>StepExecution</classname> level
<classname>ExecutionContext</classname> can be accessed in the same
way:</para>
<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;/bean&gt;
</programlisting>
<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;/bean&gt;
</programlisting>
<section>
<title>Step Scope</title>
<para>All of the late binding examples from above have a scope of
"step" declared on the bean definition:</para>
<programlisting>
&lt;bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{jobParameters[input.file.name]}" /&gt;
@@ -1499,16 +1350,17 @@
</programlisting>
<para>Using a scope of <classname>Step</classname> is required in 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>
<para>Using a scope of <classname>Step</classname> is required in
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>
<programlisting>
<programlisting>
&lt;bean class="org.springframework.batch.core.scope.StepScope" /&gt;
</programlisting>
</section>
</section>
</section>
</chapter>

View File

@@ -35,13 +35,13 @@
<section>
<title id="s.2.1.6">Java 5</title>
<para>The 1.x releases of Spring Batch were all based on Java 1.4. This
<para>The 1.x release of Spring Batch were all based on Java 1.4. This
prevented the framework from using many enhancements provided in Java 5
such as generics, parameterized types, etc. The entire framework has been
updated to utilize these features. As a result, <emphasis role="bold">Java
1.4 is no longer supported.</emphasis> Most of the interfaces developers
work with have been updated to support generic types. As an example, the
<classname>ItemReader</classname> interface from 1.1 is below:</para>
updated to utilize these features. <emphasis role="bold">Java 1.4 is no
longer supported.</emphasis> Most of the interfaces developers work with
have been updated to support generic types. As an example, the ItemReader
interface from 1.1 is below:</para>
<programlisting>
public interface ItemReader {
@@ -56,7 +56,7 @@
</programlisting>
<para>As you can see, the <methodname>read</methodname> method returns an
<classname>Object</classname>. The 2.0 version is below:</para>
Object. The 2.0 version is below:</para>
<programlisting>
public interface ItemReader&lt;T&gt; {
@@ -69,9 +69,9 @@
<para>As you can see, <classname>ItemReader</classname> now supports the
generic type, T, which is returned from read. You may also notice that
<methodname>mark</methodname> and <methodname>reset</methodname> have been
removed. This is due to step processing strategy changes, which are
discussed below. Many other interfaces have been similarly updated.</para>
mark and reset have been removed. This is due to step processing strategy
changes, which are discussed below. Many other interfaces have been
similarly updated. </para>
</section>
<section>
@@ -83,24 +83,22 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center"
fileref="images/item-oriented-processing.png" scale=""
width="50%" />
fileref="images/item-oriented-processing.png" scale="90"
width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/item-oriented-processing.png"
fileref="images/item-oriented-processing.png" scale="90"
width="50%" />
</imageobject>
</mediaobject>
<para>In item-oriented processing, the <classname>ItemReader</classname>
returns one <classname>Object</classname> (the 'item') which is then
handed to the <classname>ItemWriter</classname>, periodically committing
<para>In item-oriented processing, the ItemReader returns one Object (the
'item) which is then handed to the ItemWriter, periodically committing
when the number of items hits the commit interval. For example, if the
commit interval is 5, <classname>ItemReader</classname> and
<classname>ItemWriter</classname> will each be called 5 times. This is
illustrated in a simplified code example below:</para>
commit interval is 5, ItemReader and ItemWriter will each be called 5
times. This is illustrated in a simplified code example below:</para>
<programlisting>
for(int i = 0; i &lt; commitInterval; i++){
@@ -110,9 +108,8 @@
</programlisting>
<para>Both the <classname>ItemReader</classname> and
<classname>ItemWriter</classname> interfaces were completely geared toward
this approach:</para>
<para>Both the ItemReader and ItemWriter interfaces were completely geared
toward this approach:</para>
<programlisting>
public interface ItemReader {
@@ -138,18 +135,14 @@
</programlisting>
<para>Because the 'scope' of the processing was one item, supporting
rollback scenarios required additional methods, which is what
<methodname>mark</methodname>, <methodname>reset</methodname>,
<methodname>flush</methodname>, and <methodname>clear</methodname>
provided. If, after successfully reading and writing 2 items, the third
has an error while writing, the transaction would need to be rolled back.
In this case, the <methodname>clear</methodname> method on the writer
would be called, indicating that it should <methodname>clear</methodname>
its buffer, and <methodname>reset</methodname> would be called on the
<classname>ItemReader</classname>, indicating that it should return back
to the last position it was at when <methodname>mark</methodname> was
called. (Both <methodname>mark</methodname> and
<methodname>flush</methodname> are called on commit)</para>
rollback scenarios required additional methods, which is what mark, reset,
flush, and clear provided. If, after successfully reading and writing 2
items, the third had an error while writing, the transaction would need to
be rolled back. In this case, the clear method on the writer would be
called, indicating that it should clear its buffer, and reset would be
called on the ItemReader, indicating that it should return back to the
last position it was at when mark was called. (Both mark and flush are
called on commit)</para>
<para>In 2.0, this strategy has been changed to a chunk-oriented
approach:</para>
@@ -158,13 +151,13 @@
<imageobject role="html">
<imagedata align="center"
fileref="images/simplified-chunk-oriented-processing.png"
scale="" width="60%" />
scale="90" width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/simplified-chunk-oriented-processing.png"
width="60%" />
scale="90" width="60%" />
</imageobject>
</mediaobject>
@@ -183,8 +176,7 @@
</programlisting>
<para>This approach not only allows for much simpler processing and
scalability approaches, it also makes the
<classname>ItemReader</classname> and <classname>ItemWriter</classname>
scalability approaches, it also makes the ItemReader and ItemWriter
interfaces much cleaner:</para>
<programlisting>
@@ -205,39 +197,35 @@
</programlisting>
<para>As you can see, the interfaces no longer contain the
<methodname>mark</methodname>, <methodname>reset</methodname>,
<methodname>flush</methodname>, and <methodname>clear</methodname>
methods. This makes the creation of readers and writers much more
straightforward for developers. In the case of
<para>As you can see, the interfaces no longer contain the mark, reset,
flush, and clear methods. This makes the creation of readers and writers
much more straightforward for developers. In the case of
<classname>ItemReader</classname>, the interface is now forward-only. The
framework will buffer read items for developers in the case of rollback
(though there are exceptions if the underlying resource is transactional
see: <xref linkend="transactionalReaders" />).
<classname>ItemWriter</classname> is also simplified, since it gets the
entire 'chunk' of items at once, rather than one at a time, it can decide
to flush any resources (such as a file or hibernate session) before
returning control to the <classname>Step</classname>. More detailed
information on chunk-oriented processing can be found in <xref
framework will buffer read items for developers in the case of rollback.
(There are exceptions if the underlying resource is transactional see:
<xref linkend="transactionalReaders" />) ItemWriter is also simplified,
since it gets the entire 'chunk' of items at once, rather than one at a
time, it can decide to flush any resources (such as a file or hibernate
session) before returning control to the <classname>Step</classname>. More
detailed information on chunk-oriented processing can be found in <xref
linkend="chunkOrientedProcessing" />. Reader and writer implementation
information can be found in <xref linkend="readersAndWriters" />.</para>
information can be found in 80<xref linkend="readersAndWriters" /></para>
<section>
<title>ItemProcessor</title>
<para>Previously, <classname>Step</classname>s had only two
dependencies, <classname>ItemReader</classname> and
<para>Previously, Steps had only two dependencies,
<classname>ItemReader</classname> and
<classname>ItemWriter</classname>:</para>
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/1-1-step.png" scale=""
<imagedata align="center" fileref="images/1-1-step.png" scale="80"
width="50%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/1-1-step.png"
<imagedata align="center" fileref="images/1-1-step.png" scale="80"
width="50%" />
</imageobject>
</mediaobject>
@@ -253,21 +241,18 @@
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/composite-transformer.png"
width="70%" />
<imagedata align="center" fileref="images/composite-transformer.png"
scale="65" width="" />
</imageobject>
</mediaobject>
<para>This approach works. However, it requires an extra layer between
<para>This approach works, however, it requires an extra layer between
either the reader or the writer and the <classname>Step</classname>.
Furthermore, the <classname>ItemWriter</classname> would need to be
registered separately as an <classname>ItemStream</classname> with the
<classname>Step</classname>. For this reason, the
<classname>ItemTransfomer</classname> was renamed to
<classname>ItemProcessor</classname> and moved up to the same level as
<classname>ItemReader</classname> and
<classname>ItemWriter</classname>:</para>
<classname>Step</classname>. For this reason, the ItemTransfomer was
renamed to ItemProcessor and moved up to the same level as ItemReader
and ItemWriter:</para>
<mediaobject>
<imageobject role="html">
@@ -277,8 +262,7 @@
<imageobject role="fo">
<imagedata align="center" contentwidth="480"
fileref="images/step.png"
scale="" width="50%" />
fileref="images/step.png" scale="60" width="" />
</imageobject>
</mediaobject>
</section>
@@ -328,21 +312,20 @@
<title>Meta Data access improvements</title>
<para>The <classname>JobRepository</classname> interface represents basic
CRUD operations with <classname>Job</classname> meta-data. However, it may
also be useful to query the meta-data. For that reason, the
CRUD operations with <classname>Job</classname> meta-data. However, it can
be useful to query the meta-data. For that reason, the
<classname>JobExplorer</classname> and <classname>JobOperator</classname>
interfaces have been created:</para>
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/job-repository-advanced.png"
scale="" width="65%" />
scale="90" width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/job-repository-advanced.png"
scale="80" width="65%" />
<imagedata align="center" fileref="images/job-repository-advanced.png"
scale="70" width="" />
</imageobject>
</mediaobject>
@@ -362,13 +345,12 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/sequential-flow.png"
scale="" width="40%" />
scale="" width="" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/sequential-flow.png"
width="40%" />
<imagedata align="center" fileref="images/sequential-flow.png"
scale="80" width="" />
</imageobject>
</mediaobject>
@@ -377,13 +359,12 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/conditional-flow.png"
scale="" width="40%" />
scale="80" width="40%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="images/conditional-flow.png"
width="40%" />
<imagedata align="center" fileref="images/conditional-flow.png"
scale="80" width="40%" />
</imageobject>
</mediaobject>
@@ -403,6 +384,6 @@
</programlisting>
<para>More details on how to configure non sequential steps can be found
in <xref linkend="controllingStepFlow" />.</para>
in <xref linkend="controllingStepFlow" /></para>
</section>
</chapter>