90-character pass for domain.adoc

I removed extraneous characters from non-code lines and made each code line be as close to 90 characters as possible. I caught a few items I missed in the editing pass (such as missing hyphen and code ticks around some code words.
This commit is contained in:
Jay Bryant
2017-10-16 14:52:16 -05:00
committed by Michael Minella
parent d0238c9699
commit c83d9b4557

View File

@@ -8,58 +8,52 @@
include::toggle.adoc[]
To any experienced batch architect, the overall concepts of batch
processing used in Spring Batch should be familiar and comfortable. There
are "Jobs" and "Steps" and developer supplied processing units called
`ItemReader` and `ItemWriter`. However, because of the Spring patterns,
operations, templates, callbacks, and idioms, there are opportunities for
the following:
To any experienced batch architect, the overall concepts of batch processing used in
Spring Batch should be familiar and comfortable. There are "Jobs" and "Steps" and
developer-supplied processing units called `ItemReader` and `ItemWriter`. However,
because of the Spring patterns, operations, templates, callbacks, and idioms, there are
opportunities for the following:
* Significant improvement in adherence to a clear separation of concerns.
* Clearly delineated architectural layers and services provided as interfaces.
* Simple and default implementations that allow for quick adoption and ease of use out-of-the-box.
* Simple and default implementations that allow for quick adoption and ease of use
out-of-the-box.
* Significantly enhanced extensibility.
The following diagram is a simplified version of the batch reference
architecture that has been used for decades. It provides an overview of the
components that make up the domain language of batch processing. This
architecture framework is a blueprint that has been proven through decades
of implementations on the last several generations of platforms
(COBOL/Mainframe, C++/Unix, and now Java/anywhere). JCL and COBOL developers
are likely to be as comfortable with the concepts as C++, C#, and Java
developers. Spring Batch provides a physical implementation of the layers,
components, and technical services commonly found in the robust, maintainable
systems that are used to address the creation of simple to complex batch
applications, with the infrastructure and extensions to address very complex
processing needs.
The following diagram is a simplified version of the batch reference architecture that
has been used for decades. It provides an overview of the components that make up the
domain language of batch processing. This architecture framework is a blueprint that has
been proven through decades of implementations on the last several generations of
platforms (COBOL/Mainframe, C++/Unix, and now Java/anywhere). JCL and COBOL developers
are likely to be as comfortable with the concepts as C++, C#, and Java developers. Spring
Batch provides a physical implementation of the layers, components, and technical
services commonly found in the robust, maintainable systems that are used to address the
creation of simple to complex batch applications, with the infrastructure and extensions
to address very complex processing needs.
.Batch Stereotypes
image::{batch-asciidoc}images/spring-batch-reference-model.png[Figure 2.1: Batch Stereotypes, scaledwidth="60%"]
The preceding diagram highlights the key concepts that make up the domain
language of batch. A Job has one to many steps, each of which has exactly one
`ItemReader`, one `ItemProcessor`, and one `ItemWriter`. A job needs to be launched
(with `JobLauncher`), and metadata about the currently running process needs to be
stored (in `JobRepository`).
The preceding diagram highlights the key concepts that make up the domain language of
Spring Batch. A Job has one to many steps, each of which has exactly one `ItemReader`,
one `ItemProcessor`, and one `ItemWriter`. A job needs to be launched (with
`JobLauncher`), and metadata about the currently running process needs to be stored (in
`JobRepository`).
=== Job
This section describes stereotypes relating to the concept of a
batch job. A `Job` is an entity that encapsulates an
entire batch process. As is common with other Spring projects, a
`Job` is wired together with either an XML configuration
file or Java-based configuration. This configuration may be referred to as
the "job configuration". However, `Job` is just the
top of an overall hierarchy, as shown in the following diagram:
This section describes stereotypes relating to the concept of a batch job. A `Job` is an
entity that encapsulates an entire batch process. As is common with other Spring
projects, a `Job` is wired together with either an XML configuration file or Java-based
configuration. This configuration may be referred to as the "job configuration". However,
`Job` is just the top of an overall hierarchy, as shown in the following diagram:
.Job Hierarchy
image::{batch-asciidoc}images/job-heirarchy.png[Job Hierarchy, scaledwidth="60%"]
In Spring Batch, a `Job` is simply a container for `Step` instances. It combines
multiple steps that belong logically together in a flow and allows for
configuration of properties global to all steps, such as restartability.
The job configuration contains:
In Spring Batch, a `Job` is simply a container for `Step` instances. It combines multiple
steps that belong logically together in a flow and allows for configuration of properties
global to all steps, such as restartability. The job configuration contains:
* The simple name of the job.
* Definition and ordering of `Step` instances.
@@ -67,11 +61,10 @@ The job configuration contains:
ifdef::backend-html5[]
[role="javaContent"]
A default simple implementation of the Job
interface is provided by Spring Batch in the form of the
`SimpleJob` class, which creates some standard
functionality on top of `Job`. When using java based configuration, a collection of
builders is made available for the instantiation of a `Job`, as shown in the following example:
A default simple implementation of the Job interface is provided by Spring Batch in the
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`.
When using java based configuration, a collection of builders is made available for the
instantiation of a `Job`, as shown in the following example:
[source, java, role="javaContent"]
----
@@ -89,8 +82,8 @@ public Job footballJob() {
[role="xmlContent"]
A default simple implementation of the `Job` interface is provided by Spring Batch in the
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`.
However, the batch namespace abstracts away the need to instantiate it directly.
Instead, the `<job>` tag can be used as shown in the following example:
However, the batch namespace abstracts away the need to instantiate it directly. Instead,
the `<job>` tag can be used as shown in the following example:
[source, xml, role="xmlContent"]
----
@@ -103,11 +96,10 @@ Instead, the `<job>` tag can be used as shown in the following example:
endif::backend-html5[]
ifdef::backend-pdf[]
A default simple implementation of the Job
interface is provided by Spring Batch in the form of the
`SimpleJob` class, which creates some standard
functionality on top of `Job`. When using java based configuration, a collection of
builders are made available for the instantiation of a `Job`, as shown in the following example:
A default simple implementation of the Job interface is provided by Spring Batch in the
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`.
When using java based configuration, a collection of builders are made available for the
instantiation of a `Job`, as shown in the following example:
[source, java]
----
@@ -123,7 +115,8 @@ public Job footballJob() {
----
However, when using XML configuration, the batch namespace abstracts away the need to
instantiate it directly. Instead, the `<job>` tag can be used as shown in the following example:
instantiate it directly. Instead, the `<job>` tag can be used as shown in the following
example:
[source, xml]
----
@@ -137,145 +130,114 @@ endif::backend-pdf[]
==== JobInstance
A `JobInstance` refers to the concept of a
logical job run. Consider a batch job that should be run once at
the end of the day, such as the 'EndOfDay' `Job` from the preceding diagram.
There is one 'EndOfDay' job, but each individual
run of the `Job` must be tracked separately. In the
case of this job, there is one logical
`JobInstance` per day. For example, there is a
January 1st run, a January 2nd run, and so on. 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).
Therefore, each `JobInstance` can have multiple
executions (`JobExecution` is discussed in more
detail later in this chapter), and only one `JobInstance`
corresponding to a particular `Job` and
identifying `JobParameters` can run at a given
time.
A `JobInstance` refers to the concept of a logical job run. Consider a batch job that
should be run once at the end of the day, such as the 'EndOfDay' `Job` from the preceding
diagram. There is one 'EndOfDay' job, but each individual run of the `Job` must be
tracked separately. In the case of this job, there is one logical `JobInstance` per day.
For example, there is a January 1st run, a January 2nd run, and so on. 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). Therefore, each `JobInstance` can have multiple
executions (`JobExecution` is discussed in more detail later in this chapter), and only
one `JobInstance` corresponding to a particular `Job` and identifying `JobParameters` can
run at a given time.
The definition of a `JobInstance` has
absolutely no bearing on the data the to be loaded. It is entirely up
to the `ItemReader` implementation to
determine how data is loaded. For example, in the EndOfDay
scenario, there may be a column on the data that indicates the
'effective date' or 'schedule date' to which the data belongs. So, the
January 1st run would load only data from the 1st, and the January 2nd
run would use only data from the 2nd. Because this determination is
likely to be a business decision, it is left up to the
`ItemReader` to decide. However, using the same
`JobInstance` determines whether
or not the 'state' (that is, the `ExecutionContext`,
which is discussed later in this chapter) from previous executions is used. Using a
new `JobInstance` means 'start from the
beginning', and using an existing instance generally means 'start
from where you left off'.
The definition of a `JobInstance` has absolutely no bearing on the data the to be loaded.
It is entirely up to the `ItemReader` implementation to determine how data is loaded. For
example, in the EndOfDay scenario, there may be a column on the data that indicates the
'effective date' or 'schedule date' to which the data belongs. So, the January 1st run
would load only data from the 1st, and the January 2nd run would use only data from the
2nd. Because this determination is likely to be a business decision, it is left up to the
`ItemReader` to decide. However, using the same `JobInstance` determines whether or not
the 'state' (that is, the `ExecutionContext`, which is discussed later in this chapter)
from previous executions is used. Using a new `JobInstance` means 'start from the
beginning', and using an existing instance generally means 'start from where you left
off'.
==== JobParameters
Having discussed `JobInstance` and how it
differs from Job, the natural question to ask is:
"how is one `JobInstance` distinguished from
another?" The answer is: `JobParameters`.
A `JobParameters` object holds a set of parameters used to
start a batch job. They can be used for identification or even as
reference data during the run, as shown in the following image:
Having discussed `JobInstance` and how it differs from Job, the natural question to ask
is: "How is one `JobInstance` distinguished from another?" The answer is:
`JobParameters`. A `JobParameters` object holds a set of parameters used to start a batch
job. They can be used for identification or even as reference data during the run, as
shown in the following image:
.Job Parameters
image::{batch-asciidoc}images/job-stereotypes-parameters.png[Job Parameters, scaledwidth="60%"]
In the preceding example, where there are two instances, one for
January 1st, and another for January 2nd, there is really only one `Job`, but it has two `JobParameter` objects:
one that was started with a job parameter of 01-01-2017 and another that
was started with a parameter of 01-02-2017. Thus, the contract can be
defined as: `JobInstance` =
`Job` + identifying `JobParameters`. This
allows a developer to effectively control how a
`JobInstance` is defined, since they control what
parameters are passed in.
In the preceding example, where there are two instances, one for January 1st, and another
for January 2nd, there is really only one `Job`, but it has two `JobParameter` objects:
one that was started with a job parameter of 01-01-2017 and another that was started with
a parameter of 01-02-2017. Thus, the contract can be defined as: `JobInstance` = `Job` +
identifying `JobParameters`. This allows a developer to effectively control how a
`JobInstance` is defined, since they control what parameters are passed in.
NOTE: Not all job parameters are required to contribute to the identification
of a `JobInstance`. By default, they do so. However, the framework also
allows the submission of a `Job` with parameters that do
not contribute to the identity of a `JobInstance`.
NOTE: Not all job parameters are required to contribute to the identification of a
`JobInstance`. By default, they do so. However, the framework also allows the submission
of a `Job` with parameters that do not contribute to the identity of a `JobInstance`.
==== JobExecution
A `JobExecution` refers to the technical
concept of a single attempt to run a Job. An
execution may end in failure or success, but the
`JobInstance` corresponding to a given execution
is not considered to be complete unless the execution completes
successfully. Using the EndOfDay `Job` described
previously as an example, consider a `JobInstance` for
01-01-2017 that failed the first time it was run. If it is run again
with the same identifying job parameters as the first run (01-01-2017), a new
`JobExecution` is created. However, there is
still only one `JobInstance`.
A `JobExecution` refers to the technical concept of a single attempt to run a Job. An
execution may end in failure or success, but the `JobInstance` corresponding to a given
execution is not considered to be complete unless the execution completes successfully.
Using the EndOfDay `Job` described previously as an example, consider a `JobInstance` for
01-01-2017 that failed the first time it was run. If it is run again with the same
identifying job parameters as the first run (01-01-2017), a new `JobExecution` is
created. However, there is still only one `JobInstance`.
A `Job` defines what a job is and how it is
to be executed, and a `JobInstance` is a purely
organizational object to group executions together, primarily to enable
correct restart semantics. A `JobExecution`,
however, is the primary storage mechanism for what actually happened
during a run and contains many more properties that must be
controlled and persisted, as shown in the following table:
A `Job` defines what a job is and how it is to be executed, and a `JobInstance` is a
purely organizational object to group executions together, primarily to enable correct
restart semantics. A `JobExecution`, however, is the primary storage mechanism for what
actually happened during a run and contains many more properties that must be controlled
and persisted, as shown in the following table:
.JobExecution Properties
|===
|Property |Definition
|Status
|A `BatchStatus` object that
indicates the status of the execution. While running, it is
`BatchStatus#STARTED`. If it fails, it is `BatchStatus#FAILED`.
If it finishes successfully, it is `BatchStatus#COMPLETED`
|A `BatchStatus` object that indicates the status of the execution. While running, it is
`BatchStatus#STARTED`. If it fails, it is `BatchStatus#FAILED`. If it finishes
successfully, it is `BatchStatus#COMPLETED`
|startTime
|A `java.util.Date` representing the
current system time when the execution was started. This field is empty if the job has yet to start.
|A `java.util.Date` representing the current system time when the execution was started.
This field is empty if the job has yet to start.
|endTime
|A `java.util.Date` representing the
current system time when the execution finished, regardless of
whether or not it was successful. The field is empty if the job has yet to finish.
|A `java.util.Date` representing the current system time when the execution finished,
regardless of whether or not it was successful. The field is empty if the job has yet to
finish.
|exitStatus
|The `ExitStatus`, indicating the
result of the run. It is most important, because it contains an
exit code that is returned to the caller. See chapter 5 for
more details. The field is empty if the job has yet to finish.
|The `ExitStatus`, indicating the result of the run. It is most important, because it
contains an exit code that is returned to the caller. See chapter 5 for more details. The
field is empty if the job has yet to finish.
|createTime
|A `java.util.Date` representing the
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 always has a createTime,
which is required by the framework for managing job level
|A `java.util.Date` representing the 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 always has a createTime, which is required by the framework for managing job level
`ExecutionContexts`.
|lastUpdated
|A `java.util.Date` representing the
last time a `JobExecution` was
persisted. This field is empty if the job has yet to start.
|A `java.util.Date` representing the last time a `JobExecution` was persisted. This field
is empty if the job has yet to start.
|executionContext
|The "property bag" containing any user data that needs to
be persisted between executions.
|The "property bag" containing any user data that needs to be persisted between
executions.
|failureExceptions
|The list of exceptions encountered during the execution
of a `Job`. These can be useful if more
than one exception is encountered during the failure of a
`Job`.
|The list of exceptions encountered during the execution of a `Job`. These can be useful
if more than one exception is encountered during the failure of a `Job`.
|===
These properties are important because they are persisted and
can be used to completely determine the status of an execution. For
example, if the EndOfDay job for 01-01 is executed at 9:00 PM and fails
at 9:30, the following entries are made in the batch meta data
tables:
These properties are important because they are persisted and can be used to completely
determine the status of an execution. For example, if the EndOfDay job for 01-01 is
executed at 9:00 PM and fails at 9:30, the following entries are made in the batch
metadata tables:
.BATCH_JOB_INSTANCE
@@ -305,30 +267,23 @@ tables:
|FAILED
|===
NOTE: Column names may have been abbreviated or removed for the sake of clarity
and formatting.
NOTE: Column names may have been abbreviated or removed for the sake of clarity and
formatting.
Now that the job has failed, assume that it took the entire
night for the problem to be determined, so that the 'batch
window' is now closed. Further assuming that the window starts at 9:00 PM, the job
is kicked off again for 01-01, starting where it left off and
completing successfully at 9:30. Because it is now the next day, the
01-02 job must be run as well, and it is kicked off just afterwards at
9:31 and completes in its normal one hour time at 10:30. There is no
requirement that one `JobInstance` 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
`Job` should be run. Since they are separate
`JobInstances`, Spring Batch makes no attempt
to stop them from being run concurrently. (Attempting to run the same
`JobInstance` while another is already running
results in a `JobExecutionAlreadyRunningException`
being thrown). There should now be an extra entry in both the
`JobInstance` and
`JobParameters` tables and two extra entries in
the `JobExecution` table, as shown in the following tables:
Now that the job has failed, assume that it took the entire night for the problem to be
determined, so that the 'batch window' is now closed. Further assuming that the window
starts at 9:00 PM, the job is kicked off again for 01-01, starting where it left off and
completing successfully at 9:30. Because it is now the next day, the 01-02 job must be
run as well, and it is kicked off just afterwards at 9:31 and completes in its normal one
hour time at 10:30. There is no requirement that one `JobInstance` 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 `Job` should be run. Since they are separate `JobInstances`, Spring
Batch makes no attempt to stop them from being run concurrently. (Attempting to run the
same `JobInstance` while another is already running results in a
`JobExecutionAlreadyRunningException` being thrown). There should now be an extra entry
in both the `JobInstance` and `JobParameters` tables and two extra entries in the
`JobExecution` table, as shown in the following tables:
.BATCH_JOB_INSTANCE
|===
@@ -340,10 +295,6 @@ the `JobExecution` table, as shown in the following tables:
|EndOfDayJob
|===
.BATCH_JOB_EXECUTION_PARAMS
|===
|JOB_EXECUTION_ID|TYPE_CD|KEY_NAME|DATE_VAL|IDENTIFYING
@@ -388,143 +339,113 @@ the `JobExecution` table, as shown in the following tables:
|COMPLETED
|===
NOTE: Column names may have been abbreviated or removed for the sake of clarity
and formatting.
NOTE: Column names may have been abbreviated or removed for the sake of clarity and
formatting.
=== Step
A `Step` is a domain object that encapsulates
an independent, sequential phase of a batch job. Therefore, every
Job is composed entirely of one or more steps. A
`Step` contains all of the information necessary to
define and control the actual batch processing. This is a necessarily
vague description because the contents of any given
`Step` are at the discretion of the developer writing
a `Job`. A `Step` can be as simple or complex as the
developer desires. A simple `Step` might load data
from a file into the database, requiring little or no code (depending
upon the implementations used). A more complex `Step`
may have complicated business rules that are applied as part of the
processing. As with `Job`, a
`Step` has an individual
`StepExecution` that correlates with a unique
A `Step` is a domain object that encapsulates an independent, sequential phase of a batch
job. Therefore, every Job is composed entirely of one or more steps. A `Step` contains
all of the information necessary to define and control the actual batch processing. This
is a necessarily vague description because the contents of any given `Step` are at the
discretion of the developer writing a `Job`. A `Step` can be as simple or complex as the
developer desires. A simple `Step` might load data from a file into the database,
requiring little or no code (depending upon the implementations used). A more complex
`Step` may have complicated business rules that are applied as part of the processing. As
with a `Job`, a `Step` has an individual `StepExecution` that correlates with a unique
`JobExecution`, as shown in the following image:
.Job Hierarchy With Steps
image::{batch-asciidoc}images/jobHeirarchyWithSteps.png[Figure 2.1: Job Hierarchy With Steps, scaledwidth="60%"]
==== StepExecution
A `StepExecution` represents a single attempt
to execute a `Step`. A new
`StepExecution` is created each time a
`Step` is run, similar to
`JobExecution`. However, if a step fails to execute
because the step before it fails, no execution is persisted
for it. A `StepExecution` is created only when
its `Step` is actually started.
A `StepExecution` represents a single attempt to execute a `Step`. A new `StepExecution`
is created each time a `Step` is run, similar to `JobExecution`. However, if a step fails
to execute because the step before it fails, no execution is persisted for it. A
`StepExecution` is created only when its `Step` is actually started.
`Step` executions are represented by objects of the
`StepExecution` class. Each execution contains a
reference to its corresponding step and
`JobExecution` and transaction related data, such
as commit and rollback counts and start and end times. Additionally, each
step execution contains an `ExecutionContext`,
which contains any data a developer needs to have persisted across batch runs,
such as statistics or state information needed to restart. The following
table lists the properties for
StepExecution:
`Step` executions are represented by objects of the `StepExecution` class. Each execution
contains a reference to its corresponding step and `JobExecution` and transaction related
data, such as commit and rollback counts and start and end times. Additionally, each step
execution contains an `ExecutionContext`, which contains any data a developer needs to
have persisted across batch runs, such as statistics or state information needed to
restart. The following table lists the properties for `StepExecution`:
.StepExecution Properties
|===
|Property|Definition
|Status
|A `BatchStatus` object that
indicates the status of the execution. While running, the
status is BatchStatus.STARTED. If it fails, the status is
BatchStatus.FAILED. If it finishes successfully, the status
is BatchStatus.COMPLETED.
|A `BatchStatus` object that indicates the status of the execution. While running, the
status is `BatchStatus.STARTED`. If it fails, the status is `BatchStatus.FAILED`. If it
finishes successfully, the status is `BatchStatus.COMPLETED`.
|startTime
|A `java.util.Date` representing the
current system time when the execution was started. This field is empty if the job has yet to start.
|A `java.util.Date` representing the current system time when the execution was started.
This field is empty if the job has yet to start.
|endTime
|A `java.util.Date` representing the
current system time when the execution finished, regardless of
whether or not it was successful. This field is empty if the job has yet to exit.
|A `java.util.Date` representing the current system time when the execution finished,
regardless of whether or not it was successful. This field is empty if the job has yet to
exit.
|exitStatus
|The `ExitStatus` indicating the
result of the execution. It is most important, because it
contains an exit code that is returned to the caller. See
chapter 5 for more details. This field is empty if the job has yet to exit.
|The `ExitStatus` indicating the result of the execution. It is most important, because
it contains an exit code that is returned to the caller. See chapter 5 for more details.
This field is empty if the job has yet to exit.
|executionContext
|The "property bag" containing any user data that needs to
be persisted between executions.
|The "property bag" containing any user data that needs to be persisted between
executions.
|readCount
|The number of items that have been successfully
read.
|The number of items that have been successfully read.
|writeCount
|The number of items that have been successfully
written.
|The number of items that have been successfully written.
|commitCount
|The number of transactions that have been committed for this
execution.
|The number of transactions that have been committed for this execution.
|rollbackCount
|The number of times the business transaction controlled
by the `Step` has been rolled back.
|The number of times the business transaction controlled by the `Step` has been rolled
back.
|readSkipCount
|The number of times `read` has
failed, resulting in a skipped item.
|The number of times `read` has failed, resulting in a skipped item.
|processSkipCount
|The number of times `process` has
failed, resulting in a skipped item.
|The number of times `process` has failed, resulting in a skipped item.
|filterCount
|The number of items that have been 'filtered' by the
`ItemProcessor`.
|The number of items that have been 'filtered' by the `ItemProcessor`.
|writeSkipCount
|The number of times `write` has
failed, resulting in a skipped item.
|The number of times `write` has failed, resulting in a skipped item.
|===
=== ExecutionContext
An `ExecutionContext` represents a collection
of key/value pairs that are persisted and controlled by the framework in
order to allow developers a place to store persistent state that is scoped
to a `StepExecution` object or a
`JobExecution` object. For those familiar with Quartz, it is
very similar to JobDataMap. The best usage example
is to facilitate restart. Using flat file input as an example, while
processing individual lines, the framework periodically persists the
`ExecutionContext` at commit points. Doing so allows the
`ItemReader` to store its state in case a fatal error
occurs during the run or even if the power goes out. All that is needed
is to put the current number of lines read into the context, as shown in the following example, and the
framework will do the rest:
An `ExecutionContext` represents a collection of key/value pairs that are persisted and
controlled by the framework in order to allow developers a place to store persistent
state that is scoped to a `StepExecution` object or a `JobExecution` object. For those
familiar with Quartz, it is very similar to JobDataMap. The best usage example is to
facilitate restart. Using flat file input as an example, while processing individual
lines, the framework periodically persists the `ExecutionContext` at commit points. Doing
so allows the `ItemReader` to store its state in case a fatal error occurs during the run
or even if the power goes out. All that is needed is to put the current number of lines
read into the context, as shown in the following example, and the framework will do the
rest:
[source, java]
----
executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());
----
Using the EndOfDay example from the `Job` Stereotypes section as an
example, assume there is one step, 'loadData', that loads a file into the
database. After the first failed run, the metadata tables would look like
the following example:
Using the EndOfDay example from the `Job` Stereotypes section as an example, assume there
is one step, 'loadData', that loads a file into the database. After the first failed run,
the metadata tables would look like the following example:
.BATCH_JOB_INSTANCE
|===
@@ -570,20 +491,16 @@ the following example:
|{piece.count=40321}
|===
In the preceding case, the `Step` ran for 30 minutes
and processed 40,321 'pieces', which would represent lines in a file in
this scenario. This value is updated just before each commit by the
framework and can contain multiple rows corresponding to entries within
the `ExecutionContext`. Being notified before a
commit requires one of the various `StepListener` implementations
(or an `ItemStream`), 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 `ExecutionContext` of
the last run are reconstituted from the database. When the
`ItemReader` is opened, it can check to see if it has
any stored state in the context and initialize itself from there, as shown in the following example:
In the preceding case, the `Step` ran for 30 minutes and processed 40,321 'pieces', which
would represent lines in a file in this scenario. This value is updated just before each
commit by the framework and can contain multiple rows corresponding to entries within the
`ExecutionContext`. Being notified before a commit requires one of the various
`StepListener` implementations (or an `ItemStream`), 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 `ExecutionContext` of
the last run are reconstituted from the database. When the `ItemReader` is opened, it can
check to see if it has any stored state in the context and initialize itself from there,
as shown in the following example:
[source, java]
----
@@ -600,42 +517,30 @@ if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
}
}
----
In this case, after the above code runs, the current line
is 40,322, allowing the `Step` to start again
from where it left off. The `ExecutionContext` can
also be used for statistics that need to be persisted about the run
itself. For example, if a flat file contains orders for processing that
exist across multiple lines, it may be necessary to store how many orders
have been processed (which is much different from the number of lines
read), so that an email can be sent at the end of the
`Step` with the total number of orders processed in the body.
The framework handles storing this for the developer, in order to
correctly scope it with an individual `JobInstance`.
It can be very difficult to know whether an existing
`ExecutionContext` should be used or not. For
example, using the 'EndOfDay' example from above, when the 01-01 run
starts again for the second time, the framework recognizes that it is the
same `JobInstance` and on an individual
`Step` basis, pulls the
`ExecutionContext` out of the database, and hands it
(as part of the `StepExecution`) to the
`Step` itself. Conversely, for the 01-02 run, the
framework recognizes that it is a different instance, so an empty context
must be handed to the `Step`. There are many of these
types of determinations that the framework makes for the developer, to
ensure the state is given to them at the correct time. It is also
important to note that exactly one `ExecutionContext`
exists per `StepExecution` at any given time. Clients
of the `ExecutionContext` should be careful, because
this creates a shared keyspace. As a result, care should be taken when putting
values in to ensure no data is overwritten. However, the
`Step` stores absolutely no data in the context, so
there is no way to adversely affect the framework.
In this case, after the above code runs, the current line is 40,322, allowing the `Step`
to start again from where it left off. The `ExecutionContext` can also be used for
statistics that need to be persisted about the run itself. For example, if a flat file
contains orders for processing that exist across multiple lines, it may be necessary to
store how many orders have been processed (which is much different from the number of
lines read), so that an email can be sent at the end of the `Step` with the total number
of orders processed in the body. The framework handles storing this for the developer, in
order to correctly scope it with an individual `JobInstance`. It can be very difficult to
know whether an existing `ExecutionContext` should be used or not. For example, using the
'EndOfDay' example from above, when the 01-01 run starts again for the second time, the
framework recognizes that it is the same `JobInstance` and on an individual `Step` basis,
pulls the `ExecutionContext` out of the database, and hands it (as part of the
`StepExecution`) to the `Step` itself. Conversely, for the 01-02 run, the framework
recognizes that it is a different instance, so an empty context must be handed to the
`Step`. There are many of these types of determinations that the framework makes for the
developer, to ensure the state is given to them at the correct time. It is also important
to note that exactly one `ExecutionContext` exists per `StepExecution` at any given time.
Clients of the `ExecutionContext` should be careful, because this creates a shared
keyspace. As a result, care should be taken when putting values in to ensure no data is
overwritten. However, the `Step` stores absolutely no data in the context, so there is no
way to adversely affect the framework.
It is also important to note that there is at least one
`ExecutionContext` per
`JobExecution` and one for every
`StepExecution`. For example, consider the following
It is also important to note that there is at least one `ExecutionContext` per
`JobExecution` and one for every `StepExecution`. For example, consider the following
code snippet:
[source, java]
@@ -645,28 +550,21 @@ ExecutionContext ecJob = jobExecution.getExecutionContext();
//ecStep does not equal ecJob
----
As noted in the comment, `ecStep` does not equal `ecJob`. They are two
different `ExecutionContexts`. The one scoped to the
`Step` is saved at every commit point in the
`Step`, whereas the one scoped to the
Job is saved in between every
`Step` execution.
As noted in the comment, `ecStep` does not equal `ecJob`. They are two different
`ExecutionContexts`. The one scoped to the `Step` is saved at every commit point in the
`Step`, whereas the one scoped to the Job is saved in between every `Step` execution.
=== JobRepository
`JobRepository` is the persistence mechanism
for all of the Stereotypes mentioned above. It provides CRUD operations
for `JobLauncher`, `Job`, and
`Step` implementations. When a
Job is first launched, a
`JobExecution` is obtained from the repository, and,
during the course of execution, `StepExecution` and
`JobExecution` implementations are persisted by
passing them to the repository.
`JobRepository` is the persistence mechanism for all of the Stereotypes mentioned above.
It provides CRUD operations for `JobLauncher`, `Job`, and `Step` implementations. When a
`Job` is first launched, a `JobExecution` is obtained from the repository, and, during
the course of execution, `StepExecution` and `JobExecution` implementations are persisted
by passing them to the repository.
[role="xmlContent"]
The batch namespace provides support for configuring a `JobRepository` instance with
the `<job-repository>` tag, as shown in the following example:
The batch namespace provides support for configuring a `JobRepository` instance with the
`<job-repository>` tag, as shown in the following example:
[source, xml, role="xmlContent"]
----
@@ -679,8 +577,7 @@ When using java configuration, `@EnableBatchProcessing` annotation provides a
=== JobLauncher
`JobLauncher` represents a simple interface for
launching a `Job` with a given set of
`JobLauncher` represents a simple interface for launching a `Job` with a given set of
`JobParameters`, as shown in the following example:
[source, java]
@@ -691,50 +588,41 @@ public JobExecution run(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException;
}
----
It is expected that implementations obtain a valid
`JobExecution` from the
`JobRepository` and execute the
`Job`.
It is expected that implementations obtain a valid `JobExecution` from the
`JobRepository` and execute the `Job`.
=== Item Reader
`ItemReader` is an abstraction that represents
the retrieval of input for a `Step`, one item at a
time. When the `ItemReader` has exhausted the items
it can provide, it indicates this by returning `null`. More details
about the `ItemReader` interface and its various
implementations can be found in <<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
`ItemReader` is an abstraction that represents the retrieval of input for a `Step`, one
item at a time. When the `ItemReader` has exhausted the items it can provide, it
indicates this by returning `null`. More details about the `ItemReader` interface and its
various implementations can be found in
<<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
=== Item Writer
`ItemWriter` is an abstraction that
represents the output of a `Step`, one batch
or chunk of items at a time. Generally, an `ItemWriter` has no
knowledge of the input it should receive next and knows only the item that
was passed in its current invocation. More details about the
`ItemWriter` interface and its various
implementations can be found in <<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
`ItemWriter` is an abstraction that represents the output of a `Step`, one batch or chunk
of items at a time. Generally, an `ItemWriter` has no knowledge of the input it should
receive next and knows only the item that was passed in its current invocation. More
details about the `ItemWriter` interface and its various implementations can be found in
<<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
=== Item Processor
`ItemProcessor` is an abstraction that
represents the business processing of an item. While the
`ItemReader` reads one item, and the
`ItemWriter` writes them, the
`ItemProcessor` provides an access point 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 <<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
`ItemProcessor` is an abstraction that represents the business processing of an item.
While the `ItemReader` reads one item, and the `ItemWriter` writes them, the
`ItemProcessor` provides an access point 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
<<readersAndWriters.adoc#readersAndWriters,Readers And Writers>>.
[role="xmlContent"]
=== Batch Namespace
Many of the domain concepts listed previously need to be configured in a
Spring `ApplicationContext`. While there are
implementations of the interfaces above that can be used in a standard
bean definition, a namespace has been provided for ease of
Many of the domain concepts listed previously need to be configured in a Spring
`ApplicationContext`. While there are implementations of the interfaces above that can be
used in a standard bean definition, a namespace has been provided for ease of
configuration, as shown in the following example:
[source, xml, role="xmlContent"]
@@ -760,7 +648,7 @@ xsi:schemaLocation="
----
[role="xmlContent"]
As long as the batch namespace has been declared, any of its
elements can be used. More information on configuring a
Job can be found in <<job.adoc#configureJob,Configuring and Running a Job>>. More information on configuring a `Step` can be
found in <<step.adoc#configureStep,Configuring a Step>>.
As long as the batch namespace has been declared, any of its elements can be used. More
information on configuring a Job can be found in <<job.adoc#configureJob,Configuring and
Running a Job>>. More information on configuring a `Step` can be found in
<<step.adoc#configureStep,Configuring a Step>>.