diff --git a/src/site/docbook/reference/step.xml b/src/site/docbook/reference/step.xml
index 778be7e09..a415c0885 100644
--- a/src/site/docbook/reference/step.xml
+++ b/src/site/docbook/reference/step.xml
@@ -56,18 +56,15 @@
Below is a code representation of the same concepts shown
above:
-
- List items = new Arraylist();
- for(int i = 0; i < commitInterval; i++){
+ List items = new Arraylist();
+for(int i = 0; i < commitInterval; i++){
Object item = itemReader.read()
Object processedItem = itemProcessor.process(item);
items.add(processedItem);
- }
- itemWriter.write(items);
+}
+itemWriter.write(items);
-
-
-
+ Configuring a StepDespite the relatively short list of required dependencies for a
@@ -75,16 +72,13 @@
potentially contain many collaborators. In order to ease configuration,
the Spring Batch namespace can be used:
-
- <job id="sampleJob" job-repository="jobRepository">
+ <job id="sampleJob" job-repository="jobRepository">
<step id="step1">
- <tasklet transaction-manager="transactionManager">
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
- <tasklet>
+ <tasklet transaction-manager="transactionManager">
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ <tasklet>
</step>
- </job>
-
-
+</job>The configuration above represents the only required dependencies
to create a item-oriented step:
@@ -129,7 +123,7 @@
writer.
-
+ Referencing a Standalone StepWhile steps must exist within a Job to define the flow, it can
@@ -138,18 +132,15 @@
reference it from multiple jobs. This can be achieved with the 'parent'
attribute:
-
- <job id="sampleJob" job-repository="jobRepository">
+ <job id="sampleJob" job-repository="jobRepository">
<step id="step1" parent="standaloneStep" />
- </job>
+</job>
- <step id="standaloneStep">
- <tasklet job-repository="jobRepository" transaction-manager="transactionManager">
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
- </tasklet>
- </step>
-
-
+<step id="standaloneStep">
+ <tasklet job-repository="jobRepository" transaction-manager="transactionManager">
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ </tasklet>
+</step>It should be noted that the id attribute is still required on the
step within the job element. This is for two reasons:
@@ -186,22 +177,19 @@
allowStartIfComplete=true. Additionally, the commitInterval will be '5'
since it is overridden by the "concreteStep1":
-
- <step id="parentStep">
- <tasklet allow-start-if-complete="true">
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
- </tasklet>
- </step>
+ <step id="parentStep">
+ <tasklet allow-start-if-complete="true">
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ </tasklet>
+</step>
- <step id="concreteStep1" parent="parentStep">
- <tasklet start-limit="5">
- <chunk processor="itemProcessor" commit-interval="5"/>
- </tasklet>
- </step>
+<step id="concreteStep1" parent="parentStep">
+ <tasklet start-limit="5">
+ <chunk processor="itemProcessor" commit-interval="5"/>
+ </tasklet>
+</step>
-
-
-
+ Abstract StepSometimes it may be necessary to define a parent
@@ -219,23 +207,20 @@
be abstract. The Step "concreteStep2" will have
'itemReader', 'itemWriter', and commitInterval=10.
-
- <step id="abstractParentStep" abstract="true">
- <tasklet>
- <chunk commit-interval="10"/>
- </tasklet>
- </step>
+ <step id="abstractParentStep" abstract="true">
+ <tasklet>
+ <chunk commit-interval="10"/>
+ </tasklet>
+</step>
- <step id="concreteStep2" parent="abstractParentStep">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter"/>
- </tasklet>
- </step>
-
-
+<step id="concreteStep2" parent="abstractParentStep">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter"/>
+ </tasklet>
+</step>
-
+ Merging ListsSome of the configurable elements on
@@ -253,27 +238,24 @@
com.ListenerOne and
com.ListenerTwo:
-
- <step id="listenersParentStep" abstract="true">
- <listeners>
- <listener class="com.ListenerOne"/>
- <listeners>
- </step>
+ <step id="listenersParentStep" abstract="true">
+ <listeners>
+ <listener class="com.ListenerOne"/>
+ <listeners>
+</step>
- <step id="concreteStep3" parent="listenersParentStep">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="5"/>
- <listeners merge="true">
- <listener class="com.ListenerTwo"/>
- <listeners>
- </tasklet>
- </step>
-
-
+<step id="concreteStep3" parent="listenersParentStep">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="5"/>
+ <listeners merge="true">
+ <listener class="com.ListenerTwo"/>
+ <listeners>
+ </tasklet>
+</step>
-
+ The Commit IntervalAs mentioned above, a step reads in and writes out items,
@@ -288,17 +270,14 @@
number of items that are processed within a commit can be
configured.
-
- <job id="sampleJob">
+ <job id="sampleJob">
<step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
- </tasklet>
+ </tasklet>
</step>
- </job>
-
-
+</job>In the example above, 10 items will be processed within each
transaction. At the beginning of processing a transaction is begun, and
@@ -309,14 +288,14 @@
committed.
-
+ Configuring a Step for RestartIn , restarting a
Job was discussed. Restart has numerous impacts
on steps, and as such may require some specific configuration.
-
+ Setting a StartLimitThere are many scenarios where you may want to control the
@@ -330,14 +309,11 @@
as a Step that can be run infinitely. Below is
an example start limit configuration:
-
- <step id="step1">
+ <step id="step1">
<tasklet start-limit="1">
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
</tasklet>
- </step>
-
-
+</step>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
@@ -345,7 +321,7 @@
Integer.MAX_VALUE.
-
+ Restarting a completed stepIn the case of a restartable job, there may be one or more steps
@@ -357,42 +333,36 @@
successfully, will be skipped. Setting allow-start-if-complete to
"true" overrides this so that the step will always run:
-
- <step id="step1">
+ <step id="step1">
<tasklet allow-start-if-complete="true">
- <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
</tasklet>
- </step>
-
-
+</step>
-
+ Step restart configuration example
-
- <job id="footballJob" restartable="true">
+ <job id="footballJob" restartable="true">
<step id="playerload" next="gameLoad">
- <tasklet>
- <chunk reader="playerFileItemReader" writer="playerWriter"
- commit-interval="10" />
- </tasklet>
+ <tasklet>
+ <chunk reader="playerFileItemReader" writer="playerWriter"
+ commit-interval="10" />
+ </tasklet>
</step>
<step id="gameLoad" next="playerSummarization">
- <tasklet allow-start-if-complete="true">
- <chunk reader="gameFileItemReader" writer="gameWriter"
- commit-interval="10"/>
- </tasklet>
+ <tasklet allow-start-if-complete="true">
+ <chunk reader="gameFileItemReader" writer="gameWriter"
+ commit-interval="10"/>
+ </tasklet>
</step>
<step id="playerSummarization">
- <tasklet start-limit="3">
- <chunk reader="playerSummarizationSource" writer="summaryWriter"
- commit-interval="10"/>
- </tasklet>
+ <tasklet start-limit="3">
+ <chunk reader="playerSummarizationSource" writer="summaryWriter"
+ commit-interval="10"/>
+ </tasklet>
</step>
- </job>
-
-
+</job>The above example configuration is for a job that loads in
information about football games and summarizes them. It contains
@@ -492,7 +462,7 @@
-
+ Configuring Skip LogicThere are many scenarios where errors encountered while processing
@@ -505,19 +475,16 @@
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 id="step1">
- <tasklet>
- <chunk reader="flatFileItemReader" writer="itemWriter" commit-interval="10" <step id="step1">
+ <tasklet>
+ <chunk reader="flatFileItemReader" writer="itemWriter" commit-interval="10" skip-limit="10">
- <skippable-exception-classes>
- org.springframework.batch.item.file.FlatFileParseException
- </skippable-exception-classes>
- </chunk>
- </tasklet>
- </step>
-
-
+ <skippable-exception-classes>
+ org.springframework.batch.item.file.FlatFileParseException
+ </skippable-exception-classes>
+ </chunk>
+ </tasklet>
+</step>
In this example, a FlatFileItemReader is
used, and if at any point a
@@ -528,7 +495,7 @@
writes (regardless of success or failure).
-
+ Configuring Fatal ExceptionsOne problem with the example above is that any other exception
@@ -536,22 +503,19 @@
Job to fail. In certain scenarios this may be the
correct behavior. However, in other scenarios it may be easier to
identify which exceptions should cause failure and skip everything
- else:
- <step id="step1">
- <tasklet>
- <chunk reader="flatFileItemReader" writer="itemWriter" commit-interval="10" <step id="step1">
+ <tasklet>
+ <chunk 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>
- </chunk>
- </tasklet>
- </step>
-
-
+ <skippable-exception-classes>
+ java.lang.Exception
+ </skippable-exception-classes>
+ <fatal-exception-classes>
+ java.io.FileNotFoundException
+ </fatal-exception-classes>
+ </chunk>
+ </tasklet>
+</step>
By setting the skippable exceptions to
java.lang.Exception, any exception that is thrown
@@ -559,7 +523,7 @@
contains specific exceptions that should be fatal if encountered.
-
+ Configuring Retry LogicIn most cases you want an exception to cause either a skip or
@@ -573,19 +537,16 @@
process holds a lock on, waiting and trying again might result in
success. In this case, retry should be configured:
-
- <step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="2" <step id="step1">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="2" retry-limit="3">
- <retryable-exception-classes>
- org.springframework.dao.DeadlockLoserDataAccessException
- </retryable-exception-classes>
- </chunk>
- </tasklet>
- </step>
-
-
+ <retryable-exception-classes>
+ org.springframework.dao.DeadlockLoserDataAccessException
+ </retryable-exception-classes>
+ </chunk>
+ </tasklet>
+</step>
The Step allows a limit for the number of
times an individual item can be retried, and a list of exceptions that
@@ -593,8 +554,8 @@
linkend="retry" />
-
- Controlling rollback
+
+ Controlling RollbackBy default, regardless of retry or skip, any exceptions thrown
from the ItemWriter will cause the transaction
@@ -609,20 +570,17 @@
no-rollback-exception-classes element is a list of transaction
attributes, separated by commas or newlines.
-
- <step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
- <no-rollback-exception-classes>
- org.springframework.batch.item.validator.ValidationException
- </no-rollback-exception-classes>
- </tasklet>
- </step>
-
-
+ <step id="step1">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
+ <no-rollback-exception-classes>
+ org.springframework.batch.item.validator.ValidationException
+ </no-rollback-exception-classes>
+ </tasklet>
+</step>
- Transactional readers
+ Transactional ReadersThe basic contract of the ItemReader is
that it is forward only. The step buffers reader input, so that in the
@@ -634,36 +592,32 @@
this reason, the step can be configured to not buffer the
items:
-
- <step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="2"
- is-reader-transactional-queue="true"/>
- </tasklet>
- </step>
-
-
+ <step id="step1">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="2"
+ is-reader-transactional-queue="true"/>
+ </tasklet>
+</step>
-
+ Transaction AttributesTransaction attributes can be used to control the isolation,
- propgation, and timeout settings. More information on setting
+ propagation, and timeout settings. More information on setting
transaction attributes can be found in the spring core
documentation.
- <step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
- <transaction-attributes isolation="DEFAULT" propagation="REQUIRED" timeout="30"/>
- </tasklet>
- </step>
-
+ <step id="step1">
+ <tasklet>
+ <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
+ <transaction-attributes isolation="DEFAULT" propagation="REQUIRED" timeout="30"/>
+ </tasklet>
+</step>
-
+ Registering ItemStreams with the StepThe step has to take care of ItemStream
@@ -684,29 +638,26 @@
can be registered on the Step through the
'streams' element, as illustrated below:
-
- <step id="step1">
- <tasklet>
- <chunk reader="itemReader" writer="compositeWriter" commit-interval="2">
- <streams>
- <stream ref="fileItemWriter1"/>
- <stream ref="fileItemWriter2"/>
- </streams>
- </chunk>
- </tasklet>
- </step>
+ <step id="step1">
+ <tasklet>
+ <chunk reader="itemReader" writer="compositeWriter" commit-interval="2">
+ <streams>
+ <stream ref="fileItemWriter1"/>
+ <stream ref="fileItemWriter2"/>
+ </streams>
+ </chunk>
+ </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>
-
-
+<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
@@ -720,7 +671,7 @@
of a failure.
-
+ Intercepting Step ExecutionJust as with the Job, there are many events
@@ -736,22 +687,19 @@
interface (or an extension thereof) can be applied to a step via the
listeners element:
-
- <step id="step1">
- <tasklet>
- <chunk reader="reader" writer="writer" commit-interval="10"/>
- <listeners>
- <listener ref="stepListener"/>
- </listeners>
- </tasklet>
- </step>
-
-
+ <step id="step1">
+ <tasklet>
+ <chunk reader="reader" writer="writer" commit-interval="10"/>
+ <listeners>
+ <listener ref="stepListener"/>
+ </listeners>
+ </tasklet>
+</step>In addition to the StepListener interfaces,
annotations are provided to address the same concerns.
-
+ StepExecutionListenerStepExecutionListener represents the most
@@ -759,15 +707,13 @@
for notification before a Step is started and
after it has ends, whether it ended normally or failed:
-
- public interface StepExecutionListener extends StepListener {
+ public interface StepExecutionListener extends StepListener {
void beforeStep(StepExecution stepExecution);
ExitStatus afterStep(StepExecution stepExecution);
- }
-
+}ExitStatus is the return type of
afterStep in order to allow listeners the
@@ -787,7 +733,7 @@
-
+ ChunkListenerA chunk is defined as the items processed within the scope of a
@@ -796,12 +742,13 @@
useful to perform logic before a chunk begins processing or after a
chunk has completed:
- public interface ChunkListener extends StepListener {
+ public interface ChunkListener extends StepListener {
void beforeChunk();
void afterChunk();
- }
+
+}The beforeChunk method is called after
the transaction is started, but before read
@@ -824,19 +771,20 @@
-
+ ItemReadListenerWhen 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 {
+ ItemReaderListener:public interface ItemReadListener<T> extends StepListener {
void beforeRead();
void afterRead(T item);
void onReadError(Exception ex);
+
}The beforeRead method will be called
@@ -866,19 +814,20 @@
-
+ ItemProcessListenerJust as with the ItemReadListener, the
processing of an item can be 'listened' to:
- public interface ItemProcessListener<T, S> extends StepListener {
+ public interface ItemProcessListener<T, S> extends StepListener {
void beforeProcess(T item);
void afterProcess(T item, S result);
void onProcessError(T item, Exception e);
+
}The beforeProcess method will be called
@@ -908,7 +857,7 @@
-
+ ItemWriteListenerThe writing of an item can be 'listened' to with the
@@ -921,6 +870,7 @@
void afterWrite(List<? extends S> items);
void onWriteError(Exception exception, List<? extends S> items);
+
}
The beforeWrite method will be called
@@ -949,7 +899,7 @@
-
+ SkipListenerItemReadListener,
@@ -969,9 +919,8 @@
void onSkipInProcess(T item, Throwable t);
void onSkipInWrite(S item, Throwable t);
- }
-
+}
onSkipInRead will be called whenever an
item is skipped while reading. It should be noted that rollbacks may
@@ -997,7 +946,7 @@
-
+ SkipListeners and TransactionsOne of the most common use cases for a
@@ -1026,7 +975,7 @@
-
+ TaskletStepChunk-oriented processing is not the only way to process in a
@@ -1049,12 +998,9 @@
Tasklet object; no <chunk/> element should be
used within the <tasket/>:
-
- <step id="step1">
- <tasklet ref="myTasklet"/>
- </step>
-
-
+ <step id="step1">
+ <tasklet ref="myTasklet"/>
+</step>TaskletStep will automatically register the
@@ -1062,11 +1008,11 @@
interface
-
+ TaskletAdapter
- As with other adapters for the ItemWriter
- and ItemReader interfaces, the
+ As with other adapters for the ItemReader
+ and ItemWriter interfaces, the
Tasklet interface contains an implementation that
allows for adapting itself to any pre-existing class:
TaskletAdapter. An example where this may be
@@ -1075,19 +1021,16 @@
this class without having to write an adapter for the
Tasklet interface:
-
- <bean id="myTasklet" class="org.springframework.batch.core.step.tasklet.TaskletAdapter">
+ <bean id="myTasklet" class="org.springframework.batch.core.step.tasklet.TaskletAdapter">
<property name="targetObject">
- <bean class="org.mycompany.FooDao">
+ <bean class="org.mycompany.FooDao">
</property>
<property name="targetMethod" value="updateFoo" />
- </bean>
-
-
+</bean>
-
- Example Tasklet implementation
+
+ Example Tasklet ImplementationMany batch jobs contain steps that must be done before the main
processing begins in order to set up various resources or after
@@ -1098,34 +1041,34 @@
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;
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
- File dir = directory.getFile();
- Assert.state(dir.isDirectory());
+ File dir = directory.getFile();
+ Assert.state(dir.isDirectory());
- File[] files = dir.listFiles();
- for (int i = 0; i < files.length; i++) {
- boolean deleted = files[i].delete();
- if (!deleted) {
- throw new UnexpectedJobExecutionException("Could not delete file " +
- files[i].getPath());
+ File[] files = dir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ boolean deleted = files[i].delete();
+ if (!deleted) {
+ throw new UnexpectedJobExecutionException("Could not delete file " +
+ files[i].getPath());
+ }
}
- }
- return RepeatStatus.COMPLETED;
+ return RepeatStatus.COMPLETED;
}
public void setDirectoryResource(Resource directory) {
- this.directory = directory;
+ this.directory = directory;
}
public void afterPropertiesSet() throws Exception {
- Assert.notNull(directory, "directory must be set");
+ Assert.notNull(directory, "directory must be set");
}
- }
+}The above Tasklet implementation will
delete all files within a given directory. It should be noted that the
@@ -1133,24 +1076,21 @@
that is left is to reference the Tasklet from the
Step:
-
- <job id="taskletJob">
+ <job id="taskletJob">
<step id="deleteFilesInDir">
<tasklet ref="fileDeletingTasklet"/>
</step>
- </job>
+</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>
-
-
+<beans:bean id="fileDeletingTasklet"
+ class="org.springframework.batch.sample.tasklet.FileDeletingTasklet">
+ <beans:property name="directoryResource">
+ <beans:bean id="directory"
+ class="org.springframework.core.io.FileSystemResource">
+ <beans:constructor-arg value="target/test-outputs/test-dir" />
+ </beans:bean>
+ </beans:property>
+</beans:bean>
@@ -1166,7 +1106,7 @@
group of Steps is configured, certain steps may not even be processed at
all.
-
+ Sequential FlowThe simplest flow scenario is a job where all of the steps execute
@@ -1187,18 +1127,15 @@
This can be achieved using the 'next' attribute of the step
element:
-
- <job id="job">
+ <job id="job">
<step id="stepA" parent="s1" next="stepB" />
<step id="stepB" parent="s2" next="stepC"/>
<step id="stepC" parent="s3" />
- </job>
-
-In the scenario above, 'step A' will execute first because it
- is the first Step listed. 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.
+</job>In the scenario above, 'step A' will execute
+ first because it is the first Step listed. 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.With the Spring Batch namespace, the first step listed in the
@@ -1209,7 +1146,7 @@
-
+ Conditional FlowIn the example above, there are only two possibilities:
@@ -1248,29 +1185,26 @@
Job which Step to execute
next. However, unlike the attribute, any number of "next" elements are
allowed on a given Step, and there is no default
- behavior the the case of failure. This means that if transition elements
- are used, then all of the behavior for the Step's
+ behavior the case of failure. This means that if transition elements are
+ used, then all of the behavior for the Step's
transitions must be defined explicitly. Note also that a single step
- cannot have both a "next" attribute and a transtion element.
+ cannot have both a "next" attribute and a transition element.
The next element specifies a pattern to match and the step to
execute next:
-
- <job id="job">
+ <job id="job">
<step id="stepA" parent="s1">
- <next on="FAILED" to="stepB" />
- <next on="*" to="stepC" />
+ <next on="*" to="stepB" />
+ <next on="FAILED" to="stepC" />
</step>
<step id="stepB" parent="s2" next="stepC" />
<step id="stepC" parent="s3" />
- </job>
-
-
+</job>The "on" attribute of a transition element uses a simple
pattern-matching scheme to match the ExitStatus
- that results from the exeution of the Step. Only
+ that results from the execution of the Step. Only
two special characters are allowed in the pattern:
@@ -1296,7 +1230,7 @@
"stepA" in the example above, an ExitStatus of
"FAILED" would still go to "stepB".
-
+ Batch Status vs. Exit StatusWhen configuring a Job for conditional
@@ -1314,10 +1248,7 @@
it fails, and so on. The example above contains the following 'next'
element:
-
- <next on="FAILED" to="stepB" />
-
-
+ <next on="FAILED" to="stepB" />At first glance, it would appear that the 'on' attribute
references the BatchStatus of the
@@ -1334,14 +1265,11 @@
code needs to be different? A good example comes from the skip sample
job within the samples project:
-
- <step id="step1" parent="s1">
+ <step id="step1" parent="s1">
<end on="FAILED" />
<next on="COMPLETED WITH SKIPS" to="errorPrint1" />
<next on="*" to="step2" />
- </step>
-
-
+</step>The above step has three possibilities:
@@ -1367,18 +1295,19 @@
change the exit code based on the condition of the execution having
skipped records:
- public class SkipCheckingListener extends StepExecutionListenerSupport {
+ public class SkipCheckingListener extends StepExecutionListenerSupport {
public ExitStatus afterStep(StepExecution stepExecution) {
- if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
+ if (!stepExecution.getExitStatus().getExitCode().equals(ExitStatus.FAILED.getExitCode())
&& stepExecution.getSkipCount() > 0) {
- return new ExitStatus("COMPLETED WITH SKIPS");
- } else {
- return null;
- }
+ return new ExitStatus("COMPLETED WITH SKIPS");
+ }
+ else {
+ return null;
+ }
}
- }
+}The above code is a StepExecutionListener
that first checks to make sure the Step was
@@ -1389,7 +1318,7 @@
-
+ Configuring for StopAfter the discussion of Job will
end:
- <step id="stepC" parent="s3"/>
+ <step id="stepC" parent="s3"/>If no transitions are defined for a Step,
then the Job's statuses will be defined as
@@ -1421,7 +1350,7 @@
- Otherwise, the the Job's
+ Otherwise, the Job's
BatchStatus and
ExitStatus will both be COMPLETED.
@@ -1443,7 +1372,7 @@
in a job to have a status of FAILED but the job to have a status of
COMPLETED, or vise versa.
-
+ The 'End' ElementThe 'end' element instructs a Job to stop
@@ -1465,15 +1394,17 @@
fails, the Job will not be restartable (because
the status is COMPLETED).
- <step id="step1" parent="s1" next="step2">
- <step id="step2" parent="s2">
+ <step id="step1" parent="s1" next="step2">
+
+<step id="step2" parent="s2">
<end on="FAILED"/>
<next on="*" to="step3"/>
- </step>
- <step id="step3" parent="s3">
+</step>
+
+<step id="step3" parent="s3">
-
+ The 'Fail' ElementThe 'fail' element instructs a Job to
@@ -1494,15 +1425,17 @@
Additionally, if step2 fails, and the Job is
restarted, then execution will begin again on step2.
- <step id="step1" parent="s1" next="step2">
- <step id="step2" parent="s2">
+ <step id="step1" parent="s1" next="step2">
+
+<step id="step2" parent="s2">
<fail on="FAILED" exit-code="EARLY TERMINATION"/>
<next on="*" to="step3"/>
- </step>
- <step id="step3" parent="s3">
+</step>
+
+<step id="step3" parent="s3">
-
+ The 'Stop' ElementThe 'stop' element instructs a Job to
@@ -1513,19 +1446,20 @@
attribute that specifies the step where execution should pick up when
the Job is restarted.
- In the following scenario, if step1 finsihes with COMPLETE, then
+ In the following scenario, if step1 finishes with COMPLETE, then
the job will then stop. Once it is restarted, execution will begin on
step2.
- <step id="step1" parent="s1">
+ <step id="step1" parent="s1">
<stop on="COMPLETED" restart="step2"/>
- </step>
- <step id="step2" parent="s2"/>
+</step>
+
+<step id="step2" parent="s2"/>
-
- Programmatic flow decisions
+
+ Programmatic Flow DecisionsIn some situations, more information than the
ExitStatus may be required to decide which step
@@ -1533,39 +1467,33 @@
JobExecutionDecider can be used to assist in the
decision.
-
- public class MyDecider implements JobExecutionDecider {
- public String decide(JobExecution jobExecution, StepExecution stepExecution) {
- if (someCondition) {
- return "FAILED";
- }
- else {
- return "COMPLETED";
- }
- }
- }
-
-
+ public class MyDecider implements JobExecutionDecider {
+ public String decide(JobExecution jobExecution, StepExecution stepExecution) {
+ if (someCondition) {
+ return "FAILED";
+ }
+ else {
+ return "COMPLETED";
+ }
+ }
+}In the job configuration, a "decision" tag will specify the
decider to use as well as all of the transitions.
-
- <job id="job">
+ <job id="job">
<step id="step1" parent="s1" next="decision" />
<decision id="decision" decider="decider">
- <next on="FAILED" to="step2" />
- <next on="COMPLETED" to="step3" />
+ <next on="FAILED" to="step2" />
+ <next on="COMPLETED" to="step3" />
</decision>
<step id="step2" parent="s2" next="step3"/>
<step id="step3" parent="s3" />
- </job>
+</job>
- <beans:bean id="decider" class="com.MyDecider"/>
-
-
+<beans:bean id="decider" class="com.MyDecider"/>
@@ -1582,37 +1510,34 @@
elements such as the 'next' attribute or the 'next', 'end', 'fail', or
'pause' elements.
- <split id="split1" next="step4">
+ <split id="split1" next="step4">
<flow>
- <step id="step1" parent="s1" next="step2"/>
- <step id="step2" parent="s2"/>
+ <step id="step1" parent="s1" next="step2"/>
+ <step id="step2" parent="s2"/>
</flow>
<flow>
- <step id="step3" parent="s3"/>
+ <step id="step3" parent="s3"/>
</flow>
- </split>
- <step id="step4" parent="s4"/>
+</split>
+<step id="step4" parent="s4"/>
- Late binding of Job and Step Attributes
+ Late Binding of Job and Step AttributesBoth the XML and Flat File examples above use the Spring
- Resource abstraction to obtain a file . This works
+ Resource abstraction to obtain a file. This works
because Resource has a getFile
method, which returns a java.io.File. Both XML and
Flat File resources can be configured using standard Spring
constructs:
-
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource"
- value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" />
- </bean>
-
-
+ value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" />
+</bean>The above Resource will load the file from
the file system location specified. Note that absolute locations have to
@@ -1622,13 +1547,10 @@
at runtime as a parameter to the job. This could be solved using '-D'
parameters, i.e. a system property:
-
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="${input.file.name}" />
- </bean>
-
-
+</bean>All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that although
@@ -1644,34 +1566,31 @@
accomplish this, Spring Batch allows for the late binding of various Job
and Step attributes:
-
- <bean id="flatFileItemReader" scope="step"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader" scope="step"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{jobParameters[input.file.name]}" />
- </bean>
-
-
+</bean>Both the JobExecution and
StepExecution level
ExecutionContext can be accessed in the same
way:
-
- <bean id="flatFileItemReader" scope="step"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader" scope="step"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{jobExecutionContext[input.file.name]}" />
- </bean>
+</bean>
-
-
-
- <bean id="flatFileItemReader" scope="step"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader" scope="step"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{stepExecutionContext[input.file.name]}" />
- </bean>
+</bean>
-
+
+ Any bean that uses late-binding must be declared with
+ scope="step". See for more
+ information.
+ Step Scope
@@ -1679,13 +1598,10 @@
All of the late binding examples from above have a scope of "step"
declared on the bean definition:
-
- <bean id="flatFileItemReader" scope="step"
- class="org.springframework.batch.item.file.FlatFileItemReader">
+ <bean id="flatFileItemReader" scope="step"
+ class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{jobParameters[input.file.name]}" />
- </bean>
-
-
+</bean>Using a scope of Step is required in order
to use late binding since the bean cannot actually be instantiated until
@@ -1694,22 +1610,17 @@
must be added explicitly, either by using the batch
namespace:
-
-<beans xmlns="http://www.springframework.org/schema/beans"
+ <beans:beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="...">
...
-</beans>
-
+</beans:beans>or by including a bean definition explicitly for
theStep (but not both):
-
- <bean class="org.springframework.batch.core.scope.StepScope" />
-
-
+ <bean class="org.springframework.batch.core.scope.StepScope" />