BATCH-1063: Fixed image sizing in both pdf and html
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
<job id="footballJob">
|
||||
@@ -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>
|
||||
<job id="footballJob" <emphasis role="bold">restartable="false"</emphasis>>
|
||||
@@ -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>
|
||||
<job id="footballJob">
|
||||
@@ -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> <bean id="jobRepository"
|
||||
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" /></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 @@
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean></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> <job id="endOfDay">
|
||||
<steps>
|
||||
<step name="step1" parent="simpleStep" />
|
||||
<programlisting> <bean id="endOfDay"
|
||||
class="org.springframework.batch.core.job.SimpleJob">
|
||||
<property name="steps">
|
||||
<bean id="step1" parent="simpleStep" />
|
||||
<!-- Step details removed for clarity -->
|
||||
</steps>
|
||||
</job>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Launcher details removed for clarity -->
|
||||
<beans: bean id="jobLauncher"
|
||||
class="org.springframework.batch.core.launch.support.SimpleJobLauncher" /></programlisting>
|
||||
<bean id="jobLauncher"
|
||||
class="org.springframework.batch.core.launch.support.SimpleJobLauncher" /></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>
|
||||
<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>
|
||||
<job id="footballJob" <emphasis role="bold">incrementer="sampleIncrementer"</emphasis>>
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 < 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>
|
||||
<job id="sampleJob">
|
||||
@@ -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>
|
||||
<step name="step1">
|
||||
@@ -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>
|
||||
<step name="step1">
|
||||
@@ -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>
|
||||
<step name="step1">
|
||||
@@ -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>
|
||||
<step name="step1">
|
||||
@@ -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>
|
||||
<step name="step1">
|
||||
@@ -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 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</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>
|
||||
<bean class="org.springframework.batch.sample.tasklet.SystemCommandTasklet">
|
||||
<property name="command" value="echo hello" />
|
||||
<!-- 5 second timeout for the command to complete -->
|
||||
<property name="timeout" value="5000" />
|
||||
</bean>
|
||||
|
||||
</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>
|
||||
<job id="job">
|
||||
@@ -1003,85 +1016,47 @@
|
||||
<step name="stepC" />
|
||||
</job>
|
||||
|
||||
</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>
|
||||
<job id="job">
|
||||
<step name="stepA">
|
||||
<next on="FAILED" to="stepB" />
|
||||
<next on="*" to="stepC" />
|
||||
</step>
|
||||
<step name="stepB" next="stepC" />
|
||||
<step name="stepC" />
|
||||
</job>
|
||||
|
||||
</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>
|
||||
<job id="job">
|
||||
<step name="stepA">
|
||||
<next on="FAILED" to="stepB" />
|
||||
<next on="*" to="stepC" />
|
||||
</step>
|
||||
<step name="stepB" next="stepC" />
|
||||
<step name="stepC" />
|
||||
</job>
|
||||
|
||||
</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>
|
||||
<step name="step1">
|
||||
@@ -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())
|
||||
&& stepExecution.getSkipCount() > 0) {
|
||||
return new ExitStatus("COMPLETED WITH SKIPS");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
|
||||
&& stepExecution.getSkipCount() > 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> <step name="stepC" /></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> <step name="step1" next="step2">
|
||||
<para><programlisting>
|
||||
<step name="step1">
|
||||
<stop on="COMPLETED" to="step2"/>
|
||||
</step>
|
||||
<step name="step2">
|
||||
<end on="FAILED"/>
|
||||
<next on="*" to="step3"/>
|
||||
<next on="FOO" to="step3"/>
|
||||
<end on="*" status="FAILED"/>
|
||||
</step>
|
||||
<step name="step3"></programlisting>
|
||||
</section>
|
||||
<step name="step3" />
|
||||
|
||||
<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> <step name="step1" next="step2">
|
||||
<step name="step2">
|
||||
<fail on="FAILED" status="EARLY TERMINATION"/>
|
||||
<next on="*" to="step3"/>
|
||||
</step>
|
||||
<step name="step3"></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> <step name="step1">
|
||||
<pause on="COMPLETED" to="step2"/>
|
||||
</step>
|
||||
<step name="step2"/></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> <split id="split1" next="step4">
|
||||
<flow>
|
||||
<step name="step1" next="step2"/>
|
||||
<step name="step2"/>
|
||||
</flow>
|
||||
<flow>
|
||||
<step name="step3"/>
|
||||
</flow>
|
||||
</split>
|
||||
<step name="step4"/></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>
|
||||
<bean id="flatFileItemReader"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<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>
|
||||
<bean id="flatFileItemReader"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="${input.file.name}" />
|
||||
@@ -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>
|
||||
<bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{jobParameters[input.file.name]}</emphasis>" />
|
||||
@@ -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>
|
||||
<bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{jobExecutionContext[input.file.name]}</emphasis>" />
|
||||
</bean>
|
||||
|
||||
</programlisting>
|
||||
|
||||
<programlisting>
|
||||
<bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{stepExecutionContext[input.file.name]}</emphasis>" />
|
||||
</bean>
|
||||
|
||||
</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>
|
||||
<bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="#{<emphasis role="bold">jobExecutionContext</emphasis>[input.file.name]}" />
|
||||
</bean>
|
||||
|
||||
</programlisting>
|
||||
|
||||
<programlisting>
|
||||
<bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="#{<emphasis role="bold">stepExecutionContext</emphasis>[input.file.name]}" />
|
||||
</bean>
|
||||
|
||||
</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>
|
||||
<bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="#{jobParameters[input.file.name]}" />
|
||||
@@ -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>
|
||||
<bean class="org.springframework.batch.core.scope.StepScope" />
|
||||
|
||||
</programlisting>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -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<T> {
|
||||
@@ -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 < 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>
|
||||
|
||||
Reference in New Issue
Block a user