diff --git a/docs/models/Figures.ppt b/docs/models/Figures.ppt
index 081ba8c42..983683c5a 100644
Binary files a/docs/models/Figures.ppt and b/docs/models/Figures.ppt differ
diff --git a/docs/models/diagrams.ppt b/docs/models/diagrams.ppt
new file mode 100755
index 000000000..2b6a1392a
Binary files /dev/null and b/docs/models/diagrams.ppt differ
diff --git a/docs/src/site/docbook/reference/Discard.xml b/docs/src/site/docbook/reference/Discard.xml
deleted file mode 100644
index 4f66e8d3e..000000000
--- a/docs/src/site/docbook/reference/Discard.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- Discard
-
-
- Support Stereotypes
-
- While item readers and writers serve as the main entry points for
- item-oriented processing, they might be supplemented by a number of
- support classes that perform specific tasks within the reader / writer
- lifecycle. These support stereotypes are useful for dividing the work of
- item readers and writers into reusable pieces, as well as abstracting away
- the details of processing, such as interaction with external systems.
- Additionally, they give us another opportunity to leverage the powerful
- configuration features of the Spring framework, as we can switch between
- several beans implementing these support interfaces without changing the
- driving item reader or writer.
-
-
- Item Transformers
-
- An item transformer is a class that is capable of taking an object
- and changing it somehow before processing occurs. For instance, an item
- transformer my alter an object by changing its properties or by
- replacing it with another object entirely, such as a wrapper or
- derivative object. It can also be defined as an adaptor, allowing an
- object of one type to be converted for use as an object of a second
- type.
-
-
-
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/batch-job-testing.xml b/docs/src/site/docbook/reference/batch-job-testing.xml
deleted file mode 100644
index ec54f8f81..000000000
--- a/docs/src/site/docbook/reference/batch-job-testing.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- Batch Unit and Integration Tests
-
-
- Unit Testing
- Document Batch Job Unit Testing features. This includes the use of Mock Objects,
- embedded database (HSQLDB), etc.
-
-
-
- Integration Testing
- Document how to test against the targeted database, applications, etc.
-
-
-
diff --git a/docs/src/site/docbook/reference/batch-launch.xml b/docs/src/site/docbook/reference/batch-launch.xml
deleted file mode 100644
index 6795bcbc8..000000000
--- a/docs/src/site/docbook/reference/batch-launch.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- Run Tier - Launching Batch Jobs
-
- Mapping Batch Error Codes to Launch Client Error Codes
- Mapping Batch Error Codes to Launch Client Error Codes
-
-
- Launch Batch from Command Line
- Document Command Line Launching
-
-
- Launch Batch On Demand
- Document Launching Batch Jobs on Demand
-
-
diff --git a/docs/src/site/docbook/reference/batch-performance-testing.xml b/docs/src/site/docbook/reference/batch-performance-testing.xml
deleted file mode 100644
index 8e2b30fdc..000000000
--- a/docs/src/site/docbook/reference/batch-performance-testing.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
- Batch Performance Testing
-
- Performance Testing Overview
- A batch performance test team needs to have the following at their disposal:
-
-
-
-
- Define performance Targets -
-
-
- Establishing the requirements for a performance testing environment -
-
-
- Performance Data - generating adequate volumes of realistic data for performance testing
-
-
- Performance Tools -
-
-
- Performance Team Roles - Tool SME's, performance DBA.
-
-
-
-
-
-
- Defining Performance Targets
-
-
-
-
- Establishing Performance requirements and installing the environment.
- Establishing the requirements for the performance environment.
-
-
-
- Performance Data
- Generating adequate volumes of realistic data for performance testing.
-
-
-
- Performance Team Roles
- Tool SME's, performance dba's, environment experts (OS, JVM, etc.)
-
-
diff --git a/docs/src/site/docbook/reference/common-patterns.xml b/docs/src/site/docbook/reference/common-patterns.xml
new file mode 100644
index 000000000..106183f4b
--- /dev/null
+++ b/docs/src/site/docbook/reference/common-patterns.xml
@@ -0,0 +1,232 @@
+
+
+
+ Common Batch Patterns
+
+
+ Introduction
+
+ Some batch jobs can be assembled purely from off-the-shelf
+ components in Spring Batch, mostly the ItemReader
+ and ItemWriter implementations. Where this is not
+ possible (the majority of cases) the main API entry points for application
+ developers are the Tasklet,
+ ItemReader, ItemWriter and
+ the various listener interfaces. Most simple batch jobs will be able to
+ use off-the-shelf input from a Spring Batch
+ ItemReader, but it is very often the case that
+ there are custom concerns in the processing and writing, which normally
+ leads developers to implement an ItemWriter, or
+ ItemTransformer.
+
+ Here we provide a few examples of common patterns in custom business
+ logic, mainly using the listener interfaces . It should be noted that an
+ ItemReader or ItemWriter can
+ implement the listener interfaces as well if appropriate.
+
+
+
+ Logging Item Processing and Failures
+
+ A common use case is the need for special handling of errors in a
+ step, item by item, perhaps logging to a special channel, or inserting a
+ record into a database. The StepHandlerStep
+ (created from the step factory beans) allows users to implement this use
+ case with a simple ItemReadListener, for errors on
+ read, and an ItemWriteListener, for errors on
+ write. The below code snippets illustrate a listener that logs both read
+ and write failures:
+
+ public class ItemFailureLoggerListener extends ItemListenerSupport {
+
+ private static Log logger = LogFactory.getLog("item.error");
+
+ public void onReadError(Exception ex) {
+ logger.error("Encountered error on read", e);
+ }
+
+ public void onWriteError(Exception ex, Object item) {
+ logger.error("Encountered error on write", e);
+ }
+
+}
+
+ Having implemented this listener it must be registered with the
+ step:
+
+ <bean id="simpleStep"
+ class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
+ ...
+ <property name="listeners">
+ <bean class="org.example...ItemFailureLoggerListener"/>
+ </property>
+ </bean>
+
+ Remember that if your listener does anything in an
+ onError() method, it will be inside a transaction that is
+ going to be rolled back. If you need to use a transactional resource such
+ as a database inside an onError() method, consider adding a
+ declarative transaction to that method (see Spring Core Reference Guide
+ for details), and giving its propagation attribute the value
+ REQUIRES_NEW.
+
+
+
+ Stopping a Job Manually for Business Reasons
+
+ Spring Batch provides a stop() method through the JobLauncher
+ interface, but this is really aimed at the operator, rather than the
+ application programmer. Sometimes it is more convenient or makes more
+ sense to stop a job execution from within the business logic.
+
+ The simplest thing to do is to throw a RuntimeException (one that
+ isn't retried indefinitely or skipped), For example, a custom exception
+ type could be used, as in the example below:
+
+ public class PoisonPillItemWriter extends AbstractItemWriter {
+
+ public void write(Object item) throws Exception {
+
+ if (isPoisonPill(item)) {
+ throw new PoisonPillException("Posion pill detected: "+item);
+ }
+
+ }
+
+}
+
+ Another simple way to stop a step from executing is to simply return
+ null from the ItemReader:
+
+ public class EarlyCompletionItemReader extends AbstractItemReader {
+
+ private ItemReader delegate;
+
+ public void setDelegate(ItemReader delegate) { ... }
+
+ public Object read() throws Exception {
+
+ Object item = delegate.read();
+
+ if (isEndItem(item)) {
+ return null; // end the step here
+ }
+
+ return item;
+
+ }
+
+}
+
+ The previous example actually relies on the fact that there is a
+ default implementation of the CompletionPolicy
+ strategy which signals a complete batch when the item to be processed is
+ null. A more sophisticated completion policy could be implemented and
+ injected into the Step through the
+ RepeatOperationsStepFactoryBean:
+
+ <bean id="simpleStep"
+ class="org.springframework.batch.core.step.item.RepeatOperationsStepFactoryBean" >
+ ...
+ <property name="chunkOperations">
+ <bean class="org.springframework.batch.repeat.support.RepeatTemplate">
+ <property name="completionPolicy">
+ <bean class="org.example...SpecialCompletionPolicy"/>
+ </property>
+ </bean>
+ </property>
+ </bean>
+
+ An alternative is to set a flag in the
+ StepExecution, which is checked by the
+ Step implementations in the framework in between
+ item processing. To implement this alternative, we need access to the
+ current StepExecution, and this can be achieved by implementing a
+ StepListener and registering it with the Step. Here is an example of a
+ listener that sets the flag:
+
+ public class CustomItemWriter extends ItemListenerSupport implements StepListener {
+
+ private StepExecution stepExecution;
+
+ public void beforeStep(StepExecution stepExecution) {
+ this.stepExecution = stepExecution;
+ }
+
+ public void afterRead(Object item) {
+
+ if (isPoisonPill(item)) {
+ stepExecution.setTerminateOnly(true);
+ }
+
+ }
+
+}
+
+ The default behaviour here when the flag is set is for the step to
+ throw a JobInterruptedException. This can be
+ controlled through the StepInterruptionPolicy, but
+ the only choice is to throw or not throw an exception, so this is always
+ an abnormal ending to a job.
+
+
+
+ Adding a Footer Record
+
+ A very common requirement is to aggregate information during the
+ output process and to append a record at the end of a file summarizing the
+ data, or providing a checksum. This can also be achieved with a callbacks
+ in the step, normally as part of a custom
+ ItemWriter. In this case, since a job is
+ accumulating state that should not be lost if the job aborts, the
+ ItemStream interface should be implemented:
+
+ public class CustomItemWriter extends AbstractItemWriter implements
+ ItemStream, StepListener
+{
+
+ private static final String TOTAL_AMOUNT_KEY = "total.amount";
+
+ private ItemWriter delegate;
+
+ private double totalAmount = 0.0;
+
+ public void setDelegate(ItemWriter delegate) { ... }
+
+ public ExitStatus afterStep(StepExecution stepExecution) {
+ // Add the footer record here...
+ delegate.write("Total Amount Processed: " + totalAmount);
+ }
+
+ public void open(ExecutionContext executionContext) {
+ if (executionContext.containsKey(TOTAL_AMOUNT_KEY) {
+ totalAmount = executionContext.getDouble(TOTAL_AMOUNT_KEY);
+ }
+ }
+
+ public void update(ExecutionContext executionContext) {
+ executionContext.setDouble(TOTAL_AMOUNT_KEY, totalAmount);
+ }
+
+ public void write(Object item) {
+
+ delegate.write(item);
+ totalAmount += ((Trade) item).getAmount();
+
+ }
+
+}
+
+ The custom writer in the example is stateful (it maintains its total
+ in an instance variable totalAmount), but the state is
+ stored through the ItemStream interface in the
+ ExecutionContext. In this way we can be sure that
+ when the open() callback is received on a restart. The
+ framework garuntees we always get the last value that was committed. It
+ should be noted that it is not always necessary to implement ItemStream.
+ For example, if the ItemWriter is re-runnable, in the sense that it
+ maintains its own state in a transactional resource like a database, there
+ is no need to maintain state within the writer itself.
+
+
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/core.xml b/docs/src/site/docbook/reference/domain.xml
similarity index 51%
rename from docs/src/site/docbook/reference/core.xml
rename to docs/src/site/docbook/reference/domain.xml
index 26fd1a7d8..d04a1bab8 100644
--- a/docs/src/site/docbook/reference/core.xml
+++ b/docs/src/site/docbook/reference/domain.xml
@@ -4,130 +4,69 @@
The Domain Language of Batch
-
- Introduction
+ 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
+ ItemReaders and ItemWriters. 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
+
- 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
- ItemReaders and ItemWriters. 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
+
-
- clearly delineated architectural layers and services provided
- as interfaces
-
+
+ simple and default implementations that allowed for quick
+ adoption and ease of use out-of-the-box
+
-
- simple and default implementations that allowed for quick
- adoption and ease of use out-of-the-box
-
+
+ significantly enhanced extensibility
+
+
-
- significantly enhanced extensibility
-
-
+ The diagram below is 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 robust, maintainable
+ systems used to address the creation of simple to complex batch
+ applications, with the infrastructure and extensions to address very complex
+ processing needs.
- The diagram below is only a slight variation of the batch reference
- architecture that has been used for decades. It provides an overview of
- the high level components, technical services, and basic operations
- required by a batch architecture. 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 robust, maintainable systems used to address the
- creation of simple to complex batch applications, with the infrastructure
- and extensions to address very complex processing needs.
-
+
+
+
+
+
+
+
+
+
+ Figure 2.1: Batch Stereotypes
+
+
+ The diagram above highlights the key concepts that make up the domain
+ 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)
- Batch Application Style Interactions and Services
-
-
-
-
-
-
-
-
-
-
- Figure 2.1: Batch Stereotypes
-
-
- The above diagram highlights the interactions and key services
- provided by the Spring Batch framework. The colors used are important to
- understanding the responsibilities of a developer in Spring Batch. Grey
- represents an external application such as an enterprise scheduler or a
- database. It's important to note that scheduling is grey, and should thus
- be considered separate from Spring Batch. Blue represents application
- architecture services. In most cases these are provided by Spring Batch
- with out of the box implementations, but an architecture team may make
- specific implementations that better address their specific needs. Yellow
- represents the pieces that must be configured by a developer. For example,
- a job schedule needs to be configured so that the job is kicked off at the
- appropriate time. A job configuration file also needs to be created, which
- defines how a job will be run. It is also worth noting that the
- ItemReader and ItemWriter
- used by an application may just as easily be a custom one made by a
- developer for their specific batch job, rather than one provided by Spring
- Batch or an architecture team.
-
- The Batch Application Style is organized into four logical tiers,
- which include Run, Job, Application, and Data. The primary goal for
- organizing an application according to the tiers is to embed what is known
- as "separation of concerns" within the system. These tiers can be
- conceptual but may prove effective in mapping the deployment of the
- artifacts onto physical components like Java runtimes and integration with
- data sources and targets. Effective separation of concerns results in
- reducing the impact of change to the system. The four conceptual tiers
- containing batch artifacts are:
-
-
-
- Run Tier: The Run Tier is
- concerned with the scheduling and launching of the application. A
- vendor product is typically used in this tier to allow time-based
- and interdependent scheduling of batch jobs as well as providing
- parallel processing capabilities.
-
-
-
- Job Tier: The Job Tier is
- responsible for the overall execution of a batch job. It
- sequentially executes batch steps, ensuring that all steps are in
- the correct state and all appropriate policies are enforced.
-
-
-
- Application Tier: The
- Application Tier contains components required to execute the
- program. It contains specific tasks that address required batch
- functionality and enforces policies around execution (e.g., commit
- intervals, capture of statistics, etc.)
-
-
-
- Data Tier: The Data Tier
- provides integration with the physical data sources that might
- include databases, files, or queues.
-
-
-
-
-
- Job Stereotypes
+ Job
This section describes stereotypes relating to the concept of a
batch job. A Job is an entity that encapsulates an
@@ -138,7 +77,8 @@
-
+
@@ -147,51 +87,40 @@
-
- Job
+ In Spring Batch, a Job is simply a continer for Steps. 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:
- A job is represented by a Spring bean that implements the
- Job interface and contains all of the information
- necessary to define the operations performed by a job. A job
- configuration is typically contained within a Spring XML configuration
- file and the job's name is determined by the "id" attribute associated
- with the job configuration bean. The job configuration contains
+
+
+ The simple name of the job
+
-
-
- The simple name of the job
-
+
+ Definition and ordering of Steps
+
-
- Definition and ordering of Steps
-
+
+ Whether or not the job is restartable
+
+
-
- Whether or not the job is restartable
-
-
+ 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 instaniate it directly. Instead, the
+ <job> tag can be used:
- 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, namely a standard
- execution logic that all jobs should utilize. In general, all jobs
- should be defined using a bean of type
- SimpleJob:
+
+ <job id="footballJob">
+ <step name="playerload" next="gameLoad"/>
+ <step name="gameLoad" next="playerSummarization"/>
+ <step name="playerSummarization"/>
+ </job>
- <bean id="footballJob"
- class="org.springframework.batch.core.job.SimpleJob">
- <property name="steps">
- <list>
- <!-- Step Bean details ommitted for clarity -->
- <bean id="playerload" parent="simpleStep" />
- <bean id="gameLoad" parent="simpleStep" />
- <bean id="playerSummarization" parent="simpleStep" />
- </list>
- </property>
- <property name="restartable" value="true" />
- </bean>
-
+
JobInstance
@@ -238,13 +167,28 @@
another?" The answer is: JobParameters.
JobParameters 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. In the example above, where there are two
- instances, one for January 1st, and another for January 2nd, there is
- really only one Job, one that was started with a job parameter of
- 01-01-2008 and another that was started with a parameter of 01-02-2008.
- Thus, the contract can be defined as: JobInstance
- = Job + JobParameters.
- This allows a developer to effectively control how you a
+ reference data during the run:
+
+
+
+
+
+
+
+
+
+
+
+ In the example above, where there are two instances, one for
+ January 1st, and another for January 2nd, there is really only one Job,
+ one that was started with a job parameter of 01-01-2008 and another that
+ was started with a parameter of 01-02-2008. Thus, the contract can be
+ defined as: JobInstance =
+ Job + JobParameters. This
+ allows a developer to effectively control how a
JobInstance is defined, since they control what
parameters are passed in.
@@ -258,10 +202,10 @@
JobInstance corresponding to a given execution
will not be considered complete unless the execution completes
successfully. Using the EndOfDay Job described
- above as an example, consider a JobInstance for 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 JobExecution will be
- created. However, there will still be only one
+ above as an example, consider a JobInstance for
+ 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
+ JobExecution will be created. However, there will still be only one
JobInstance.
A Job defines what a job is and how it is
@@ -334,6 +278,15 @@
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.
+
@@ -573,26 +526,29 @@
- Step Stereotypes
+ 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 should be thought of as a unique processing
- stream that will be executed in sequence. For example, if you have one
- step that loads a file into a database, another that reads from the
- database, validates the data, preforms processing, and then writes to
- another table, and another that reads from that table and writes out to a
- file. Each of these steps will be performed completely before moving on to
- the next step. The file will be completely read into the database before
- step 2 can begin. As with Job, 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 corresponds with a unique
JobExecution:
-
+
@@ -601,30 +557,6 @@
-
- Step
-
- 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.
-
- Steps are defined by instantiating implementations of the
- Step interface. Two step implementation classes
- are available in the Spring Batch framework, and they are each discussed
- in detail in Chatper 4 of this guide. For most situations, the
- StepHandlerStep implementation is sufficient, but
- for situations where only one call is needed, such as a stored procedure
- call or a wrapper around existing script, a
- TaskletStep may be a better option.
-
-
StepExecution
@@ -753,181 +685,173 @@
+
-
- ExecutionContext
+
+ 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 or
- JobExecution. For those familiar with Quartz, it
- is very similar to JobDataMap. The best usage
- example is restart. Using flat file input as an example, while
- processing individual lines, the framework periodically persists the
- ExecutionContext at commit points. This 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, 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 or
+ JobExecution. 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. This 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, and the
+ framework will do the rest:
- executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());
+ executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());
- Using the EndOfDay example from the Job Stereotypes section as an
- example, assume there's one step: 'loadData', that loads a file into the
- database. After the first failed run, the meta data tables would look
- like the following:
+ Using the EndOfDay example from the Job Stereotypes section as an
+ example, assume there's one step: 'loadData', that loads a file into the
+ database. After the first failed run, the meta data tables would look like
+ the following:
-
- BATCH_JOB_INSTANCE
+
+ BATCH_JOB_INSTANCE
-
-
-
- JOB_INSTANCE_ID
+
+
+
+ JOB_INSTANCE_ID
- JOB_NAME
-
+ JOB_NAME
+
-
- 1
+
+ 1
- EndOfDayJob
-
-
-
-
- BATCH_JOB_PARAMS
+ EndOfDayJob
+
+
+
+
+ BATCH_JOB_PARAMS
-
-
-
- JOB_INSTANCE_ID
+
+
+
+ JOB_INSTANCE_ID
- TYPE_CD
+ TYPE_CD
- KEY_NAME
+ KEY_NAME
- DATE_VAL
-
+ DATE_VAL
+
-
- 1
+
+ 1
- DATE
+ DATE
- schedule.Date
+ schedule.Date
- 2008-01-01 00:00:00
-
-
-
-
- BATCH_JOB_EXECUTION
+ 2008-01-01 00:00:00
+
+
+
+
+ BATCH_JOB_EXECUTION
-
-
-
- JOB_EXECUTION_ID
+
+
+
+ JOB_EXECUTION_ID
- JOB_INSTANCE_ID
+ JOB_INSTANCE_ID
- START_TIME
+ START_TIME
- END_TIME
+ END_TIME
- STATUS
-
+ STATUS
+
-
- 1
+
+ 1
- 1
+ 1
- 2008-01-01 21:00:23.571
+ 2008-01-01 21:00:23.571
- 2008-01-01 21:30:17.132
+ 2008-01-01 21:30:17.132
- FAILED
-
-
-
-
- BATCH_STEP_EXECUTION
+ FAILED
+
+
+
+
+ BATCH_STEP_EXECUTION
-
-
-
- STEP_EXECUTION_ID
+
+
+
+ STEP_EXECUTION_ID
- JOB_EXECUTION_ID
+ JOB_EXECUTION_ID
- STEP_NAME
+ STEP_NAME
- START_TIME
+ START_TIME
- END_TIME
+ END_TIME
- STATUS
-
+ STATUS
+
-
- 1
+
+ 1
- 1
+ 1
- loadDate
+ loadDate
- 2008-01-01 21:00:23.571
+ 2008-01-01 21:00:23.571
- 2008-01-01 21:30:17.132
+ 2008-01-01 21:30:17.132
- FAILED
-
-
-
-
- BATCH_EXECUTION_CONTEXT
+ FAILED
+
+
+
+
+ BATCH_STEP_EXECUTION_CONTEXT
-
-
-
- EXECUTION_ID
+
+
+
+ STEP_EXECUTION_ID
- TYPE_CD
+ SHORT_CONTEXT
+
- KEY_NAME
+
+ 1
- LONG_VAL
-
+ {piece.count=40321}
+
+
+
+
In this case, the Step ran for 30 minutes
+ and processed 40,321 'pieces', which would represent lines in a file in
+ this scenario. This value will be 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 StepListeners, 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, and when the ItemReader is
+ opened, it can check to see if it has any stored state in the context, and
+ initialize itself from there:
-
- 1
-
- LONG
-
- piece.count
-
- 40321
-
-
-
-
In this case, the Step ran for 30
- minutes and processed 40,321 'pieces', which would represent lines in a
- file in this scenario. This value will be 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 StepListeners, 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, and when the
- ItemReader is opened, it can check to see if it
- has any stored state in the context, and initialize itself from
- there:
-
- if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
+ if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
log.debug("Initializing for restart. Restart data is: " + executionContext);
long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));
@@ -940,70 +864,75 @@
}
}
- In this case, after the above code is executed, the current line
- will be 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 than the number
- of lines read) so that an email can be sent at the end of the
- Step with the total 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, so 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 is executed, the current line
+ will be 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 than the number of lines
+ read) so that an email can be sent at the end of the
+ Step with the total 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, so 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
+ code snippet:
+
+
+ ExecutionContext ecStep = stepExecution.getExecutionContext();
+ ExecutionContext ecJob = jobExecution.getExecutionContext();
+ //ecStep does not equal ecJob
+
+
+
+ As noted in the comment, ecStep will not equal ecJob, they are two
+ different ExecutionContexts. The one scoped to the
+ Step will be saved at every commit point in the
+ Step, wheras the one scoped to the
+ Job will be saved in between every
+ Step execution.
JobRepository
JobRepository is the persistence mechanism
- for all of the Stereotypes mentioned above. When a job is first launched,
- a JobExecution is obtained by calling the
- repository's createJobExecution method, and
- during the course of execution, StepExecution and
- JobExecution are persisted by passing them to the
- repository:
+ 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:
- public interface JobRepository {
+
+ <job-repository id="jobRepository"/>
- public JobExecution createJobExecution(Job job, JobParameters jobParameters)
- throws JobExecutionAlreadyRunningException, JobRestartException;
-
- void add(StepExecution stepExecution);
-
- void update(JobExecution jobExecution);
-
- void update(StepExecution stepExecution);
-
- void updateExecutionContext(StepExecution stepExecution);
-
- StepExecution getLastStepExecution(JobInstance jobInstance, Step step);
-
- int getStepExecutionCount(JobInstance jobInstance, Step step);
-
-}
@@ -1027,25 +956,6 @@
Job.
-
- JobLocator
-
- JobLocator represents an interface for
- locating a Job:
-
- public interface JobLocator {
-
- Job getJob(String name) throws NoSuchJobException;
- }
-
- This interface is very necessary due to the nature of Spring itself.
- Because it can't be guaranteed that one
- ApplicationContext equals one
- Job, an abstraction is needed to obtain a
- Job for a given name. It becomes especially useful
- when launching jobs from within a Java EE application server.
-
-
Item Reader
@@ -1080,15 +990,4 @@
that it's not valid, returning null indicates that it should not be
written out.
-
-
- Tasklet
-
- A Tasklet represents the execution of a
- logical unit of work, as defined by its implementation of the Spring Batch
- provided Tasklet interface. A
- Tasklet is useful for encapsulating processing
- logic that is not natural to split into read-(transform)-write phases,
- such as invoking a system command or a stored procedure.
-
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/execution.xml b/docs/src/site/docbook/reference/execution.xml
index 24484b117..b0469a99b 100644
--- a/docs/src/site/docbook/reference/execution.xml
+++ b/docs/src/site/docbook/reference/execution.xml
@@ -13,8 +13,7 @@
+ fileref="images/spring-batch-reference-model.png" />
@@ -457,8 +456,8 @@
</bean>
The databaseType property indicates the type of incrementer that
- must be used. Options include: "db2", "db2zos", "derby", "hsql", "mysql",
- "oracle", and "postgres".
+ must be used. Options include: "db2", "db2zos", "derby", "hsql",
+ "mysql", "oracle", and "postgres".
@@ -1152,9 +1151,9 @@
<property name="retryableExceptionClasses" value="org.springframework.dao.DeadlockLoserDataAccessException" />
</bean>
- The SkipLimitStepFactoryBean requires
- a limit for the number of times an individual item can be retried, and
- a list of Exceptions that are 'retryable'.
+ The SkipLimitStepFactoryBean requires a
+ limit for the number of times an individual item can be retried, and a
+ list of Exceptions that are 'retryable'.
@@ -1421,11 +1420,11 @@
<property name="jobRepository" ref="repository" />
</bean>
-
- TaskletStep will automatically register the tasklet as
- StepExecutionListener if it implements
- this interface
-
+
+ TaskletStep will automatically register the tasklet as
+ StepExecutionListener if it implements this
+ interface
+
TaskletAdapter
@@ -1565,16 +1564,14 @@
Logging Item Processing and Failures
- A common use case is the need for special handling of
- errors in a step, item by item, perhaps logging to a special
- channel, or inserting a record into a
- database. The StepHandlerStep (created
- from the step factory beans) allows users to implement this use
- case with a simple
- ItemReadListener, for errors on read, and an
- ItemWriteListener, for errors on write. The below
- code snippets illustrate a listener that logs both read and write
- failures:
+ A common use case is the need for special handling of errors in a
+ step, item by item, perhaps logging to a special channel, or inserting a
+ record into a database. The StepHandlerStep
+ (created from the step factory beans) allows users to implement this use
+ case with a simple ItemReadListener, for errors
+ on read, and an ItemWriteListener, for errors on
+ write. The below code snippets illustrate a listener that logs both read
+ and write failures:
public class ItemFailureLoggerListener extends ItemListenerSupport {
@@ -1771,4 +1768,4 @@
itself.
-
+
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/images/chunk-oriented-processing.png b/docs/src/site/docbook/reference/images/chunk-oriented-processing.png
new file mode 100644
index 000000000..5098aca16
Binary files /dev/null and b/docs/src/site/docbook/reference/images/chunk-oriented-processing.png differ
diff --git a/docs/src/site/docbook/reference/images/conditional-flow.png b/docs/src/site/docbook/reference/images/conditional-flow.png
new file mode 100644
index 000000000..68c7c573d
Binary files /dev/null and b/docs/src/site/docbook/reference/images/conditional-flow.png differ
diff --git a/docs/src/site/docbook/reference/images/job-heirarchy.png b/docs/src/site/docbook/reference/images/job-heirarchy.png
index a6c201c7f..afead4323 100644
Binary files a/docs/src/site/docbook/reference/images/job-heirarchy.png and b/docs/src/site/docbook/reference/images/job-heirarchy.png differ
diff --git a/docs/src/site/docbook/reference/images/job-launcher-sequence-async.png b/docs/src/site/docbook/reference/images/job-launcher-sequence-async.png
index f3886040f..8d7ab5d21 100644
Binary files a/docs/src/site/docbook/reference/images/job-launcher-sequence-async.png and b/docs/src/site/docbook/reference/images/job-launcher-sequence-async.png differ
diff --git a/docs/src/site/docbook/reference/images/job-launcher-sequence-sync.png b/docs/src/site/docbook/reference/images/job-launcher-sequence-sync.png
index 126e56fbd..7e7547d33 100644
Binary files a/docs/src/site/docbook/reference/images/job-launcher-sequence-sync.png and b/docs/src/site/docbook/reference/images/job-launcher-sequence-sync.png differ
diff --git a/docs/src/site/docbook/reference/images/job-stereotypes-parameters.png b/docs/src/site/docbook/reference/images/job-stereotypes-parameters.png
new file mode 100644
index 000000000..d9465b5e1
Binary files /dev/null and b/docs/src/site/docbook/reference/images/job-stereotypes-parameters.png differ
diff --git a/docs/src/site/docbook/reference/images/jobHeirarchyWithSteps.png b/docs/src/site/docbook/reference/images/jobHeirarchyWithSteps.png
index 912e32616..d8c55746e 100644
Binary files a/docs/src/site/docbook/reference/images/jobHeirarchyWithSteps.png and b/docs/src/site/docbook/reference/images/jobHeirarchyWithSteps.png differ
diff --git a/docs/src/site/docbook/reference/images/sequential-flow.png b/docs/src/site/docbook/reference/images/sequential-flow.png
new file mode 100644
index 000000000..0036b04db
Binary files /dev/null and b/docs/src/site/docbook/reference/images/sequential-flow.png differ
diff --git a/docs/src/site/docbook/reference/images/spring-batch-layers.png b/docs/src/site/docbook/reference/images/spring-batch-layers.png
index ff1764074..136617340 100644
Binary files a/docs/src/site/docbook/reference/images/spring-batch-layers.png and b/docs/src/site/docbook/reference/images/spring-batch-layers.png differ
diff --git a/docs/src/site/docbook/reference/images/spring-batch-reference-model.png b/docs/src/site/docbook/reference/images/spring-batch-reference-model.png
index 0da2b3bbc..7062d470b 100644
Binary files a/docs/src/site/docbook/reference/images/spring-batch-reference-model.png and b/docs/src/site/docbook/reference/images/spring-batch-reference-model.png differ
diff --git a/docs/src/site/docbook/reference/images/step.png b/docs/src/site/docbook/reference/images/step.png
new file mode 100644
index 000000000..1a321fafe
Binary files /dev/null and b/docs/src/site/docbook/reference/images/step.png differ
diff --git a/docs/src/site/docbook/reference/index.xml b/docs/src/site/docbook/reference/index.xml
index f16e82c2d..890d1edc0 100644
--- a/docs/src/site/docbook/reference/index.xml
+++ b/docs/src/site/docbook/reference/index.xml
@@ -48,17 +48,21 @@
-
+
+
+
+
+
-
-
+
+
diff --git a/docs/src/site/docbook/reference/job.xml b/docs/src/site/docbook/reference/job.xml
index adbbef628..92efd3f85 100644
--- a/docs/src/site/docbook/reference/job.xml
+++ b/docs/src/site/docbook/reference/job.xml
@@ -1,281 +1,253 @@
-
- Configuring and Executing A Job
+
+ Configuring and Running A Job
+
+ In Chapter 2, the overall architecture design was discussed, using the
+ following diagram as a guide:
+
+
+
+
+
+
+
+
+
+
+
+ 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
+ Job 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 Job.
- Introduction
+ Configuring a Job
- In Chapter 2, the overall architecture design was discussed, using
- the following diagram as a guide:
+ The only current implementation of the Job
+ interface is SimpleJob. Since a
+ Job is just a simple loop through a list of Steps,
+ this implementation should be sufficient for the majority of needs. It has
+ only three required dependencies: a name,
+ JobRepository, and a list of Steps.
-
-
-
-
+
+ <job id="footballJob">
+ <step name="playerload" next="gameLoad"/>
+ <step name="gameLoad" next="playerSummarization"/>
+ <step name="playerSummarization"/>
+ </job>
-
-
-
-
+
- When viewed from left to right, the diagram describes a basic flow
- for the execution of a batch job:
+ The namespace defaults to referencing a repository with an id of
+ 'jobRepository', which is a sensible default. However, this can be
+ overriden explicitely:
-
-
- A Scheduler kicks off a job script (usually some form of shell
- script)
-
+
+ <job id="footballJob" repository="specialRepository">
+ <step name="playerload" next="gameLoad"/>
+ <step name="gameLoad" next="playerSummarization"/>
+ <step name="playerSummarization"/>
+ </job>
-
- The script sets up the classpath appropriately, and starts the
- Java process. In most cases, using
- CommandLineJobRunner as the entry point
-
-
-
- The JobRunner finds the Job using the
- JobLocator, pulls together the
- JobParameters and launches the
- Job
-
-
-
- The JobLauncher retrieves a
- JobExecution from the
- JobRepository, and executes the
- Job
-
-
-
- The Job executes each
- Step.
-
-
-
- When execution is complete, the Step
- returns control back to the Job, and if no more
- steps exist, control is returned back to the original caller, in this
- case, the scheduler.
-
-
-
- This flow is perhaps a bit overly simplified, but describes the
- complete flow in the most basic terms. From here, each tier will be
- described in detail, using actual implementations and examples.
-
-
-
- JobLauncher
-
- The most basic implementation of the
- JobLauncher interface is the SimpleJobLauncher.
- It's only required dependency is a JobRepository,
- in order to obtain an execution:
-
- <bean id="jobLauncher"
- class="org.springframework.batch.execution.launch.SimpleJobLauncher">
- <property name="jobRepository" ref="jobRepository" />
- </bean>
-
- Once a JobExecution is obtained, it is passed
- to the execute method of Job, ultimately returning
- the JobExecution to the caller:
-
-
-
-
-
-
-
-
-
-
-
- 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 SimpleJobLauncher 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:
-
-
-
-
-
-
-
-
-
-
-
- The SimpleJobLauncher can easily be
- configured to allow for this scenario by configuring a
- TaskExecutor:
-
- <bean id="jobLauncher"
- class="org.springframework.batch.execution.launch.SimpleJobLauncher">
- <property name="jobRepository" ref="jobRepository" />
- <property name="taskExecutor">
- <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
- </property>
- </bean>
-
- Any implementation of the spring TaskExecutor
- interface can be used to control how jobs are asynchronously
- executed.
-
-
-
- JobRepository
-
- The SimpleJobRepository is the only provided implementation of the
- JobRepository interface. It completely manages the
- various batch domain objects and ensures they are created and persisted
- correctly. The SimpleJobRepository uses three
- different DAO interfaces for the three major domain types it stores:
- JobInstanceDao,
- JobExecutionDao, and
- StepExecutionDao. The repository delegates to these
- DAOs to both persist the various domain objects and query for them during
- initialization. The following configuration shows a SimpleJobRepository
- configured with JDBC DAOs:
-
- <bean id="jobRepository" class="org.springframework.batch.core.repository.support.SimpleJobRepository">
- <constructor-arg ref="jobInstanceDao" />
- <constructor-arg ref="jobExecutionDao" />
- <constructor-arg ref="stepExecutionDao" />
- </bean>
-
- <bean id="jobInstanceDao" class="org.springframework.batch.core.repository.support.dao.JdbcJobInstanceDao" >
- <property name="jdbcTemplate" ref="jdbcTemplate" />
- <property name="jobIncrementer" ref="jobIncrementer" />
- </bean>
-
- <bean id="jobExecutionDao" class="org.springframework.batch.core.repository.support.dao.JdbcJobExecutionDao" >
- <property name="jdbcTemplate" ref="jdbcTemplate" />
- <property name="jobExecutionIncrementer" ref="jobExecutionIncrementer" />
- </bean>
-
- <bean id="stepExecutionDao" class="org.springframework.batch.core.repository.support.dao.JdbcStepExecutionDao" >
- <property name="jdbcTemplate" ref="jdbcTemplate" />
- <property name="stepExecutionIncrementer" ref="stepExecutionIncrementer" />
- </bean>
-
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- The configuration above isn't quite complete, each DAO
- implementation makes a reference to a Spring
- DataFieldMaxValueIncrementer.
- JobInstance, JobExecution,
- and StepExecution each have unique IDs, and the
- incrementers are used to create them.
+
- JobRepositoryFactoryBean
+ Restartability
- Including the incrementers, which must be database specific, the
- configuration above is verbose. In order to make this more manageable,
- the framework provides a FactoryBean for
- convenience: JobRepositoryFactoryBean.
+ 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 JobInstance 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. It is entirely up to
+ the developer to ensure that a new instance is always created in this
+ scenario. However, Spring Batch does provide some help. If a
+ Job should never be restarted, but should always be run as part of a new
+ JobInstance, then the restartable property may be
+ set to 'false':
- <bean id="jobRepository"
- class="org.springframework.batch.execution.repository.JobRepositoryFactoryBean"
- <property name="databaseType" value="hsql" />
- <property name="dataSource" ref="dataSource" />
- <property name="transactionManager" ref="transactionManager" />
- </bean>
+
+ <job id="footballJob" restartable="false">
+ <step name="playerload" next="gameLoad"/>
+ <step name="gameLoad" next="playerSummarization"/>
+ <step name="playerSummarization"/>
+ </job>
- The databaseType property indicates the type of incrementer that
- must be used. Options include: "db2", "db2zos", "derby", "hsql",
- "mysql", "oracle", and "postgres".
+
+
+ To phrase it another way, setting restartable to false means "this
+ Job does not support being started again". Restarting a Job that is not
+ restartable will cause a JobRestartException to
+ be thrown:
+
+
+ Job job = new SimpleJob();
+ job.setRestartable(false);
+
+ JobParameters jobParameters = new JobParameters();
+
+ JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
+ jobRepository.saveOrUpdate(firstExecution);
+
+ try {
+ jobRepository.createJobExecution(job, jobParameters);
+ fail();
+ }
+ catch (JobRestartException e) {
+ // expected
+ }
+
+
+
+ This snippet of JUnit code shows how attempting to create a
+ JobExecution the first time for a non restartable
+ job will cause no issues. However, the second
+ attempt will throw a JobRestartException.
- In-Memory Repository
+ Intercepting Job execution
- There are scenarios in which you may not want to persist your
- 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. Spring
- batch provides a solution:
+ During the course of the execution of a
+ Job, it may be useful to be notified of various
+ events in its lifecycle so that custom code may be executed. The
+ SimpleJob allows for this by calling a
+ JobListener at the appropriate time:
- <bean id="simpleJobRepository" class="org.springframework.batch.core.repository.support.SimpleJobRepository">
- <constructor-arg ref="mapJobInstanceDao" />
- <constructor-arg ref="mapJobExecutionDao" />
- <constructor-arg ref="mapStepExecutionDao" />
- </bean>
+
+ public interface JobExecutionListener {
- <bean id="mapJobInstanceDao"
- class="org.springframework.batch.core.repository.dao.MapJobInstanceDao" />
+ void beforeJob(JobExecution jobExecution);
- <bean id="mapJobExecutionDao"
- class="org.springframework.batch.core.repository.dao.MapJobExecutionDao" />
+ void afterJob(JobExecution jobExecution);
- <bean id="mapStepExecutionDao"
- class="org.springframework.batch.core.repository.dao.MapStepExecutionDao" />
+ }
- The Map* DAO implementations store the batch artifacts in a
- transactional map. So, the repository and DAOs may still be used
- normally, and are transactionally sound, but their contents will be lost
- when the class is destroyed.
+
- There is also a separate FactoryBean for the in-memory
- JobRepository, which reduces the amount of
- configuration required:
+ Listeners can be added to a SimpleJob via
+ the setJobListeners property:
- <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
+
+ <job id="footballJob">
+ <step name="playerload" next="gameLoad"/>
+ <step name="gameLoad" next="playerSummarization"/>
+ <step name="playerSummarization"/>
+ <listeners>
+ <listener class="org.springframework.batch.sample.SampleListener"/>
+ </listeners>
+ </job>
-
- Transaction Configuration For the JobRepository
+
- If either of the JobRepository factory beans are 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 behaviour of the framework is not well defined if the
- repository methods are not transactional. The isolation level in the
- create* method attributes is specified separately to
- ensure that when jobs are launched there if two processes are trying
- to launch the same job at the same time, only one will succeed. The
- default isolation level for that method is SERIALIZABLE, which is
- quite aggressive: READ_COMMITTED would work just as well;
- READ_UNCOMMITTED would be fine if two processes are not likely to
- collide in this way. However, since a call to the
- create* method is quite short, it is unlikely
- that the SERIALIZED will cause problems, as long as the database
- platform supports it. However, this can be overriden in the factory
- beans:
+ It should be noted that afterJob will be called regardless of the
+ success or failure of the Job. If success or
+ failure needs to be determined it can be obtained from the
+ JobExecution:
- <bean id="jobRepository"
- class="org.springframework.batch.execution.repository.JobRepositoryFactoryBean"
- <property name="databaseType" value="hsql" />
- <property name="dataSource" ref="dataSource" />
- <property name="transactionManager" ref="transactionManager" />
- <property name="IsolationLevelForCreate" value="ISOLATION_REPEATABLE_READ" />
- </bean>
+
+ void afterJob(JobExecution jobExecution){
+ if( jobExecution.getStatus = BatchStatus.COMPLETED ){
+ //job success
+ }
+ else if(jobExecution.getStatus = BatchStatus.FAILED){
+ //job failure
+ }
- If the factory beans aren't used then it is also essential to
- configure the transactional behaviour of the repository using
- AOP:
+
+
-
+
+ JobFactory and Stateful Components in Steps
+
+ Unlike many traditional Spring applications, many of the
+ 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 ApplicationContext for each job
+ execution. If the Job is launched from the
+ command line with CommandLineJobRunner 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 ApplicationContext 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)
+
+ The strategy provided by Spring Batch to deal with this scenario
+ is the JobFactory, and the samples provide an
+ example of a specialized implementation that can load an
+ ApplicationContext and close it properly when the
+ job is finished. A relevant examples is
+ ClassPathXmlApplicationContextJobFactory and its
+ use in the adhoc-job-launcher-context.xml and the
+ quartz-job-launcher-context.xml, which can be found in the
+ Samples project.
+
+
+
+
+ Configuring a JobRepository
+
+ As described in Chatper 2, the JobRepository 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 JobLauncher,
+ Job, and Step. The batch
+ namespace abstract much of the implementation details of the JobRepository
+ implementations and their collaborators. However, there are still a few
+ configuration options available:
+
+
+ <job-repository id="jobRepository"
+ dataSource="dataSource"
+ transactionManager="transactionManager"
+ isolation-level-for-create="serializable"
+ table-prefix="BATCH_"
+ />
+
+
+
+ None of the configuration options listed above are required except
+ the id. If they are not set, the defaults shown above will be used. They
+ are shown above for awareness purposes.
+
+
+ Transaction Configuration For the JobRepository
+
+ 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 behaviour of the framework is not
+ well defined if the repository methods are not transactional. The
+ isolation level in the create* method attributes is
+ specified separately to ensure that when jobs are launched, if two
+ processes are trying to launch the same job at the same time, only one
+ will succeed. The default isolation level for that method is
+ SERIALIZABLE, which is quite aggressive: READ_COMMITTED would work just
+ as well; READ_UNCOMMITTED would be fine if two processes are not likely
+ to collide in this way. However, since a call to the
+ create* method is quite short, it is unlikely
+ that the SERIALIZED will cause problems, as long as the database
+ platform supports it. However, this can be overriden:
+
+
+ <job-repository id="jobRepository"
+ isolation-level-for-create="ISOLATION_REPEATABLE_READ" />
+
+
+
+ If the namespace or factory beans aren't used then it is also
+ essential to configure the transactional behaviour of the repository
+ using AOP:
+
+
<aop:config>
<aop:advisor
pointcut="execution(* org.springframework.batch.core..*Repository+.*(..))"
@@ -290,11 +262,10 @@
- This fragment can be used as is, with almost no changes.
- Remember also to include the appropiate namespace declarations and to
- make sure spring-tx and spring-aop (or the whole of spring) is on the
- classpath.
-
+ This fragment can be used as is, with almost no changes. Remember
+ also to include the appropiate namespace declarations and to make sure
+ spring-tx and spring-aop (or the whole of spring) is on the
+ classpath.
Recommendations for Indexing Meta Data Tables
@@ -369,175 +340,131 @@
+
+
+ Changing the table prefix
+
+ Another modifiable property of the
+ JobRepository is the table prefix of the
+ meta-data tables. By default they are all prefaced with BATCH_.
+ BATCH_JOB_EXECUTION and BATCH_STEP_EXECUTION are two examples. However,
+ there are potential reasons to modify this prefix. If the schema names
+ needs to be prepended to the table names, or if more than one set of
+ meta data tables is needed within the same schema, then the table prefix
+ will need to be changed:
+
+
+ <job-repository id="jobRepository"
+ table-prefix="SYSTEM.TEST_"
+ />
+
+
+
+ Given the above changes, every query to the meta data tables will
+ be prefixed with "SYSTEM.TEST_". BATCH_JOB_EXECUTION will be referred to
+ as SYSTEM.TEST_JOB_EXECUTION.
+
+
+ Only the table prefix is configurable, the table and column
+ names are not.
+
+
+
+
+ In-Memory Repository
+
+ There are scenarios in which you may not want to persist your
+ 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. Spring
+ batch provides a solution:
+
+ <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
+
- Job
+ Configuring a JobLauncher
- The only current implementation of the Job
- interface is SimpleJob. Since a
- Job is just a simple loop through a list of Steps,
- this implementation should be sufficient for the majority of needs. It has
- only three required dependencies: a name,
- JobRepository, and a list of Steps.
+ The most basic implementation of the
+ JobLauncher interface is the SimpleJobLauncher.
+ It's only required dependency is a JobRepository,
+ in order to obtain an execution:
- <bean id="footballJob"
- class="org.springframework.batch.core.job.SimpleJob">
- <property name="steps">
- <list>
- <!-- Step Bean details ommitted for clarity -->
- <bean id="playerload" parent="simpleStep" />
- <bean id="gameLoad" parent="simpleStep" />
- <bean id="playerSummarization" parent="simpleStep" />
- </list>
- </property>
+ <bean id="jobLauncher"
+ class="org.springframework.batch.execution.launch.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
- Each Step will be executed in sequence until
- all have completed successfully. Any Step that fails will cause the entire
- job to fail.
+ Once a JobExecution is obtained, it is passed
+ to the execute method of Job, ultimately returning
+ the JobExecution to the caller:
-
- Restartability
+
+
+
+
- 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 JobInstance 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. It is entirely up to
- the developer to ensure that a new instance is always created in this
- scenario. However, Spring Batch does provide some help. If a
- Job should never be restarted, but should always be run as part of a new
- JobInstance, then the restartable property may be
- set to 'false':
+
+
+
+
- <bean id="footballJob"
- class="org.springframework.batch.core.job.SimpleJob">
- <property name="steps">
- <list>
- <!-- Step Bean details ommitted for clarity -->
- <bean id="playerload" parent="simpleStep" />
- <bean id="gameLoad" parent="simpleStep" />
- <bean id="playerSummarization" parent="simpleStep" />
- </list>
- </property>
+ 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 SimpleJobLauncher 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:
+
+
+
+
+
+
+
+
+
+
+
+ The SimpleJobLauncher can easily be
+ configured to allow for this scenario by configuring a
+ TaskExecutor:
+
+ <bean id="jobLauncher"
+ class="org.springframework.batch.execution.launch.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
- <property name="restartable" value="false" />
+ <property name="taskExecutor">
+ <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
+ </property>
</bean>
- To phrase it another way, setting restartable to false means "this
- Job does not support being started again". Restarting a Job that is not
- restartable will cause a JobRestartException to
- be thrown:
-
- Job job = new SimpleJob();
- job.setRestartable(false);
-
- JobParameters jobParameters = new JobParameters();
-
- JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
- jobRepository.saveOrUpdate(firstExecution);
-
- try {
- jobRepository.createJobExecution(job, jobParameters);
- fail();
- }
- catch (JobRestartException e) {
- // expected
- }
-
- This snippet of JUnit code shows how attempting to create a
- JobExecution the first time for a non restartable
- job will cause no issues. However, the second
- attempt will throw a JobRestartException.
-
-
-
- Intercepting Job execution
-
- During the course of the execution of a
- Job, it may be useful to be notified of various
- events in its lifecycle so that custom code may be executed. The
- SimpleJob allows for this by calling a
- JobListener at the appropriate time:
-
- public interface JobListener {
-
- void beforeJob(JobExecution jobExecution);
-
- void afterJob(JobExecution jobExecution);
-
- void onError(JobExecution jobExecution, Throwable e);
-
- void onInterrupt(JobExecution jobExecution);
- }
-
- Listeners can be added to a SimpleJob via
- the setJobListeners property:
-
- <bean id="footballJob"
- class="org.springframework.batch.core.job.SimpleJob">
- <property name="steps">
- <list>
- <!-- Step Bean details ommitted for clarity -->
- <bean id="playerload" parent="simpleStep" />
- <bean id="gameLoad" parent="simpleStep" />
- <bean id="playerSummarization" parent="simpleStep" />
- </list>
- </property>
- <property name="jobRepository" ref="jobRepository" />
- <property name="jobListeners">
- <bean class="org.springframework.batch.core.listener.JobListenerSupport" />
- </property>
- </bean>
-
-
-
- JobFactory and Stateful Components in Steps
-
- Unlike many traditional Spring applications, many of the
- 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 ApplicationContext for each job
- execution. If the Job is launched from the
- command line with CommandLineJobRunner 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 ApplicationContext 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)
-
- The strategy provided by Spring Batch to deal with this scenario
- is the JobFactory, and the samples provide an
- example of a specialized implementation that can load an
- ApplicationContext and close it properly when the
- job is finished. A relevant examples is
- ClassPathXmlApplicationContextJobFactory and its
- use in the adhoc-job-launcher-context.xml and the
- quartz-job-launcher-context.xml, which can be found in the
- Samples project.
-
+ Any implementation of the spring TaskExecutor
+ interface can be used to control how jobs are asynchronously
+ executed.
Running a Job
- Regardless of whether the originator is a Scheduler or an HTTP
- request, a Job must be obtained, parameters must be parsed, and eventually
- a JobLauncher called:
-
-
-
-
-
-
-
-
-
-
+ At a minimum, launching a batch job requires two things: the Job to
+ be launched and a JobLauncher. 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 JobLauncher. However, if
+ running from within a web container within the scope of an
+ HttpRequest, there will usually be one
+ JobLauncher, configured for asynchronous job
+ launching, that multiple requests will invoke to launch their jobs.
Running Jobs from the Command Line
@@ -765,265 +692,4 @@
-
-
- Job Tier
-
- The Job Tier is responsible for the overall execution of a batch
- job. It sequentially executes batch steps, ensuring that all steps are in
- the correct state and all appropriate policies are enforced:
-
-
-
-
-
-
-
-
-
-
-
- The job tier is entirely concerned with maintaining the three job
- stereotypes: Job,
- JobInstance, and
- JobExecution. The
- JobLauncher interacts with the
- JobRepository in order to create a
- JobExecution, and the Job
- stores the JobExecution using the
- repository.
-
-
-
- Examples of Customized Business Logic
-
-
- Some batch jobs can be assembled purely from off-the-shelf
- components in Spring Batch, mostly the ItemReader
- and ItemWriter implementations. Where this is not
- possible (the majority of cases) the main API entry points for
- application developers are the Tasklet,
- ItemReader, ItemWriter and
- the various listener interfaces. Most simple batch jobs will be able to
- use off-the-shelf input from a Spring Batch
- ItemReader, but it is very often the case that
- there are custom concerns in the processing and writing, which normally
- leads developers to implement an ItemWriter, or
- ItemTransformer.
-
- Here we provide a few examples of common patterns in custom
- business logic, mainly using the listener interfaces . It should be
- noted that an ItemReader or
- ItemWriter can implement the listener interfaces
- as well if appropriate.
-
-
-
- Logging Item Processing and Failures
-
- A common use case is the need for special handling of errors in a
- step, item by item, perhaps logging to a special channel, or inserting a
- record into a database. The StepHandlerStep
- (created from the step factory beans) allows users to implement this use
- case with a simple ItemReadListener, for errors
- on read, and an ItemWriteListener, for errors on
- write. The below code snippets illustrate a listener that logs both read
- and write failures:
-
- public class ItemFailureLoggerListener extends ItemListenerSupport {
-
- private static Log logger = LogFactory.getLog("item.error");
-
- public void onReadError(Exception ex) {
- logger.error("Encountered error on read", e);
- }
-
- public void onWriteError(Exception ex, Object item) {
- logger.error("Encountered error on write", e);
- }
-
-}
-
- Having implemented this listener it must be registered with the
- step:
-
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- ...
- <property name="listeners">
- <bean class="org.example...ItemFailureLoggerListener"/>
- </property>
- </bean>
-
- Remember that if your listener does anything in an
- onError() method, it will be inside a transaction that is
- going to be rolled back. If you need to use a transactional resource
- such as a database inside an onError() method, consider
- adding a declarative transaction to that method (see Spring Core
- Reference Guide for details), and giving its propagation attribute the
- value REQUIRES_NEW.
-
-
-
- Stopping a Job Manually for Business Reasons
-
- Spring Batch provides a stop() method through the JobLauncher
- interface, but this is really aimed at the operator, rather than the
- application programmer. Sometimes it is more convenient or makes more
- sense to stop a job execution from within the business logic.
-
- The simplest thing to do is to throw a RuntimeException (one that
- isn't retried indefinitely or skipped), For example, a custom exception
- type could be used, as in the example below:
-
- public class PoisonPillItemWriter extends AbstractItemWriter {
-
- public void write(Object item) throws Exception {
-
- if (isPoisonPill(item)) {
- throw new PoisonPillException("Posion pill detected: "+item);
- }
-
- }
-
-}
-
- Another simple way to stop a step from executing is to simply
- return null from the
- ItemReader:
-
- public class EarlyCompletionItemReader extends AbstractItemReader {
-
- private ItemReader delegate;
-
- public void setDelegate(ItemReader delegate) { ... }
-
- public Object read() throws Exception {
-
- Object item = delegate.read();
-
- if (isEndItem(item)) {
- return null; // end the step here
- }
-
- return item;
-
- }
-
-}
-
- The previous example actually relies on the fact that there is a
- default implementation of the CompletionPolicy
- strategy which signals a complete batch when the item to be processed is
- null. A more sophisticated completion policy could be implemented and
- injected into the Step through the
- RepeatOperationsStepFactoryBean:
-
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.RepeatOperationsStepFactoryBean" >
- ...
- <property name="chunkOperations">
- <bean class="org.springframework.batch.repeat.support.RepeatTemplate">
- <property name="completionPolicy">
- <bean class="org.example...SpecialCompletionPolicy"/>
- </property>
- </bean>
- </property>
- </bean>
-
- An alternative is to set a flag in the
- StepExecution, which is checked by the
- Step implementations in the framework in between
- item processing. To implement this alternative, we need access to the
- current StepExecution, and this can be achieved by implementing a
- StepListener and registering it with the Step. Here is an example of a
- listener that sets the flag:
-
- public class CustomItemWriter extends ItemListenerSupport implements StepListener {
-
- private StepExecution stepExecution;
-
- public void beforeStep(StepExecution stepExecution) {
- this.stepExecution = stepExecution;
- }
-
- public void afterRead(Object item) {
-
- if (isPoisonPill(item)) {
- stepExecution.setTerminateOnly(true);
- }
-
- }
-
-}
-
- The default behaviour here when the flag is set is for the step to
- throw a JobInterruptedException. This can be
- controlled through the StepInterruptionPolicy,
- but the only choice is to throw or not throw an exception, so this is
- always an abnormal ending to a job.
-
-
-
- Adding a Footer Record
-
- A very common requirement is to aggregate information during the
- output process and to append a record at the end of a file summarizing
- the data, or providing a checksum. This can also be achieved with a
- callbacks in the step, normally as part of a custom
- ItemWriter. In this case, since a job is
- accumulating state that should not be lost if the job aborts, the
- ItemStream interface should be
- implemented:
-
- public class CustomItemWriter extends AbstractItemWriter implements
- ItemStream, StepListener
-{
-
- private static final String TOTAL_AMOUNT_KEY = "total.amount";
-
- private ItemWriter delegate;
-
- private double totalAmount = 0.0;
-
- public void setDelegate(ItemWriter delegate) { ... }
-
- public ExitStatus afterStep(StepExecution stepExecution) {
- // Add the footer record here...
- delegate.write("Total Amount Processed: " + totalAmount);
- }
-
- public void open(ExecutionContext executionContext) {
- if (executionContext.containsKey(TOTAL_AMOUNT_KEY) {
- totalAmount = executionContext.getDouble(TOTAL_AMOUNT_KEY);
- }
- }
-
- public void update(ExecutionContext executionContext) {
- executionContext.setDouble(TOTAL_AMOUNT_KEY, totalAmount);
- }
-
- public void write(Object item) {
-
- delegate.write(item);
- totalAmount += ((Trade) item).getAmount();
-
- }
-
-}
-
- The custom writer in the example is stateful (it maintains its
- total in an instance variable totalAmount), but the
- state is stored through the ItemStream interface
- in the ExecutionContext. In this way we can be
- sure that when the open() callback is received on a
- restart. The framework garuntees we always get the last value that was
- committed. It should be noted that it is not always necessary to
- implement ItemStream. For example, if the ItemWriter is re-runnable, in
- the sense that it maintains its own state in a transactional resource
- like a database, there is no need to maintain state within the writer
- itself.
-
-
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/namespace.xml b/docs/src/site/docbook/reference/namespace.xml
deleted file mode 100644
index 02b35d94b..000000000
--- a/docs/src/site/docbook/reference/namespace.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- Namespace Support
-
-
- Namespace Support
- Document Namespace support here.
-
-
-
diff --git a/docs/src/site/docbook/reference/outline.xml b/docs/src/site/docbook/reference/outline.xml
deleted file mode 100644
index 0a319f87b..000000000
--- a/docs/src/site/docbook/reference/outline.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
- The Spring Batch - Reference Documentation
- Wayne Lund, Waseem Malik, Lucas Ward, Scott Wintermute,
- Kerry O'Brien, Tomi Vanek
- May 2007
-
-
-
-
- Chapter 1: Spring Batch Introduction
- Overview of the Spring Batch Architecture - the Spring Batch Reference Model
-
-
-
- Chapter 2: The Spring Batch Infrastructure
- Infrastructure covers Repeat Template, I/O facilities and the RetryTemplate
-
-
-
- Chapter 3: Spring Batch Core
- Describe the domain language of batch and how the pieces fit together.
-
-
-
- Chapter 4: Spring Batch Execution
- Describe the simple batch execution environment.
-
-
-
- Chapter 5: Spring Batch Applications
- Describe the solution space for spring batch.
-
-
-
- Chapter 6: Spring Batch Samples
- The documentation for samples goes here
-
-
-
- Chapter 7: Unit and Integration Testing Batch Jobs
- The documentation for unit and integration testing of batch jobs goes here.
-
-
-
- Chapter 8: Performance Testing Batch Jobs
- How to performance test batch jobs.
-
-
-
- Chapter 9: Glossary
- (Should this be Appendix A?) The Batch Glossary documents common terms used in the batch processing domain.
-
-
-
- More sections that may come later?
- More advanced info about building Spring Batch, how to contribute, JMS integration, management with JMX, the Spring Batch data model, integration with schedulers (eg quartz)
-
-
-
diff --git a/docs/src/site/docbook/reference/partitioned-containers.xml b/docs/src/site/docbook/reference/partitioned-containers.xml
deleted file mode 100644
index 2f245b529..000000000
--- a/docs/src/site/docbook/reference/partitioned-containers.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Batch IO Support
-
- Partitioned Containers
- Much stuff goes here.
-
-
-
diff --git a/docs/src/site/docbook/reference/spring-batch-intro.xml b/docs/src/site/docbook/reference/spring-batch-intro.xml
index 98152cf19..3628a8d6c 100644
--- a/docs/src/site/docbook/reference/spring-batch-intro.xml
+++ b/docs/src/site/docbook/reference/spring-batch-intro.xml
@@ -4,209 +4,201 @@
Spring Batch Introduction
+ Many applications within the enterprise domain require bulk processing
+ to perform business operations in mission critical environments. These
+ business operations include automated, complex processing of large volumes
+ of information that is most efficiently processed without user interaction.
+ These operations typically include time based events (e.g. month-end
+ calculations, notices or correspondence), periodic application of complex
+ business rules processed repetitively across very large data sets (e.g.
+ Insurance benefit determination or rate adjustments), or the integration of
+ information that is received from internal and external systems that
+ typically requires formatting, validation and processing in a transactional
+ manner into the system of record. Batch processing is used to process
+ billions of transactions every day for enterprises.
+
+ Spring Batch is a lightweight, comprehensive batch framework designed
+ to enable the development of robust batch applications vital for the daily
+ operations of enterprise systems. Spring Batch builds upon the productivity,
+ POJO-based development approach, and general ease of use capabilities people
+ have come to know from the Spring Framework, while making it easy for
+ developers to access and leverage more advance enterprise services when
+ necessary. Spring Batch is not a scheduling framework. There are many good
+ enterprise schedulers available in both the commercial and open source
+ spaces such as Quartz, Tivoli, Control-M, etc. It is intended to work in
+ conjunction with a scheduler, not replace a scheduler.
+
+ Spring Batch provides reusable functions that are essential in
+ processing large volumes of records, including logging/tracing, transaction
+ management, job processing statistics, job restart, skip, and resource
+ management. It also provides more advance technical services and features
+ that will enable extremely high-volume and high performance batch jobs
+ though optimization and partitioning techniques. Simple as well as complex,
+ high-volume batch jobs can leverage the framework in a highly scalable
+ manner to process significant volumes of information.
+
- Introduction
+ Background
- Many applications within the enterprise domain require bulk
- processing to perform business operations in mission critical
- environments. These business operations include automated, complex
- processing of large volumes of information that is most efficiently
- processed without user interaction. These operations typically include
- time based events (e.g. month-end calculations, notices or
- correspondence), periodic application of complex business rules processed
- repetitively across very large data sets (e.g. Insurance benefit
- determination or rate adjustments), or the integration of information that
- is received from internal and external systems that typically requires
- formatting, validation and processing in a transactional manner into the
- system of record. Batch processing is used to process billions of
- transactions every day for enterprises.
+ While open source software projects and associated communities have
+ focused greater attention on web-based and SOA messaging-based
+ architecture frameworks, there has been a notable lack of focus on
+ reusable architecture frameworks to accommodate Java-based batch
+ processing needs, despite continued needs to handle such processing within
+ enterprise IT environments. The lack of a standard, reusable batch
+ architecture has resulted in the proliferation of many one-off, in-house
+ solutions developed within client enterprise IT functions.
- Spring Batch is a lightweight, comprehensive batch framework
- designed to enable the development of robust batch applications vital for
- the daily operations of enterprise systems. Spring Batch builds upon the
- productivity, POJO-based development approach, and general ease of use
- capabilities people have come to know from the Spring Framework, while
- making it easy for developers to access and leverage more advance
- enterprise services when necessary. Spring Batch is not a scheduling
- framework. There are many good enterprise schedulers available in both the
- commercial and open source spaces such as Quartz, Tivoli, Control-M, etc.
- It is intended to work in conjunction with a scheduler, not replace a
- scheduler.
+ SpringSource and Accenture have collaborated to change this.
+ Accenture's hands-on industry and technical experience in implementing
+ batch architectures, SpringSource's depth of technical experience, and
+ Spring's proven programming model together mark a natural and powerful
+ partnership to create high-quality, market relevant software aimed at
+ filling an important gap in enterprise Java. Both companies are also
+ currently working with a number of clients solving similar problems
+ developing Spring-based batch architecture solutions. This has provided
+ some useful additional detail and real-life constraints helping to ensure
+ the solution can be applied to the real-world problems posed by clients.
+ For these reasons and many more, SpringSource and Accenture have teamed to
+ collaborate on the development of Spring Batch.
- Spring Batch provides reusable functions that are essential in
- processing large volumes of records, including logging/tracing,
- transaction management, job processing statistics, job restart, skip, and
- resource management. It also provides more advance technical services and
- features that will enable extremely high-volume and high performance batch
- jobs though optimization and partitioning techniques. Simple as well as
- complex, high-volume batch jobs can leverage the framework in a highly
- scalable manner to process significant volumes of information.
+ Accenture has contributed previously proprietary batch processing
+ architecture frameworks, based upon decades worth of experience in
+ building batch architectures with the last several generations of
+ platforms, (i.e., COBOL/Mainframe, C++/Unix, and now Java/anywhere) to the
+ Spring Batch project along with committer resources to drive support,
+ enhancements, and the future roadmap.
-
- Background
-
- While open source software projects and associated communities
- have focused greater attention on web-based and SOA messaging-based
- architecture frameworks, there has been a notable lack of focus on
- reusable architecture frameworks to accommodate Java-based batch
- processing needs, despite continued needs to handle such processing
- within enterprise IT environments. The lack of a standard, reusable
- batch architecture has resulted in the proliferation of many one-off,
- in-house solutions developed within client enterprise IT
- functions.
-
- SpringSource and Accenture have collaborated to change this.
- Accenture's hands-on industry and technical experience in implementing
- batch architectures, SpringSource's depth of technical experience, and
- Spring's proven programming model together mark a natural and powerful
- partnership to create high-quality, market relevant software aimed at
- filling an important gap in enterprise Java. Both companies are also
- currently working with a number of clients solving similar problems
- developing Spring-based batch architecture solutions. This has provided
- some useful additional detail and real-life constraints helping to
- ensure the solution can be applied to the real-world problems posed by
- clients. For these reasons and many more, SpringSource and Accenture
- have teamed to collaborate on the development of Spring Batch.
-
- Accenture has contributed previously proprietary batch processing
- architecture frameworks, based upon decades worth of experience in
- building batch architectures with the last several generations of
- platforms, (i.e., COBOL/Mainframe, C++/Unix, and now Java/anywhere) to
- the Spring Batch project along with committer resources to drive
- support, enhancements, and the future roadmap.
-
- The collaborative effort between Accenture and SpringSource aims
- to promote the standardization of software processing approaches,
- frameworks, and tools that can be consistently leveraged by enterprise
- users when creating batch applications. Companies and government
- agencies desiring to deliver standard, proven solutions to their
- enterprise IT environments will benefit from Spring Batch.
-
-
-
- Usage Scenarios
-
- A typical batch program generally reads a large number of records
- from a database, file, or queue, processes the data in some fashion, and
- then writes back data in a modified form. Spring Batch automates this
- basic batch iteration, providing the capability to process similar
- transactions as a set, typically in an offline environment without any
- user interaction. Batch jobs are part of most IT projects and Spring
- Batch is the only open source framework that provides a robust,
- enterprise-scale solution.
-
- Business Scenarios
-
- Commit batch process periodically
-
-
-
- Concurrent batch processing: parallel processing of a
- job
-
-
-
- Staged, enterprise message-driven processing
-
-
-
- Massively parallel batch processing
-
-
-
- Manual or scheduled restart after failure
-
-
-
- Sequential processing of dependent steps (with extensions to
- workflow-driven batches)
-
-
-
- Partial processing: skip records (e.g. on rollback)
-
-
-
- Whole-batch transaction: for cases with a small batch size
- or existing stored procedures/scripts
-
-
-
- Technical Objectives
-
- Batch developers use the Spring programming model:
- concentrate on business logic; let the framework take care of
- infrastructure.
-
-
-
- Clear separation of concerns between the infrastructure, the
- batch execution environment, and the batch application.
-
-
-
- Provide common, core execution services as interfaces that
- all projects can implement.
-
-
-
- Provide simple and default implementations of the core
- execution interfaces that can be used ‘out of the box’.
-
-
-
- Easy to configure, customize, and extend services, by
- leveraging the spring framework in all layers.
-
-
-
- All existing core services should be easy to replace or
- extend, without any impact to the infrastructure layer.
-
-
-
- Provide a simple deployment model, with the architecture
- JARs completely separate from the application, built using
- Maven.
-
-
-
-
-
- Spring Batch Architecture
-
- Spring Batch is designed with extensibility and a diverse group of
- end users in mind. The figure below shows a sketch of the layered
- architecture that supports the extensibility and ease of use for
- end-user developers.
-
-
-
-
-
-
-
-
- Figure 1.1: Spring Batch Layered
- Architecture
-
-
- This layered architecture highlights three major high level
- components: Application, Core, and Infrastructure. The application
- contains all batch jobs and custom code written by developers using
- Spring Batch. The Batch Core contains the core runtime classes necessary
- to launch and control a batch job. It includes things such as a
- JobLauncher, Job, and
- Step implementations. Both Application and Core
- are built on top of a common infrastructure. This infrastructure
- contains common readers and writers, and services such as the
- RetryTemplate, which are used both by application
- developers(ItemReader and
- ItemWriter) and the core framework itself.
- (retry)
-
+ The collaborative effort between Accenture and SpringSource aims to
+ promote the standardization of software processing approaches, frameworks,
+ and tools that can be consistently leveraged by enterprise users when
+ creating batch applications. Companies and government agencies desiring to
+ deliver standard, proven solutions to their enterprise IT environments
+ will benefit from Spring Batch.
-
+
+
+ Usage Scenarios
+
+ A typical batch program generally reads a large number of records
+ from a database, file, or queue, processes the data in some fashion, and
+ then writes back data in a modified form. Spring Batch automates this
+ basic batch iteration, providing the capability to process similar
+ transactions as a set, typically in an offline environment without any
+ user interaction. Batch jobs are part of most IT projects and Spring Batch
+ is the only open source framework that provides a robust, enterprise-scale
+ solution.
+
+ Business Scenarios
+
+ Commit batch process periodically
+
+
+
+ Concurrent batch processing: parallel processing of a
+ job
+
+
+
+ Staged, enterprise message-driven processing
+
+
+
+ Massively parallel batch processing
+
+
+
+ Manual or scheduled restart after failure
+
+
+
+ Sequential processing of dependent steps (with extensions to
+ workflow-driven batches)
+
+
+
+ Partial processing: skip records (e.g. on rollback)
+
+
+
+ Whole-batch transaction: for cases with a small batch size or
+ existing stored procedures/scripts
+
+
+
+ Technical Objectives
+
+ Batch developers use the Spring programming model: concentrate
+ on business logic; let the framework take care of
+ infrastructure.
+
+
+
+ Clear separation of concerns between the infrastructure, the
+ batch execution environment, and the batch application.
+
+
+
+ Provide common, core execution services as interfaces that all
+ projects can implement.
+
+
+
+ Provide simple and default implementations of the core
+ execution interfaces that can be used ‘out of the box’.
+
+
+
+ Easy to configure, customize, and extend services, by
+ leveraging the spring framework in all layers.
+
+
+
+ All existing core services should be easy to replace or
+ extend, without any impact to the infrastructure layer.
+
+
+
+ Provide a simple deployment model, with the architecture JARs
+ completely separate from the application, built using Maven.
+
+
+
+
+
+ Spring Batch Architecture
+
+ Spring Batch is designed with extensibility and a diverse group of
+ end users in mind. The figure below shows a sketch of the layered
+ architecture that supports the extensibility and ease of use for end-user
+ developers.
+
+
+
+
+
+
+
+
+ Figure 1.1: Spring Batch Layered
+ Architecture
+
+
+ This layered architecture highlights three major high level
+ components: Application, Core, and Infrastructure. The application
+ contains all batch jobs and custom code written by developers using Spring
+ Batch. The Batch Core contains the core runtime classes necessary to
+ launch and control a batch job. It includes things such as a
+ JobLauncher, Job, and
+ Step implementations. Both Application and Core are
+ built on top of a common infrastructure. This infrastructure contains
+ common readers and writers, and services such as the
+ RetryTemplate, which are used both by application
+ developers(ItemReader and
+ ItemWriter) and the core framework itself.
+ (retry)
+
+
\ No newline at end of file
diff --git a/docs/src/site/docbook/reference/step.xml b/docs/src/site/docbook/reference/step.xml
index d3a5520d4..1d89142d7 100644
--- a/docs/src/site/docbook/reference/step.xml
+++ b/docs/src/site/docbook/reference/step.xml
@@ -1,771 +1,858 @@
-
- Step
+
+ Configuring a Step
+
+ A Step is a domain object that encapsulates an
+ independent, sequential phase of a batch job and 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.
+
+
+
+
+
+
+
+
+
+
+
- Introduction
+ Chunk-Oriented Processing
- In Chapter 2, the overall architecture design was discussed, using
- the following diagram as a guide:
+ 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
+ ItemReader, handed to an
+ ItemWriter, and aggregated. Once the number of
+ items read equals the commit interval, the entire chunk is written out via
+ the ItemWriter, and then the transaction is committed.
+ fileref="images/chunk-oriented-processing.png" scale="75"
+ width="" />
- When viewed from left to right, the diagram describes a basic flow
- for the execution of a batch job:
+ Below is a code representation of the same concepts shown above:
+
-
-
- A Scheduler kicks off a job script (usually some form of shell
- script)
-
+
+ List items = new Arraylist();
+ for(int i = 0; i < commitInterval; i++){
+ Object processedItem = itemProcessor.process(itemReader.read());
+ items.add(processedItem);
+ }
+ itemWriter.write(items);
-
- The script sets up the classpath appropriately, and starts the
- Java process. In most cases, using
- CommandLineJobRunner as the entry point
-
-
-
- The JobRunner finds the Job using the
- JobLocator, pulls together the
- JobParameters and launches the
- Job
-
-
-
- The JobLauncher retrieves a
- JobExecution from the
- JobRepository, and executes the
- Job
-
-
-
- The Job executes each
- Step in sequence.
-
-
-
- The Step calls read on the
- ItemReader, handing the resulting item to the
- ItemWriter until null is returned, periodically
- committing and storing status in the
- JobRepository.
-
-
-
- When execution is complete, the Step
- returns control back to the Job, and if no more
- steps exist, control is returned back to the original caller, in this
- case, the scheduler.
-
-
-
- This flow is perhaps a bit overly simplified, but describes the
- complete flow in the most basic terms. From here, each tier will be
- described in detail, using actual implementations and examples.
-
-
-
- Application Tier
-
- The Application tier is entirely concerned with the actual
- processing of input:
-
-
-
-
-
-
-
-
-
-
+
- StepHandlerStep
+ Configuring a Step
- The figure above shows a simple 'item-oriented' execution flow.
- One item is read in from an ItemReader, and then
- handed to an ItemWriter, until their are no more
- items left. When processing first begins, a transaction is started and
- periodically committed until the Step is
- complete. Given these basic requirements, the
- StepHandlerStep requires the following
- dependencies, at a minimum:
+ Despite the relatively short list of required dependencies for a
+ Step, it is an extremely complex class that can
+ potentially contain many collaborators. In order to ease configuration,
+ the Spring Batch namespace can be used:
-
-
- ItemReader - The
- ItemReader that provides items for
- processing.
-
+
+ <job id="sampleJob">
+ <step name="step1" job-repository="jobRepository" transaction-manager="transactionManager">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ </step>
+ </job>
-
- ItemWriter - The
- ItemWriter that processes the items provided
- by the ItemReader.
-
+
-
- PlatformTransactionManager - Spring
- transaction manager that will be used to begin and commit
- transactions during processing.
-
+ The configuration above represents the only required dependencies
+ to create a item-oriented step:
+
+ reader - The ItemReader that provides
+ items for processing.
+
-
- JobRepository - The
- JobRepository that will be used to
- periodically store the StepExecution and
- ExecutionContext during processing (just
- before committing).
-
-
+
+ writer - The ItemWriter that
+ processes the items provided by the
+ ItemReader.
+
+
+
+ transaction-manager - Spring's
+ PlatformTransactionManager that will be
+ used to begin and commit transactions during processing.
+
+
+
+ job-repository - The JobRepository
+ that will be used to periodically store the
+ StepExecution and
+ ExecutionContext during processing (just
+ before committing).
+
+
+
+ commit-interval - The number of items that will be processed
+ before the transaction is committed.
+
+
+
+ It should be noted that, job-repository defaults to
+ "jobRepository" and transaction-manager defaults to "transactionManger".
+ Furthermore, the ItemProcessor is not required, since the item could be
+ directly passed from the reader to the writer.
+
+
+
+ The Commit Interval
+
+ As mentioned above, a step reads in and writes out items,
+ periodically committing using the supplied
+ PlatformTransactionManager. With a
+ commit-interval of 1, it will commit after writing only one item. This
+ is less than ideal in many situations, since beginning and committing a
+ transaction is expensive. Ideally, it is preferable to process as many
+ items as possible in each transaction, which is completely dependent
+ upon the type of data being processed and the resources with which the
+ step is interacting. For this reason, the number of items that are
+ processed within a commit can be configured.
+
+
+ <job id="sampleJob">
+ <step name="step1" job-repository="jobRepository" transaction-manager="transactionManager">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ </step>
+ </job>
+
+
+
+ In the example above, 10 items will be processed within each
+ transaction. At the beginning of processing a transaction is begun, and
+ each time read is called on the
+ ItemReader, a counter is incremented. When it
+ reaches 10, the list of aggregated items is passed to the
+ ItemWriter, and the transaction will be
+ committed.
+
+
+
+ Configuring a Step for Restart
+
+ In the Chapter on configuring a Job, restarting a
+ Job was discussed. Restart has numerous impacts
+ on steps, and as such may require some specific configuration.
- SimpleStepFactoryBean
+ Setting a StartLimit
- Despite the relatively short list of required dependencies for
- an StepHandlerStep, it is an extremely complex
- class that can potentially contain many collaborators. In order to
- ease configuration, a SimpleStepFactoryBean can
- be used:
+ There are many scenarios where you may want to control the
+ number of times a Step may be started. An
+ example is a Step 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
+ Job as Step that can be
+ run infinitely. Below is an example start limit configuration:
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- <property name="transactionManager" ref="transactionManager" />
- <property name="jobRepository" ref="jobRepository" />
- <property name="itemReader" ref="itemReader" />
- <property name="itemWriter" ref="itemWriter" />
- </bean>
+
+ <step name="step1">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="10" start-limit="1"/>
+ </step>
- The configuration above represents the only required
- dependencies of the factory bean. Attempting to instantiate a
- SimpleStepFactoryBean without at least those
- four dependencies will result in an exception being thrown during
- construction by the Spring container.
+
+
+ The simple step above can be run only once. Attempting to run it
+ again will cause an exception to be thrown. It should be noted that
+ the default value for the start-limit is
+ Integer.MAX_VALUE.
- Configuring a CommitInterval
+ Restarting a completed step
- As mentioned above, the StepHandlerStep
- reads in and writes out items, periodically commiting using the
- supplied PlatformTransactionManager. By
- default, it will commit after each item has been written. This is less
- than ideal in many situations, since beginning and commiting a
- transaction is expensive. Ideally, you would like to process as many
- items as possible in each transaction, which is completely dependant
- upon the type of data being processed and the resources that are being
- interacted with. For this reason, the number of items that are
- processed within a commit can be set as the commit interval:
+ In the case of a restartable job, there may be one or more steps
+ that should always be run, regardless of whether or not they were
+ successful the first time. An example might be a validation step, or a
+ Step that cleans up resources before
+ processing. During normal processing of a restarted job, any step with
+ a status of 'COMPLETED', meaning it has already been completed
+ successfully, will be skipped. Setting allow-start-if-complete to
+ "true" overrides this so that the step will always run:
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- <property name="transactionManager" ref="transactionManager" />
- <property name="jobRepository" ref="jobRepository" />
- <property name="itemReader" ref="itemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="commitInterval" value="10" />
- </bean>
+
+ <step name="step1">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="10" allow-start-if-complete="true"/>
+ </step>
- In this example, 10 items will be processed within each
- transaction. At the beginning of processing a transaction is begun,
- and each time read is called on the
- ItemReader, a counter is incremented. When it
- reaches 10, the transaction will be committed.
+
- Configuring a Step for Restart
+ Step restart configuration example
- Earlier in this chapter, restarting a Job
- was discussed. Restart has numerous impacts on steps, and as such may
- require some specific configuration.
+
+ <job id="footballJob" restartable="true">
+ <step name="playerload" next="gameLoad">
+ <tasklet reader="playerFileItemReader" writer="playerWriter"
+ commit-interval="10" />
+ </step>
+ <step name="gameLoad" next="playerSummarization">
+ <tasklet reader="gameFileItemReader" writer="gameWriter"
+ commit-interval="10" allow-start-if-complete="true"/>
+ </step>
+ <step name="playerSummarization">
+ <tasklet reader="playerSummarizationSource" writer="summaryWriter"
+ commit-interval="10" start-limit="3"/>
+ </step>
+ </job>
-
- There are many scenarios where you may want to control the
- number of times a Step may be started. An
- example is a Step 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
- Job as Step that can
- be run infinitely. Below is an example start limit
- configuration:
+ The above example configuration is for a job that loads in
+ information about football games and summarizes them. It contains
+ three steps: playerLoad, gameLoad, and playerSummarization. The
+ playerLoad Step loads player information from a
+ flat file, while the gameLoad Step does the
+ same for games. The final Step,
+ playerSummarization, then summarizes the statistics for each player
+ based upon the provided games. It is assumed that the file loaded by
+ 'playerLoad' must be loaded only once, but that 'gameLoad' will load
+ any games found within a particular directory, deleting them after
+ they have been successfully loaded into the database. As a result, the
+ playerLoad Step contains no additional
+ configuration. It can be started almost limitlessly, and if complete
+ will be skipped. The 'gameLoad' Step, however,
+ needs to be run every time in case extra files have been dropped since
+ it last executed. It has 'allow-start-if-complete' set to 'true' in
+ order to always be started. (It is assumed that the database tables
+ games are loaded into has a process indicator on it, to ensure new
+ games can be properly found by the summarization step). The
+ summarization Step, which is the most important
+ in the Job, is configured to have a start limit
+ of 3. This is useful because if the step continually fails, a new exit
+ code will be returned to the operators that control job execution, and
+ it won't be allowed to start again until manual intervention has taken
+ place.
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- <property name="transactionManager" ref="transactionManager" />
- <property name="jobRepository" ref="jobRepository" />
- <property name="itemReader" ref="itemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="commitInterval" value="10" />
- <property name="startLimit" value="1" />
- </bean>
+
+ This job is purely for example purposes and is not the same as
+ the footballJob found in the samples project.
+
- The simple step above can be run only once. Attempting to run
- it again will cause an exception to be thrown. It should be noted
- that the default value for startLimit is
- Integer.MAX_VALUE.
-
+ Run 1:
-
- Restarting a completed step
+
+
+ playerLoad is executed and completes successfully, adding
+ 400 players to the 'PLAYERS' table.
+
- In the case of a restartable job, there may be one or more
- steps that should always be run, regardless of whether or not they
- were successful the first time. An example might be a validation
- step, or a step that cleans up resources before processing. During
- normal processing of a restarted job, any step with a status of
- 'COMPLETED', meaning it has already been completed successfully,
- will be skipped. Setting allowStartIfComplete to true overrides this
- so that the step will always run:
+
+ gameLoad is executed and processes 11 files worth of game
+ data, loading their contents into the 'GAMES' table.
+
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- <property name="transactionManager" ref="transactionManager" />
- <property name="jobRepository" ref="jobRepository" />
- <property name="itemReader" ref="itemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="commitInterval" value="10" />
- <property name="startLimit" value="1" />
- <property name="allowStartIfComplete" value="true" />
- </bean>
-
+
+ playerSummarization begins processing and fails after 5
+ minutes.
+
+
-
- Step restart configuration example
+ Run 2:
- <bean id="footballJob"
- class="org.springframework.batch.core.job.SimpleJob">
- <property name="steps">
- <list>
- <!-- Step Bean details ommitted for clarity -->
- <bean id="playerload" parent="simpleStep" />
- <bean id="gameLoad" parent="simpleStep" >
- <property name="allowStartIfComplete" value="true" />
- </bean>
- <bean id="playerSummarization" parent="simpleStep" >
- <property name="startLimit" value="2" />
- </bean>
- </list>
- </property>
- <property name="jobRepository" ref="jobRepository" />
- <property name="restartable" value="true" />
- </bean>
+
+
+ playerLoad is not run, since it has already completed
+ successfully, and allow-start-if-complete is 'false' (the
+ default).
+
- The above example configuration is for a job that loads in
- information about football games and summarizes them. It contains
- three steps: playerLoad, gameLoad, and playerSummarization. The
- playerLoad Step loads player information from
- a flat file, while the gameLoad
- Step does the same for games. The final
- Step, playerSummarization, then summarizes
- the statistics for each player based upon the provided games. It is
- assumed that the file loaded by 'playerLoad' must be loaded only
- once, but that 'gameLoad' will load any games found within a
- particular directory, deleting them after they have been
- successfully loaded into the database. As a result, the playerLoad
- Step contains no additional configuration. It
- can be started almost limitlessly, and if complete will be skipped.
- The 'gameLoad' Step, however, needs to be run
- everytime, in case extra files have been dropped since it last
- executed, so it has 'allowStartIfComplete' set to 'true' in order to
- always be started. (It is assumed that the database tables games are
- loaded into has a process indicator on it, to ensure new games can
- be properly found by the summarization step) The summarization
- step, which is the most important in the
- Job, is configured to have a start limit of
- 3. This is useful in case it continually fails, a new exit code will
- be returned to the operators that control job execution, and it
- won't be allowed to start again until manual intervention has taken
- place.
+
+ gameLoad is executed again and processes another 2 files,
+ loading their contents into the 'GAMES' table as well (with a
+ process indicator indicating they have yet to be processed)
+
-
- This job is purely for example purposes and is not the same
- as the footballJob found in the samples project.
-
+
+ playerSummarization begins processing of all remaining game
+ data (filtering using the process indicator) and fails again after
+ 30 minutes.
+
+
- Run 1:
+ Run 3:
-
-
- playerLoad is executed and completes successfully, adding
- 400 players to the 'PLAYERS' table.
-
+
+
+ playerLoad is not run, since it has already completed
+ successfully, and allow-start-if-complete is 'false' (the
+ default).
+
-
- gameLoad is executed and processes 11 files worth of game
- data, loading their contents into the 'GAMES' table.
-
+
+ gameLoad is executed again and processes another 2 files,
+ loading their contents into the 'GAMES' table as well (with a
+ process indicator indicating they have yet to be processed)
+
-
- playerSummarization begins processing and fails after 5
- minutes.
-
-
-
- Run 2:
-
-
-
- playerLoad is not run, since it has already completed
- succesfully, and allowStartIfComplete is false (the
- default).
-
-
-
- gameLoad is executed again and processes another 2 files,
- loading their contents into the 'GAMES' table as well (with a
- process indicator indicating they have yet to be
- processed)
-
-
-
- playerSummarization begins processing of all remaining
- game data (filtering using the process indicator) and fails
- again after 30 minutes.
-
-
-
- Run 3:
-
-
-
- playerLoad is not run, since it has already completed
- succesfully, and allowStartIfComplete is false (the
- default).
-
-
-
- gameLoad is executed again and processes another 2 files,
- loading their contents into the 'GAMES' table as well (with a
- process indicator indicating they have yet to be
- processed)
-
-
-
- playerSummarization is not start, and the job is
- immeadiately killed, since this is the third execution of
- playerSummarization, and it's limit is only 2. The limit must
- either be raised, or the Job must be
- executed as a new JobInstance.
-
-
-
+
+ playerSummarization is not start, and the job is immediately
+ killed, since this is the third execution of playerSummarization,
+ and it's limit is only 2. The limit must either be raised, or the
+ Job must be executed as a new
+ JobInstance.
+
+
+
+
+
+ Configuring Skip Logic
+
+ There are many scenarios where errors encountered while processing
+ should not result in Step failure, but should be
+ skipped instead. This is usually a decision that must be made by someone
+ who understands the data itself and what meaning it has. Financial data,
+ for example, may not be skippable because it results in money being
+ transferred, which needs to be completely accurate. Loading a list of
+ vendors, on the other hand, might allow for skips. If a vendor is not
+ loaded because it was formatted incorrectly or was missing necessary
+ information, then there probably won't be issues. Usually these bad
+ records are logged as well, which will be covered later when discussing
+ listeners.
+ <step name="step1">
+ <tasklet reader="flatFileItemReader" writer="itemWriter" commit-interval="10" skip-limit="10">
+ <skippable-exception-classes>
+ org.springframework.batch.item.file.FlatFileParseException
+ </skippable-exception-classes>
+ </tasklet>
+ </step>
+
+
+
+ In this example, a FlatFileItemReader is
+ used, and if at any point a
+ FlatFileParseException is thrown, it will be
+ skipped and counted against the total skip limit of 10. It should be
+ noted that any failures encountered while reading will not count against
+ the commit interval. In other words, the commit interval is only
+ incremented on writes (regardless of success or failure).
+
+
+
+ One problem with the example above is that any other exception
+ besides a FlatFileParseException will cause the
+ Job to fail. In certain scenarios this may be the
+ correct behaviour, however, in certain scenarios it may be easier to
+ identify which exceptions should cause failure and skip everything
+ else:
+ <step name="step1">
+ <tasklet reader="flatFileItemReader" writer="itemWriter" commit-interval="10" skip-limit="10">
+ <skippable-exception-classes>
+ java.lang.Exception
+ </skippable-exception-classes>
+ <fatal-exception-classes>
+ java.io.FileNotFoundException
+ </fatal-exception-classes>
+ </tasklet>
+ </step>
+
+
+
+ By setting the skippable exceptions to
+ java.lang.Exception, any exception that is thrown
+ will be skipped. However, the second list, 'fatal-exception-classes',
+ contains specific exceptions that should be fatal if encountered.
+
+
+
+ Configuring Retry Logic
+
+ In most cases you want an exception to cause either a skip or
+ Step failure. However, not all exceptions are
+ deterministic. If a FlatFileParseException is
+ encountered while reading, it will always be thrown for that record.
+ Resetting the ItemReader will not help. However,
+ for other exceptions, such as a
+ DeadlockLoserDataAccessException, which indicates
+ that the current process has attempted to update a record that another
+ process holds a lock on, waiting and trying again might result in
+ success. In this case, retry should be configured:
+
+
+ <step name="step1">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="2" retry-limit="3">
+ <retryable-exception-classes>
+ org.springframework.dao.DeadlockLoserDataAccessException
+ </retryable-exception-classes>
+ </tasklet>
+ </step>
+
+
+
+ The Step allows a limit for the number of
+ times an individual item can be retried, and a list of exceptions that
+ are 'retryable'.
+
+
+
+ Controlling rollback
+
+ By default, regardless of retry or skip, any exceptions thrown
+ from the ItemWriter will cause the transaction
+ controlled by the Step to rollback. If skip is
+ configured as described above, exceptions thrown from the
+ ItemReader will not cause a rollback. However,
+ there are many scenarios in which exceptions thrown from the
+ ItemWriter should not cause a rollback because no
+ action has taken place to invalidate the transaction. For this reason,
+ the Step can be configured with a list of
+ exceptions that should not cause rollback. The transaction-attribute
+ attribute is a comma-separated list. Prefixing a class name with the "+"
+ symbol will indicate that exception should not cause rollback.
+
+
+ <step name="step1">
+ <tasklet reader="itemReader" writer="itemWriter" commit-interval="2" skip-limit="1"
+ transaction-attribute="+org.springframework.batch.item.validator.ValidationException">
+ </tasklet>
+ </step>
+
+
+
+ Transaction attributes can be used to control multiple other
+ settings such as isolation and propagation behaviour. More information
+ on setting transaction attributes can be found in the spring core
+ documentation.
+
+
+
+ Registering ItemStreams with the Step
+
+ The step has to take care of ItemStream
+ callbacks at the necessary points in its lifecycle. (for more
+ information on the ItemStream interface, please refer to the chapter on
+ Readers and Writers) This is vital if a step fails, and might need to be
+ restarted, because the ItemStream interface is
+ where the step gets the information it needs about persistent state
+ between executions.
+
+ If the ItemReader,
+ ItemProcessor, or
+ ItemWriter itself implements the
+ ItemStream interface, then these will be
+ registered automatically. Any other streams need to be registered
+ separately. This is often the case where there are indirect
+ dependencies, like delegates being injected into the reader and writer.
+ To a stream it can be injected into the Step
+ through the 'streams' element, as illustrated below:
+
+
+ <step name="step1">
+ <tasklet reader="itemReader" writer="compositeWriter" commit-interval="2">
+ <streams>
+ <stream ref="fileItemWriter1"/>
+ <stream ref="fileItemWriter2"/>
+ </streams>
+ </tasklet>
+ </step>
+
+ <beans:bean id="compositeWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
+ <beans:property name="delegates">
+ <beans:list>
+ <beans:ref bean="fileItemWriter1" />
+ <beans:ref bean="fileItemWriter2" />
+ </beans:list>
+ </beans:property>
+ </beans:bean>
+
+
+
+ In the example above, the
+ CompositeItemWriter is not an
+ ItemStream, 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
+ ItemReader does not need to explicitly registered
+ as a stream because it is a direct property of the
+ Step. The step will now be restartable and the
+ state of the reader and writer will be correctly persisted in case of a
+ failure.
+
+
+
+ Intercepting Step Execution
+
+ Just as with the Job, there are many events
+ during the execution of a Step where a user may
+ need to perform some functionality. For example, in order to write out
+ to a flat file that requires a footer, the
+ ItemWriter needs to be notified when the
+ Step has been completed, so that the footer can
+ written. This can be accomplished with one of many
+ Step scoped listeners.
+
+ Any class that implements the StepListener
+ interface (or an extension thereof) can be applied to a step via the
+ listeners element:
+
+
+ <step name="step1">
+ <tasklet reader="reader" writer="writer" commit-interval="10"/>
+ <listeners>
+ <listener ref="stepListener"/>
+ </listeners>
+ </step>
+
+
+
+ In addition to the StepListener interfaces,
+ annotations are provided address the same concerns.
- Configuring Skip Logic
+ StepExecutionListener
- There are many scenarios where errors encountered while
- processing should not result in Step failure,
- but should be skipped instead. This is usually a decision that must be
- made by someone who understands the data itself and what meaning it
- has. Financial data, for example, may not be skippable because it
- results in money being transferred, which needs to be completely
- accurate. Loading in a list of vendors, on the other hand, might allow
- for skips, since a vendor not being loaded because it was formatted
- incorrectly, or missing necessary information, won't cause issues.
- Usually these bad records are logged as well, which will be covered
- later when discussing listeners. Configuring skip handling requires
- using a new factory bean:
- SkipLimitStepFactoryBean <bean id="skipSample"
- class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
- <property name="skipLimit" value="10" />
- <property name="itemReader" ref="flatFileItemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="skippableExceptionClasses"
- value="org.springframework.batch.item.file.FlatFileParseException">
- </property>
- </bean>
+ StepExecutionListener represents the most
+ generic listener for Step execution. It allows
+ for notification before a Step is started and
+ after it has ends, whether it ended normally or failed:
- In this example, a FlatFileItemReader is
- used, and if at any point a FlatFileParseException is thrown, it will
- be skipped and counted against the total skip limit of 10. It should
- be noted that any failures encountered while reading will not count
- against the commit interval. In other words, the commit interval is
- only incremented on writes (regardless of success or failure).
-
-
-
- One problem with the example above is that any other exception
- besides a FlatFileParseException will cause the
- Job to fail. In certain scenarios this may be
- the correct behaviour, however, in certain scenarios it may be easier
- to identify which exceptions should cause failure and skip everything
- else: <bean id="skipSample"
- class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
- <property name="skipLimit" value="10" />
- <property name="itemReader" ref="flatFileItemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="skippableExceptionClasses"
- value="java.lang.Exception">
- <property name="fatalExceptionClasses"
- value="java.io.FileNotFoundException">
- </property>
- </befan>
-
- By setting the skippable exceptions to
- java.lang.Exception, any exception that is
- thrown will be skipped. However, the second list,
- 'fatalExceptionClasses', contains specific exceptions that should be
- fatal if encountered.
-
-
-
- Configuring Retry Logic
-
- In most cases you want an Exception to cause either a skip or
- Step failure. However, not all exceptions are
- deterministic. If a FlatFileParseException is encountered while
- reading, it will always be thrown for that record. Resseting the
- ItemReader will not help. However, for other
- exceptions, such as a
- DeadlockLoserDataAccessException, which
- indicates that the current process has attempted to update a record
- that another process holds a lock on, waiting and trying again might
- result in success. In this case, retry should be configured:
-
- <bean id="step1"
- class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
- <property name="itemReader" ref="itemGenerator" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="retryLimit" value="3" />
- <property name="retryableExceptionClasses" value="org.springframework.dao.DeadlockLoserDataAccessException" />
- </bean>
-
- The SkipLimitStepFactoryBean requires a
- limit for the number of times an individual item can be retried, and a
- list of Exceptions that are 'retryable'.
-
-
-
- Controlling rollback
-
- By default, regardless of retry or skip, any exceptions thrown
- from the ItemWriter will cause the transaction
- controlled by the Step to rollback. If skip is
- configured as described above, exceptions thrown from the
- ItemReader will not cause a rollback. However,
- there are many scenarios in which exceptions thrown from the
- ItemWriter should not cause a rollback because
- no action has taken place to invalidate the transaction. For this
- reason, the SkipLimitStepFactoryBean can be
- configured with a list of exceptions that should not cause
- rollback:
-
- <bean id="step2"
- class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
- <property name="commitInterval" value="2" />
- <property name="skipLimit" value="1" />
- <!-- No rollback for exceptions that are marked with "+" in the tx attributes -->
- <property name="transactionAttribute"
- value="+org.springframework.batch.item.validator.ValidationException" />
- <property name="itemReader"
- ref="tradeSqlItemReader" />
- <property name="itemWriter"
- ref="itemTrackingWriter" />
- </bean>
-
- The TransactionAttribute property above
- can be used to control multiple other settings such as isolation and
- propagation behaviour. More information on setting transaction
- attributes can be found in the spring core documentation.
-
-
-
- Registering ItemStreams with the Step
-
- The step has to take care of ItemStream
- callbacks at the necessary points in its lifecycle. This is vital if a
- step fails, and might need to be restarted, because the
- ItemStream interface is where the step gets the
- information it needs about persistent state between executions. The
- factory beans that Spring Batch provides for convenient configuration
- of Step instances have features that allow
- streams to be registered with the step when it is configured.
-
- If the ItemReader or
- ItemWriter themselves implement the ItemStream
- interface, then these will be registered automatically. Any other
- streams need to be registered separately. This is often the case where
- there are indirect dependencies, like delegates being injected into
- the reader and writer. To register these they can be injected into the
- factory beans through the streams property, as illustrated
- below:
-
- <bean id="step1"
- class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
- <property name="streams" ref="fileItemReader" />
- <property name="itemReader">
- <bean
- class="org.springframework.batch.item.validator.ValidatingItemReader">
- <property name="itemReader" ref="itemReader" />
- <property name="validator" ref="fixedValidator" />
- </bean>
- </property>
- ...
-</bean>
-
- In the example above the main item reader is being set up to
- delegate to a bean called "fileItemReader", which itself is being
- registered as a stream directly. The step will now be restartable and
- the state of the reader will be correctly persisted in case of a
- failure.
-
-
-
- Intercepting Step Execution
-
- Just as with the Job, there are many
- events during the execution of a Step that a
- user may need notification of. For example, if writing out to a flat
- file that requires a footer, the ItemWriter
- needs to be notified when the Step has been
- completed, so that it can write the footer. This can be accomplished
- with one of many Step scoped listeners.
-
-
- StepExecutionListener
-
- StepExecutionListener represents the
- most generic listener for Step execution. It
- allows for notification before a Step is
- started, after it has completed, and if any errors are encountered
- during processing:
-
- public interface StepExecutionListener extends StepListener {
+
+ public interface StepExecutionListener extends StepListener {
void beforeStep(StepExecution stepExecution);
- ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e);
-
ExitStatus afterStep(StepExecution stepExecution);
-}
+ }
- ExitStatus is the return type of
- onErrorInStep and
- afterStep in order to allow listeners the
- chance to modify the exit code that is returned upon completion of a
- Step. A
- StepExecutionListener can be applied to any
- step factory bean via the listeners property:
+
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- <property name="transactionManager" ref="transactionManager" />
- <property name="jobRepository" ref="jobRepository" />
- <property name="itemReader" ref="itemReader" />
- <property name="itemWriter" ref="itemWriter" />
- <property name="commitInterval" value="10" />
- <property name="listeners" ref="stepListener" />
- </bean>
+ ExitStatus is the return type of
+ afterStep in order to allow listeners the
+ chance to modify the exit code that is returned upon completion of a
+ Step.
- Because all listeners extend the
- StepListener interface, they all may be
- applied to factory beans in the same way.
-
+ The annotations corresponding to this interface are:
-
- ChunkListener
+
+
+ @BeforeStep
+
- A chunk is defined as the items processed within the scope of
- a transaction. Committing a transaction commits a 'chunk'. It may be
- useful to be nofied before and after a chunk has completed, in which
- case the ChunkListener interface may be
- used:
+
+ @AfterStep
+
+
+
- public interface ChunkListener extends StepListener {
+
+ ChunkListener
+
+ A chunk is defined as the items processed within the scope of a
+ transaction. Committing a transaction, at each commit interval,
+ commits a 'chunk'. A ChunkListener may be
+ useful to perform logic before a chunk begins processing or after a
+ chunk has completed:
+
+ public interface ChunkListener extends StepListener {
void beforeChunk();
void afterChunk();
}
- The beforeChunk method is called
- after the transaction is started, but before
- read is called on the
- ItemReader. Conversely,
- afterChunk is called after the last call to
- write on the
- ItemWriter, but before the chunk has been
- committed.
-
+ The beforeChunk method is called after
+ the transaction is started, but before read
+ is called on the ItemReader. Conversely,
+ afterChunk is called after the last call to
+ write on the
+ ItemWriter, but before the chunk has been
+ committed.
-
- ItemReadListener
+ The annotations corresponding to this interface are:
- When discussing skip logic above, it was mentioned that it may
- be beneficial to log out skipped records, so that they can be deal
- with later. In the case of read errors, this can be done with an
- ItemReaderListener: public interface ItemReadListener extends StepListener {
+
+
+ @BeforeChunk
+
+
+
+ @AfterChunk
+
+
+
+
+
+ ItemReadListener
+
+ When discussing skip logic above, it was mentioned that it may
+ be beneficial to log out skipped records, so that they can be deal
+ with later. In the case of read errors, this can be done with an
+ ItemReaderListener: public interface ItemReadListener<T> extends StepListener {
void beforeRead();
- void afterRead(Object item);
+ void afterRead(T item);
void onReadError(Exception ex);
}
- The beforeRead method will be called
- before each call to read on the
- ItemReader. The
- afterRead method will be called after each
- successful call to read, and will be passed
- the item that was read. If there was an error while reading, the
- onReadError method will be called. The
- exception encounterd will be provided so that it can be
- logged.
-
+ The beforeRead method will be called
+ before each call to read on the
+ ItemReader. The
+ afterRead method will be called after each
+ successful call to read, and will be passed
+ the item that was read. If there was an error while reading, the
+ onReadError method will be called. The
+ exception encountered will be provided so that it can be
+ logged.
-
- ItemWriteListener
+ The annotations corresponding to this interface are:
- Just as with the ItemReaderListener, the writing of an item
- can be 'listened' to:
+
+
+ @BeforeRead
+
- public interface ItemWriteListener extends StepListener {
+
+ @AfterRead
+
- void beforeWrite(Object item);
+
+ @OnReadError
+
+
+
- void afterWrite(Object item);
+
+ ItemProcessListener
- void onWriteError(Exception ex, Object item);
+ Just as with the ItemReadListener, the
+ processing of an item can be 'listened' to:
+
+ public interface ItemProcessListener<T, S> extends StepListener {
+
+ void beforeProcess(T item);
+
+ void afterProcess(T item, S result);
+
+ void onProcessError(T item, Exception e);
}
- The beforeWrite method will be called
- before write on the
- ItemWriter, and is handed the item that will
- be written. The afterWrite method will be
- called after the item has been succesfully writen. If there was an
- error while writing, the onWriteError
- method will be called. The exception encountered and the item that
- was attempted to be written will be provided, so that they can be
- logged.
-
+ The beforeProcess method will be called
+ before process on the
+ ItemProcessor, and is handed the item that will
+ be processed. The afterProcess method will be
+ called after the item has been successfully processed. If there was an
+ error while processing, the onProcessError
+ method will be called. The exception encountered and the item that was
+ attempted to be processed will be provided, so that they can be
+ logged.
-
- SkipListener
+ The annotations corresponding to this interface are:
- Both ItemReadListener and
- ItemWriteListner provide a mechanism for
- being notified of errors, but neither one will inform you that a
- record has actually been skipped.
- onWriteError, for example, will be called
- even if an item is retried and successful. For this reason, there is
- a separate interface for tracking skipped items:
+
+
+ @BeforeProcess
+
-
- public interface SkipListener extends StepListener {
+
+ @AfterProcess
+
+
+
+ @OnProcessError
+
+
+
+
+
+ ItemWriteListener
+
+ The writing of an item can be 'listened' to with the
+ ItemWriteListener:
+
+ public interface ItemWriteListener<S> extends StepListener {
+
+ void beforeWrite(List<? extends S> items);
+
+ void afterWrite(List<? extends S> items);
+
+ void onWriteError(Exception exception, List<? extends S> items);
+}
+
+ The beforeWrite method will be called
+ before write on the
+ ItemWriter, and is handed the item that will be
+ written. The afterWrite method will be called
+ after the item has been successfully written. If there was an error
+ while writing, the onWriteError method will
+ be called. The exception encountered and the item that was attempted
+ to be written will be provided, so that they can be logged.
+
+ The annotations corresponding to this interface are:
+
+
+
+ @BeforeWrite
+
+
+
+ @AfterWrite
+
+
+
+ @OnWriteError
+
+
+
+
+
+ SkipListener
+
+ Both ItemReadListener and
+ ItemWriteListner provide a mechanism for being
+ notified of errors, but neither one will inform you that a record has
+ actually been skipped. onWriteError, for
+ example, will be called even if an item is retried and successful. For
+ this reason, there is a separate interface for tracking skipped
+ items:
+
+
+ public interface SkipListener<T,S> extends StepListener {
void onSkipInRead(Throwable t);
- void onSkipInWrite(Object item, Throwable t);
+ void onSkipInWrite(S item, Throwable t);
+
+ void onSkipInProcess(T item, Throwable t);
}
- onSkipInRead will be called whenever
- an item is skipped while reading. It should be noted that rollbacks
- may cause the same item to be registered as skipped more than once.
- onSkipInWrite will be called when an item
- is skipped while writing. Because the item has been read
- successfully (and not skipped), it is also provided the item itself
- as an argument.
-
+ onSkipInRead will be called whenever an
+ item is skipped while reading. It should be noted that rollbacks may
+ cause the same item to be registered as skipped more than once.
+ onSkipInWrite will be called when an item is
+ skipped while writing. Because the item has been read successfully
+ (and not skipped), it is also provided the item itself as an
+ argument.
+
+ The annotations corresponding to this interface are:
+
+
+
+ @OnSkipInRead
+
+
+
+ @OnSkipInWrite
+
+
+
+ @OnSkipInProcess
+
+
+
+
+
+ TaskletStep
+
+ Chunk oriented processing is not the only way to process in a
+ Step. What if a Step must
+ consist as a simple stored procedure call? You could implement the call as
+ an ItemReader and return null after the procedure
+ finishes, but it is a bit unnatural since there would need to be a no-op
+ ItemWriter and lots of overhead for transaction
+ handling, listeners, etc. Spring Batch provides an implementation of
+ Step for this scenario:
+ TaskletStep. As explained in Chapter 2, the
+ Tasklet is a simple interface that has one method,
+ execute, which will be a called once for the
+ whole Step. Tasklet
+ implementors might call a stored procedure, a script, or a simple SQL
+ update statement. To create a TaskletStep, the
+ 'tasklet' attribute should reference a bean defining a
+ Tasklet object; no 'tasklet' element is needed
+ within the 'step':
+
+
+ <step name="step1" tasklet="myTasklet" />
+
+
+
+
+ TaskletStep will automatically register the tasklet as
+ StepExecutionListener if it implements this
+ interface
+
+
+
+ TaskletAdapter
+
+ As with other adapters for the ItemWriter
+ and ItemReader interfaces, the
+ Tasklet interface contains an implementation that
+ allows for adapting itself to any pre-existing class:
+ TaskletAdapter. An example where this may be
+ useful is an existing DAO that is used to update a flag on a set of
+ records. The TaskletAdapter can be used to call
+ this class without having to write an adapter for the
+ Tasklet interface:
+
+
+ <bean id="myTasklet" class="org.springframework.batch.core.step.tasklet.TaskletAdapter">
+ <property name="targetObject">
+ <bean class="org.mycompany.FooDao">
+ </property>
+ <property name="targetMethod" value-"updateFoo" />
+ </bean>
+
+
+
- TaskletStep
+ Example Tasklet implementation
- Item oriented processing is not the only way to process in a
- Step. What if a Step must
- consist as a simple storec procedure call? You could implement the call
- as an ItemReader and return null after the
- procedure finishes, but it is a bit unnatural since there would need to
- be a no-op ItemWriter and lots of overhead for
- transaction handling, listeners, etc. Spring Batch provides an
- implementation of Step for this scenario:
- TaskletStep. As explained in Chapter 2, the
- Tasklet is a simple interface that has one
- method, execute, which will be a called once
- for the whole Step.
- Tasklet implementors might call a stored
- procedure, a script, or a simple SQL upate statement. Because there are
- less concerns, there are only two required dependencies for a
- TaskletStep: a Tasklet,
- and a JobRepository:
+ Many batch jobs contain steps that must be done before the main
+ 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
+ another location. The example below taken from the Spring Batch samples
+ project, is a Tasklet implementation with just
+ such a responsibility:
- <bean id="taskletStep"
- class="org.springframework.batch.core.step.tasklet.TaskletStep" />
- <property name="tasklet" ref="tasklet" />
- <property name="jobRepository" ref="repository" />
-</bean>
-
-
- TaskletStep will automatically register the tasklet as
- StepExecutionListener if it implements this
- interface
-
-
-
- TaskletAdapter
-
- As with other adapters for the ItemWriter
- and ItemReader interfaces, the
- Tasklet interface contains an implementation
- that allows for adapting itself to any pre-existing class:
- TaskletAdapter. An example where this may be
- useful is an existing DAO that is used to upate a flag on a set of
- records. The TaskletAdapter can be used to call
- this class without having to write an adapter for the
- Tasklet interface:
-
- <bean id="deleteFilesInDir" parent="taskletStep">
- <property name="tasklet">
- <bean class="org.springframework.batch.core.step.tasklet.TaskletAdapter">
- <property name="targetObject">
- <bean class="org.mycompany.FooDao">
- </property>
- <property name="targetMethod" value-"updateFoo" />
- </bean>
- </property>
- </bean>
-
-
-
- Example Tasklet implementation
-
- Many batch jobs contains steps that must be done before the main
- 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
- another location. The example below taken from the Spring Batch
- samples project, is a Tasklet implementation
- with just such a responsibility:
-
- public class FileDeletingTasklet implements Tasklet, InitializingBean {
+ public class FileDeletingTasklet implements Tasklet, InitializingBean {
private Resource directory;
@@ -792,42 +879,42 @@
}
}
- The above Tasklet implementation will
- delete all files within a given directory. It should be noted that the
- execute method will only be called once. All
- that is left is to inject the Tasklet into a
- TaskletStep:
+ The above Tasklet implementation will
+ delete all files within a given directory. It should be noted that the
+ execute method will only be called once. All
+ that is left is to reference the Tasklet from the
+ Step:
- <bean id="taskletJob" parent="simpleJob">
- <property name="steps">
- <bean id="deleteFilesInDir" parent="taskletStep">
- <property name="tasklet">
- <bean class="org.springframework.batch.sample.tasklet.FileDeletingTasklet">
- <property name="directoryResource" ref="directory" />
- </bean>
- </property>
+
+ <job id="taskletJob">
+ <step name="deleteFilesInDir" tasklet="fileDeletingTasklet"/>
+ </job>
+
+ <bean id="fileDeletingTasklet"
+ class="org.springframework.batch.sample.tasklet.FileDeletingTasklet">
+ <property name="directoryResource">
+ <bean id="directory"
+ class="org.springframework.core.io.FileSystemResource">
+ <constructor-arg value="target/test-outputs/test-dir" />
</bean>
</property>
</bean>
- <bean id="directory"
- class="org.springframework.core.io.FileSystemResource">
- <constructor-arg value="target/test-outputs/test-dir" />
- </bean>
-
+
+
-
- Executing System Commands
+
+ Executing System Commands
- 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 Tasklet
- implementation for calling system commands:
+ 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 Tasklet implementation for
+ calling system commands:
-
+
<bean class="org.springframework.batch.sample.tasklet.SystemCommandTasklet">
<property name="command" value="echo hello" />
<!-- 5 second timeout for the command to complete -->
@@ -835,263 +922,292 @@
</bean>
-
-
-
-
- Controlling Step Flow
-
-
-
-
-
-
- Configuring for Stop
-
-
-
-
-
- Programmatic flow decisions
-
-
-
- Examples of Customized Business Logic
+ Controlling Step Flow
+
+ 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 Step doesn't necessarily
+ mean that the Job should fail. Further, there may
+ be more than one type of 'success', which determines which
+ Step should be executed next. Depending upon how a
+ group of Steps is configured, certain steps may not even be processed at
+ all.
- Some batch jobs can be assembled purely from off-the-shelf
- components in Spring Batch, mostly the ItemReader
- and ItemWriter implementations. Where this is not
- possible (the majority of cases) the main API entry points for
- application developers are the Tasklet,
- ItemReader, ItemWriter and
- the various listener interfaces. Most simple batch jobs will be able to
- use off-the-shelf input from a Spring Batch
- ItemReader, but it is very often the case that
- there are custom concerns in the processing and writing, which normally
- leads developers to implement an ItemWriter, or
- ItemTransformer.
+ Sequential Flow
- Here we provide a few examples of common patterns in custom
- business logic, mainly using the listener interfaces . It should be
- noted that an ItemReader or
- ItemWriter can implement the listener interfaces
- as well if appropriate.
+ The simplest flow scenario is a job where all of the steps execute
+ sequentially:
+
+
+
+
+
+
+
+
+
+
+
+ This can be achieved using the 'next' attribute of
+ Step:
+
+
+ <job id="job">
+ <step name="stepA" next="stepB" />
+ <step name="stepB" next="stepC"/>
+ <step name="stepC" />
+ </job>
+
+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 Job will fail and
+ 'step B' will not execute.
- Logging Item Processing and Failures
+ Conditional Flow
- A common use case is the need for special handling of errors in a
- step, item by item, perhaps logging to a special channel, or inserting a
- record into a database. The StepHandlerStep
- (created from the step factory beans) allows users to implement this use
- case with a simple ItemReadListener, for errors
- on read, and an ItemWriteListener, for errors on
- write. The below code snippets illustrate a listener that logs both read
- and write failures:
+ In the example above, there's only two possibilities:
- public class ItemFailureLoggerListener extends ItemListenerSupport {
+
+
+ The Step is successful and the next Step should be
+ executed
+
- private static Log logger = LogFactory.getLog("item.error");
+
+ The Step failed and thus the Job should fail.
+
+
- public void onReadError(Exception ex) {
- logger.error("Encountered error on read", e);
- }
-
- public void onWriteError(Exception ex, Object item) {
- logger.error("Encountered error on write", e);
+ 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?
+
+
+
+
+
+
+
+
+
+ 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:
+
+
+
+ "*" will zero or more characters
+
+
+
+ "?" will match exactly one character
+
+
+
+ For example, "c*t" will match "cat" and "count", while "c?t" will
+ match "cat" but not "count".
+
+ 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".
+
+
+ <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>
+
+
+
+
+ Batch Status vs. Exit Status
+
+ When configuring a Job for conditional
+ flow, it is important to understand the difference between
+ BatchStatus and
+ ExitStatus. BatchStatus
+ is an enumeration that is a property of both
+ JobExecution and
+ StepExecution, and is used by the framework to
+ record the status of a Job or
+ Step. 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
+ step or job has completed successfully, FAILED is set when it fails,
+ and so on. The example above contains the following 'next'
+ element:
+
+
+ <next on="FAILED" to="stepB" />
+
+
+
+ At first glance, it would appear that the 'on' attribute
+ references the BatchStatus of the
+ Step it belongs to. However, it references the
+ ExitStatus of the Step.
+ As the name implies, ExitStatus represents the
+ status of a Step after it finishes execution.
+ More specifically, the 'next' element above references the
+ ExitCode of the
+ ExitStatus. 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 BatchStatus 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:
+
+
+ <step name="step1">
+ <end on="FAILED" />
+ <next on="COMPLETED WITH SKIPS" to="errorPrint1" />
+ <next on="*" to="step2" />
+ </step>
+
+
+
+ The above step has three possibilities:
+
+
+
+ The step failed, in which case the job should fail.
+
+
+
+ The Step completed successfully.
+
+
+
+ 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.
+
+
+
+ The above configuration will work, however, something needs to
+ change the exit code based on the condition of the execution having
+ skipped records:
+
+ 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;
}
+ }
+ ...
}
- Having implemented this listener it must be registered with the
- step:
-
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" >
- ...
- <property name="listeners">
- <bean class="org.example...ItemFailureLoggerListener"/>
- </property>
- </bean>
-
- Remember that if your listener does anything in an
- onError() method, it will be inside a transaction that is
- going to be rolled back. If you need to use a transactional resource
- such as a database inside an onError() method, consider
- adding a declarative transaction to that method (see Spring Core
- Reference Guide for details), and giving its propagation attribute the
- value REQUIRES_NEW.
+ The above code is a StepExecutionListener
+ that first checks to make sure the Step was
+ successful, and next if the skip count on the
+ StepExecution is higher than 0. If both
+ conditions are met, a new ExitStatus with an exit code of "COMPLETED
+ WITH SKIPS" is returned.
+
- Stopping a Job Manually for Business Reasons
+ Configuring for Stop
- Spring Batch provides a stop() method through the JobLauncher
- interface, but this is really aimed at the operator, rather than the
- application programmer. Sometimes it is more convenient or makes more
- sense to stop a job execution from within the business logic.
+ If it is desired that the batch job stop under certain conditions,
+ then either the "stop" tag or the "end" tag may be used.
- The simplest thing to do is to throw a RuntimeException (one that
- isn't retried indefinitely or skipped), For example, a custom exception
- type could be used, as in the example below:
+ 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.
- public class PoisonPillItemWriter extends AbstractItemWriter {
-
- public void write(Object item) throws Exception {
+ 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".
- if (isPoisonPill(item)) {
- throw new PoisonPillException("Posion pill detected: "+item);
- }
+
+ <step name="step1">
+ <stop on="COMPLETED" to="step2"/>
+ </step>
+ <step name="step2">
+ <next on="FOO" to="step3"/>
+ <end on="*" status="FAILED"/>
+ </step>
+ <step name="step3" />
- }
-
-}
-
- Another simple way to stop a step from executing is to simply
- return null from the
- ItemReader:
-
- public class EarlyCompletionItemReader extends AbstractItemReader {
-
- private ItemReader delegate;
-
- public void setDelegate(ItemReader delegate) { ... }
-
- public Object read() throws Exception {
-
- Object item = delegate.read();
-
- if (isEndItem(item)) {
- return null; // end the step here
- }
-
- return item;
-
- }
-
-}
-
- The previous example actually relies on the fact that there is a
- default implementation of the CompletionPolicy
- strategy which signals a complete batch when the item to be processed is
- null. A more sophisticated completion policy could be implemented and
- injected into the Step through the
- RepeatOperationsStepFactoryBean:
-
- <bean id="simpleStep"
- class="org.springframework.batch.core.step.item.RepeatOperationsStepFactoryBean" >
- ...
- <property name="chunkOperations">
- <bean class="org.springframework.batch.repeat.support.RepeatTemplate">
- <property name="completionPolicy">
- <bean class="org.example...SpecialCompletionPolicy"/>
- </property>
- </bean>
- </property>
- </bean>
-
- An alternative is to set a flag in the
- StepExecution, which is checked by the
- Step implementations in the framework in between
- item processing. To implement this alternative, we need access to the
- current StepExecution, and this can be achieved by implementing a
- StepListener and registering it with the Step. Here is an example of a
- listener that sets the flag:
-
- public class CustomItemWriter extends ItemListenerSupport implements StepListener {
-
- private StepExecution stepExecution;
-
- public void beforeStep(StepExecution stepExecution) {
- this.stepExecution = stepExecution;
- }
-
- public void afterRead(Object item) {
-
- if (isPoisonPill(item)) {
- stepExecution.setTerminateOnly(true);
- }
-
- }
-
-}
-
- The default behaviour here when the flag is set is for the step to
- throw a JobInterruptedException. This can be
- controlled through the StepInterruptionPolicy,
- but the only choice is to throw or not throw an exception, so this is
- always an abnormal ending to a job.
+
- Adding a Footer Record
+ Programmatic flow decisions
- A very common requirement is to aggregate information during the
- output process and to append a record at the end of a file summarizing
- the data, or providing a checksum. This can also be achieved with a
- callbacks in the step, normally as part of a custom
- ItemWriter. In this case, since a job is
- accumulating state that should not be lost if the job aborts, the
- ItemStream interface should be
- implemented:
+ In some situations, more information than the exit status may be
+ required to decide which step to execute next. In this case, a
+ JobExecutionDecider can be used to assist in the
+ decision.
- public class CustomItemWriter extends AbstractItemWriter implements
- ItemStream, StepListener
-{
+
+ public class MyDecider implements JobExecutionDecider {
- private static final String TOTAL_AMOUNT_KEY = "total.amount";
-
- private ItemWriter delegate;
-
- private double totalAmount = 0.0;
-
- public void setDelegate(ItemWriter delegate) { ... }
-
- public ExitStatus afterStep(StepExecution stepExecution) {
- // Add the footer record here...
- delegate.write("Total Amount Processed: " + totalAmount);
+ public String decide(JobExecution jobExecution, StepExecution stepExecution) {
+ if (someCondition) {
+ return "FAILED";
+ }
+ else {
+ return "COMPLETED";
+ }
}
- public void open(ExecutionContext executionContext) {
- if (executionContext.containsKey(TOTAL_AMOUNT_KEY) {
- totalAmount = executionContext.getDouble(TOTAL_AMOUNT_KEY);
- }
- }
+ }
- public void update(ExecutionContext executionContext) {
- executionContext.setDouble(TOTAL_AMOUNT_KEY, totalAmount);
- }
+
+
+ In the job configuration, a "decision" tag will specify the
+ decider to use as well as all of the transitions.
+
+
+ <job id="job">
+ <step name="step1" next="decision" />
- public void write(Object item) {
+ <decision id="skipCheckingDecision" decider="decider">
+ <next on="FAILED" to="step2" />
+ <next on="COMPLETED" to="step3" />
+ </step>
- delegate.write(item);
- totalAmount += ((Trade) item).getAmount();
+ <step name="step2" next="step3"/>
+ <step name="step3" />
+ </job>
- }
+ <bean id="decider" class="com.MyDecider"/>
-}
-
- The custom writer in the example is stateful (it maintains its
- total in an instance variable totalAmount), but the
- state is stored through the ItemStream interface
- in the ExecutionContext. In this way we can be
- sure that when the open() callback is received on a
- restart. The framework garuntees we always get the last value that was
- committed. It should be noted that it is not always necessary to
- implement ItemStream. For example, if the ItemWriter is re-runnable, in
- the sense that it maintains its own state in a transactional resource
- like a database, there is no need to maintain state within the writer
- itself.
+
\ No newline at end of file