Made the "Both" option make sense

I edited the leader paragraphs above each code block that
was flagged as being XML or Java content, such that the
paragraphs make sense when both the XML and the Java blocks
are present. I turned the Both button back on. I also caught
a bunch of other editing things.

Mahmoud Ben Hassine caught a couple of sentences that were
problematic when the Both option is on. I then caught a couple
of other problems. I fixed all of that.

Thanks for reading closely, Mahmoud. I always appreciate that.
This commit is contained in:
Jay Bryant
2018-01-30 09:10:31 -06:00
committed by Mahmoud Ben Hassine
parent ae6cd59155
commit efdce562b0
13 changed files with 1521 additions and 1220 deletions

View File

@@ -50,8 +50,10 @@ public class ItemFailureLoggerListener extends ItemListenerSupport {
} }
---- ----
Having implemented this listener, it must be registered with a step, as shown in the Having implemented this listener, it must be registered with a step.
following example:
[role="xmlContent"]
The following example shows how to register a listener with a step in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -66,6 +68,9 @@ following example:
</step> </step>
---- ----
[role="javaContent"]
The following example shows how to register a listener with a step Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -134,8 +139,10 @@ public class EarlyCompletionItemReader implements ItemReader<T> {
The previous example actually relies on the fact that there is a default implementation The previous example actually relies on the fact that there is a default implementation
of the `CompletionPolicy` strategy that signals a complete batch when the item to be of the `CompletionPolicy` strategy that signals a complete batch when the item to be
processed is `null`. A more sophisticated completion policy could be implemented and processed is `null`. A more sophisticated completion policy could be implemented and
injected into the `Step` through the `SimpleStepFactoryBean`, as shown in the following injected into the `Step` through the `SimpleStepFactoryBean`.
example:
[role="xmlContent"]
The following example shows how to inject a completion policy into a step in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -150,6 +157,9 @@ example:
<bean id="completionPolicy" class="org.example...SpecialCompletionPolicy"/> <bean id="completionPolicy" class="org.example...SpecialCompletionPolicy"/>
---- ----
[role="javaContent"]
The following example shows how to inject a completion policy into a step in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -196,12 +206,15 @@ so this is always an abnormal ending to a job.
[[addingAFooterRecord]] [[addingAFooterRecord]]
=== Adding a Footer Record === Adding a Footer Record
Often, when writing to flat files, a "footer" record must be appended to the end of the Often, when writing to flat files, a "`footer`" record must be appended to the end of the
file, after all processing has be completed. This can be achieved using the file, after all processing has be completed. This can be achieved using the
`FlatFileFooterCallback` interface provided by Spring Batch. The `FlatFileFooterCallback` `FlatFileFooterCallback` interface provided by Spring Batch. The `FlatFileFooterCallback`
(and its counterpart, the `FlatFileHeaderCallback`) are optional properties of the (and its counterpart, the `FlatFileHeaderCallback`) are optional properties of the
`FlatFileItemWriter` and can be added to an item writer as shown in the following `FlatFileItemWriter` and can be added to an item writer.
example:
[role="xmlContent"]
The following example shows how to use the `FlatFileHeaderCallback` and the
`FlatFileFooterCallback` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -214,6 +227,10 @@ example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to use the `FlatFileHeaderCallback` and the
`FlatFileFooterCallback` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -292,7 +309,10 @@ method, once we are guaranteed that no exceptions are thrown, that we update the
In order for the `writeFooter` method to be called, the `TradeItemWriter` (which In order for the `writeFooter` method to be called, the `TradeItemWriter` (which
implements `FlatFileFooterCallback`) must be wired into the `FlatFileItemWriter` as the implements `FlatFileFooterCallback`) must be wired into the `FlatFileItemWriter` as the
`footerCallback`. The following example shows how to do so: `footerCallback`.
[role="xmlContent"]
The following example shows how to wire the `TradeItemWriter` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -308,6 +328,9 @@ implements `FlatFileFooterCallback`) must be wired into the `FlatFileItemWriter`
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to wire the `TradeItemWriter` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -409,7 +432,10 @@ multi-line record as a group, so that it can be passed to the `ItemWriter` intac
Because a single record spans multiple lines and because we may not know how many lines Because a single record spans multiple lines and because we may not know how many lines
there are, the `ItemReader` must be careful to always read an entire record. In order to there are, the `ItemReader` must be careful to always read an entire record. In order to
do this, a custom `ItemReader` should be implemented as a wrapper for the do this, a custom `ItemReader` should be implemented as a wrapper for the
`FlatFileItemReader`, as shown in the following example: `FlatFileItemReader`.
[role="xmlContent"]
The following example shows how to implement a custom `ItemReader` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -429,6 +455,9 @@ do this, a custom `ItemReader` should be implemented as a wrapper for the
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to implement a custom `ItemReader` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -459,7 +488,10 @@ delegate `FlatFileItemReader`. See
link:readersAndWriters.html#flatFileItemReader[`FlatFileItemReader` in the Readers and link:readersAndWriters.html#flatFileItemReader[`FlatFileItemReader` in the Readers and
Writers chapter] for more details. The delegate reader then uses a Writers chapter] for more details. The delegate reader then uses a
`PassThroughFieldSetMapper` to deliver a `FieldSet` for each line back to the wrapping `PassThroughFieldSetMapper` to deliver a `FieldSet` for each line back to the wrapping
`ItemReader`, as shown in the following example: `ItemReader`.
[role="xmlContent"]
The following example shows how to ensure that each line is properly tokenized in XML:
.XML Content .XML Content
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -476,6 +508,9 @@ Writers chapter] for more details. The delegate reader then uses a
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to ensure that each line is properly tokenized in Java:
.Java Content .Java Content
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -545,7 +580,10 @@ common metadata about the run would be lost. Furthermore, a multi-step job would
need to be split up into multiple jobs as well. need to be split up into multiple jobs as well.
Because the need is so common, Spring Batch provides a `Tasklet` implementation for Because the need is so common, Spring Batch provides a `Tasklet` implementation for
calling system commands, as shown in the following example: calling system commands.
[role="xmlContent"]
The following example shows how to call an external command in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -557,6 +595,9 @@ calling system commands, as shown in the following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to call an external command in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -639,13 +680,16 @@ public class SavingItemWriter implements ItemWriter<Object> {
} }
---- ----
To make the data available to future `Steps`, it must be "promoted" to the `Job` To make the data available to future `Steps`, it must be "`promoted`" to the `Job`
`ExecutionContext` after the step has finished. Spring Batch provides the `ExecutionContext` after the step has finished. Spring Batch provides the
`ExecutionContextPromotionListener` for this purpose. The listener must be configured `ExecutionContextPromotionListener` for this purpose. The listener must be configured
with the keys related to the data in the `ExecutionContext` that must be promoted. It can with the keys related to the data in the `ExecutionContext` that must be promoted. It can
also, optionally, be configured with a list of exit code patterns for which the promotion also, optionally, be configured with a list of exit code patterns for which the promotion
should occur (`COMPLETED` is the default). As with all listeners, it must be registered should occur (`COMPLETED` is the default). As with all listeners, it must be registered
on the `Step` as shown in the following example: on the `Step`.
[role="xmlContent"]
The following example shows how to promote a step to the `Job` `ExecutionContext` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -674,6 +718,9 @@ on the `Step` as shown in the following example:
</beans:bean> </beans:bean>
---- ----
[role="xmlContent"]
The following example shows how to promote a step to the `Job` `ExecutionContext` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----

View File

@@ -63,10 +63,11 @@ global to all steps, such as restartability. The job configuration contains:
ifdef::backend-html5[] ifdef::backend-html5[]
[role="javaContent"] [role="javaContent"]
A default simple implementation of the Job interface is provided by Spring Batch in the For those who use Java configuration, Spring Batch provices a default implementation of
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`. the Job interface in the form of the `SimpleJob` class, which creates some standard
When using java based configuration, a collection of builders is made available for the functionality on top of `Job`. When using java based configuration, a collection of
instantiation of a `Job`, as shown in the following example: builders is made available for the instantiation of a `Job`, as shown in the following
example:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -82,10 +83,11 @@ public Job footballJob() {
---- ----
[role="xmlContent"] [role="xmlContent"]
A default simple implementation of the `Job` interface is provided by Spring Batch in the For those who use XML configuration, Spring Batch provides a default implementation of the
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`. `Job` interface in the form of the `SimpleJob` class, which creates some standard
However, the batch namespace abstracts away the need to instantiate it directly. Instead, functionality on top of `Job`. However, the batch namespace abstracts away the need to
the `<job>` tag can be used as shown in the following example: instantiate it directly. Instead, the `<job>` element can be used, as shown in the
following example:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -98,9 +100,9 @@ the `<job>` tag can be used as shown in the following example:
endif::backend-html5[] endif::backend-html5[]
ifdef::backend-pdf[] ifdef::backend-pdf[]
A default simple implementation of the Job interface is provided by Spring Batch in the Spring Batch provides a default implementation of the Job interface in the form of the
form of the `SimpleJob` class, which creates some standard functionality on top of `Job`. `SimpleJob` class, which creates some standard functionality on top of `Job`. When using
When using java based configuration, a collection of builders are made available for the Java-based configuration, a collection of builders are made available for the
instantiation of a `Job`, as shown in the following example: instantiation of a `Job`, as shown in the following example:
[source, java] [source, java]
@@ -565,8 +567,8 @@ the course of execution, `StepExecution` and `JobExecution` implementations are
by passing them to the repository. by passing them to the repository.
[role="xmlContent"] [role="xmlContent"]
The batch namespace provides support for configuring a `JobRepository` instance with the The Spring Batch XML namespace provides support for configuring a `JobRepository` instance
`<job-repository>` tag, as shown in the following example: with the `<job-repository>` tag, as shown in the following example:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -574,7 +576,7 @@ The batch namespace provides support for configuring a `JobRepository` instance
---- ----
[role="javaContent"] [role="javaContent"]
When using java configuration, `@EnableBatchProcessing` annotation provides a When using Java configuration, the `@EnableBatchProcessing` annotation provides a
`JobRepository` as one of the components automatically configured out of the box. `JobRepository` as one of the components automatically configured out of the box.
=== JobLauncher === JobLauncher

View File

@@ -30,7 +30,7 @@ options and runtime concerns of a `Job`.
ifdef::backend-html5[] ifdef::backend-html5[]
[role="javaContent"] [role="javaContent"]
There are multiple implementations of the <<job.adoc#configureJob,`Job`>> interface, however There are multiple implementations of the <<job.adoc#configureJob,`Job`>> interface. However,
builders abstract away the difference in configuration. builders abstract away the difference in configuration.
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -56,10 +56,9 @@ builders can also contain other elements that help with parallelisation (`Split`
declarative flow control (`Decision`) and externalization of flow definitions (`Flow`). declarative flow control (`Decision`) and externalization of flow definitions (`Flow`).
[role="xmlContent"] [role="xmlContent"]
There are multiple implementations of the <<job.adoc#configureJob,`Job`>> interface, however, the namespace Whether you use Java or XML, there are multiple implementations of the <<job.adoc#configureJob,`Job`>>
abstracts away the differences in configuration. It has only three interface. However, the namespace abstracts away the differences in configuration. It has
required dependencies: a name, a `JobRepository` , and only three required dependencies: a name, `JobRepository` , and a list of `Step` instances.
a list of `Step` instances.
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -71,8 +70,8 @@ a list of `Step` instances.
---- ----
[role="xmlContent"] [role="xmlContent"]
The examples here use a parent bean definition to create the steps; The examples here use a parent bean definition to create the steps.
see the section on <<step.adoc#configureStep, step configuration>> See the section on <<step.adoc#configureStep, step configuration>>
for more options declaring specific step details inline. The XML namespace for more options declaring specific step details inline. The XML namespace
defaults to referencing a repository with an id of 'jobRepository', which defaults to referencing a repository with an id of 'jobRepository', which
is a sensible default. However, this can be overridden explicitly: is a sensible default. However, this can be overridden explicitly:
@@ -88,11 +87,9 @@ is a sensible default. However, this can be overridden explicitly:
---- ----
[role="xmlContent"] [role="xmlContent"]
In addition to steps a job configuration can contain other elements In addition to steps a job configuration can contain other elements that help with
that help with parallelisation (`<split>`), parallelization (`<split>`), declarative flow control (`<decision>`) and externalization
declarative flow control (`<decision>`) and of flow definitions (`<flow/>`).
externalization of flow definitions
(`<flow/>`).
endif::backend-html5[] endif::backend-html5[]
ifdef::backend-pdf[] ifdef::backend-pdf[]
@@ -153,17 +150,17 @@ endif::backend-pdf[]
==== Restartability ==== Restartability
One key issue when executing a batch job concerns the behavior of One key issue when executing a batch job concerns the behavior of a `Job` when it is
a `Job` when it is restarted. The launching of a restarted. The launching of a `Job` is considered to be a 'restart' if a `JobExecution`
`Job` is considered to be a 'restart' if a already exists for the particular `JobInstance`. Ideally, all jobs should be able to start
`JobExecution` already exists for the particular up where they left off, but there are scenarios where this is not possible. _It is
`JobInstance`. Ideally, all jobs should be able to entirely up to the developer to ensure that a new `JobInstance` is created in this
start up where they left off, but there are scenarios where this is not scenario._ However, Spring Batch does provide some help. If a `Job` should never be
possible. __It is entirely up to the developer to ensure that a new `JobInstance` is created in this scenario__. However, Spring Batch does provide some help. If a restarted, but should always be run as part of a new `JobInstance`, then the
`Job` should never be restarted, but should always restartable property may be set to 'false'.
be run as part of a new `JobInstance`, then the
restartable property may be set to 'false':
[role="xmlContent"]
The following example shows how to set the `restartable` field to `false` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -173,6 +170,9 @@ restartable property may be set to 'false':
</job> </job>
---- ----
[role="javaContent"]
The following example shows how to set the `restartable` field to `false` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -185,10 +185,10 @@ public Job footballJob() {
} }
---- ----
To phrase it another way, setting restartable to false means "this To phrase it another way, setting restartable to false means "`this
`Job` does not support being started again". Restarting a `Job` that is not `Job` does not support being started again`". Restarting a `Job` that is not
restartable will cause a `JobRestartException` to restartable causes a `JobRestartException` to
be thrown: be thrown.
[source, java] [source, java]
---- ----
@@ -235,10 +235,10 @@ public interface JobExecutionListener {
} }
---- ----
`JobListeners` can be added to a `JobListeners` can be added to a `SimpleJob` by setting listeners on the job.
`SimpleJob` via the listeners element on the
job:
[role="xmlContent"]
The following example shows how to add a listener element to an XML job definition:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -253,6 +253,9 @@ job:
</job> </job>
---- ----
[role="javaContent"]
The following example shows how to add a listener method to a Java job definition:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -265,11 +268,9 @@ public Job footballJob() {
} }
---- ----
It should be noted that `afterJob` will be It should be noted that the `afterJob` method is called regardless of the success or
called regardless of the success or failure of the failure of the `Job`. If success or failure needs to be determined, it can be obtained
Job. If success or failure needs to be determined from the `JobExecution`, as follows:
it can be obtained from the `JobExecution`:
[source, java] [source, java]
---- ----
@@ -352,7 +353,7 @@ A job declared in the XML namespace or using any subclass of
ifdef::backend-html5[] ifdef::backend-html5[]
[role="xmlContent"] [role="xmlContent"]
The configuration of a validator is supported through the XML namespace through a child The configuration of a validator is supported through the XML namespace through a child
element of the job, e.g: element of the job, as shown in the following example:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -363,11 +364,12 @@ The configuration of a validator is supported through the XML namespace through
---- ----
[role="xmlContent"] [role="xmlContent"]
The validator can be specified as a reference (as above) or as a The validator can be specified as a reference (as shown earlier) or as a nested bean
nested bean definition in the beans namespace. definition in the beans namespace.
[role="javaContent"] [role="javaContent"]
The configuration of a validator is supported through the java builders, e.g: The configuration of a validator is supported through the java builders, as shown in the
following example:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -383,7 +385,7 @@ public Job job1() {
endif::backend-html5[] endif::backend-html5[]
ifdef::backend-pdf[] ifdef::backend-pdf[]
The configuration of a validator is supported through the java builders, e.g: The configuration of a validator is supported through the java builders, as follows:
[source, java] [source, java]
---- ----
@@ -406,8 +408,8 @@ XML namespace support is also available for configuration of a `JobParametersVal
</job> </job>
---- ----
The validator can be specified as a reference (as above) or as a The validator can be specified as a reference (as above) or as a nested bean definition in
nested bean definition in the beans namespace. the beans namespace.
endif::backend-pdf[] endif::backend-pdf[]
@@ -416,73 +418,36 @@ endif::backend-pdf[]
=== Java Config === Java Config
Spring 3 brought the ability to configure applications via java in addition to XML. Spring 3 brought the ability to configure applications via java instead of XML. As of
As of Spring Batch 2.2.0, batch jobs can be configured using the same Spring Batch 2.2.0, batch jobs can be configured using the same java config.
java config. There are two components for the java based configuration: There are two components for the java based configuration: the `@EnableBatchProcessing`
the `@EnableBatchProcessing` annotation and two builders. annotation and two builders.
The `@EnableBatchProcessing` works similarly to the other The `@EnableBatchProcessing` works similarly to the other @Enable* annotations in the
@Enable* annotations in the Spring family. In this case, Spring family. In this case, `@EnableBatchProcessing` provides a base configuration for
`@EnableBatchProcessing` provides a base configuration for building batch jobs. Within this base configuration, an instance of `StepScope` is
building batch jobs. Within this base configuration, an instance of created in addition to a number of beans made available to be autowired:
`StepScope` is created in addition to a number of beans made
available to be autowired:
* `JobRepository`: bean name "jobRepository"
* `JobLauncher`: bean name "jobLauncher"
* `JobRegistry`: bean name "jobRegistry"
* `PlatformTransactionManager`: bean name "transactionManager"
* `JobBuilderFactory`: bean name "jobBuilders"
* `StepBuilderFactory`: bean name "stepBuilders"
The core interface for this configuration is the `BatchConfigurer`. The default
* `JobRepository` - bean name "jobRepository" implementation provides the beans mentioned above and requires a `DataSource` as a bean
within the context to be provided. This data source is used by the JobRepository.
* `JobLauncher` - bean name "jobLauncher"
* `JobRegistry` - bean name "jobRegistry"
* `PlatformTransactionManager` - bean name "transactionManager"
* `JobBuilderFactory` - bean name "jobBuilders"
* `StepBuilderFactory` - bean name "stepBuilders"
The core interface for this configuration is the `BatchConfigurer`.
The default implementation provides the beans mentioned above and requires a
`DataSource` as a bean within the context to be provided. This data
source will be used by the JobRepository. You can customize any of these beans
by creating a custom implementation of the `BatchConfigurer` interface.
Typically, extending the `DefaultBatchConfigurer` (which is provided if a
`BatchConfigurer` is not found) and overriding the required getter is sufficient.
However, implementing your own from scratch may be required. The following
example shows how to provide a custom transaction manager:
[source, java]
----
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
@Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
----
[NOTE] [NOTE]
==== ====
Only one configuration class needs to have the Only one configuration class needs to have the `@EnableBatchProcessing` annotation. Once
`@EnableBatchProcessing` annotation. Once you have a class you have a class annotated with it, you will have all of the above available.
annotated with it, you will have all of the above available.
==== ====
With the base configuration in place, a user can use the provided builder factories to
With the base configuration in place, a user can use the provided builder factories configure a job. The following example shows a two step job configured with the
to configure a job. Below is an example of a two step job configured via the `JobBuilderFactory` and the `StepBuilderFactory`:
`JobBuilderFactory` and the `StepBuilderFactory`.
[source, java] [source, java]
@@ -541,11 +506,9 @@ As described in earlier, the <<job.adoc#configureJob,`JobRepository`>> is used f
`Job`, and `Step`. `Job`, and `Step`.
[role="xmlContent"] [role="xmlContent"]
The batch The batch namespace abstracts away many of the implementation details of the
namespace abstracts away many of the implementation details of the `JobRepository` implementations and their collaborators. However, there are still a few
`JobRepository` implementations and their configuration options available, as shown in the following example:
collaborators. However, there are still a few configuration options
available:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -559,16 +522,16 @@ The batch
---- ----
[role="xmlContent"] [role="xmlContent"]
None of the configuration options listed above are required except None of the configuration options listed above are required except the `id`. If they are
the id. If they are not set, the defaults shown above will be used. They not set, the defaults shown above will be used. They are shown above for awareness
are shown above for awareness purposes. The purposes. The `max-varchar-length` defaults to 2500, which is the length of the long
`max-varchar-length` defaults to 2500, which is the `VARCHAR` columns in the <<schema-appendix.adoc#metaDataSchemaOverview,sample schema
length of the long `VARCHAR` columns in the <<schema-appendix.adoc#metaDataSchemaOverview,sample schema scripts>> scripts>>.
[role="javaContent"] [role="javaContent"]
When using java configuration, a `JobRepository` is provided for you. A JDBC based one is When using java configuration, a `JobRepository` is provided for you. A JDBC based one is
provided out of the box if a `DataSource` is provided, the `Map` based one if not. However provided out of the box if a `DataSource` is provided, the `Map` based one if not. However,
you can customize the configuration of the `JobRepository` via an implementation of the you can customize the configuration of the `JobRepository` through an implementation of the
`BatchConfigurer` interface. `BatchConfigurer` interface.
.Java Configuration .Java Configuration
@@ -602,21 +565,21 @@ None of the configuration options listed above are required except
==== Transaction Configuration for the JobRepository ==== Transaction Configuration for the JobRepository
If the namespace or the provided `FactoryBean` is used, transactional advice will be If the namespace or the provided `FactoryBean` is used, transactional advice is
automatically created around the repository. This is to ensure that the automatically created around the repository. This is to ensure that the batch meta-data,
batch meta data, including state that is necessary for restarts after a including state that is necessary for restarts after a failure, is persisted correctly.
failure, is persisted correctly. The behavior of the framework is not The behavior of the framework is not well defined if the repository methods are not
well defined if the repository methods are not transactional. The transactional. The isolation level in the `create*` method attributes is specified
isolation level in the `create*` method attributes is separately to ensure that, when jobs are launched, if two processes try to launch
specified separately to ensure that when jobs are launched, if two the same job at the same time, only one succeeds. The default isolation level for that
processes are trying to launch the same job at the same time, only one method is `SERIALIZABLE`, which is quite aggressive. `READ_COMMITTED` would work just as
will succeed. The default isolation level for that method is well. `READ_UNCOMMITTED` would be fine if two processes are not likely to collide in this
SERIALIZABLE, which is quite aggressive: READ_COMMITTED would work just way. However, since a call to the `create*` method is quite short, it is unlikely that
as well; READ_UNCOMMITTED would be fine if two processes are not likely `SERIALIZED` causes problems, as long as the database platform supports it. However, this
to collide in this way. However, since a call to the can be overridden.
`create*` method is quite short, it is unlikely
that the SERIALIZED will cause problems, as long as the database [role="xmlContent"]
platform supports it. However, this can be overridden: The following example shows how to the isolation level in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -625,6 +588,9 @@ If the namespace or the provided `FactoryBean` is used, transactional advice wil
isolation-level-for-create="REPEATABLE_READ" /> isolation-level-for-create="REPEATABLE_READ" />
---- ----
[role="javaContent"]
The following example shows how to the isolation level in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -639,10 +605,12 @@ protected JobRepository createJobRepository() throws Exception {
} }
---- ----
If the namespace or factory beans aren't used then it is also If the namespace or factory beans are not used, then it is also essential to configure the
essential to configure the transactional behavior of the repository transactional behavior of the repository using AOP.
using AOP:
[role="xmlContent"]
The following example shows how to configure the transactional behavior of the repository
in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -661,10 +629,13 @@ If the namespace or factory beans aren't used then it is also
---- ----
[role="xmlContent"] [role="xmlContent"]
This fragment can be used as is, with almost no changes. Remember The preceding fragment can be used nearly as is, with almost no changes. Remember also to
also to include the appropriate namespace declarations and to make sure include the appropriate namespace declarations and to make sure spring-tx and spring-aop
spring-tx and spring-aop (or the whole of spring) are on the (or the whole of Spring) are on the classpath.
classpath.
[role="javaContent"]
The following example shows how to configure the transactional behavior of the repository
in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -682,19 +653,17 @@ public TransactionProxyFactoryBean baseProxy() {
---- ----
[[repositoryTablePrefix]] [[repositoryTablePrefix]]
==== Changing the Table Prefix ==== Changing the Table Prefix
Another modifiable property of the Another modifiable property of the `JobRepository` is the table prefix of the meta-data
`JobRepository` is the table prefix of the tables. By default they are all prefaced with `BATCH_`. `BATCH_JOB_EXECUTION` and
meta-data tables. By default they are all prefaced with BATCH_. `BATCH_STEP_EXECUTION` are two examples. However, there are potential reasons to modify this
BATCH_JOB_EXECUTION and BATCH_STEP_EXECUTION are two examples. However, prefix. If the schema names needs to be prepended to the table names, or if more than one
there are potential reasons to modify this prefix. If the schema names set of meta data tables is needed within the same schema, then the table prefix needs to
needs to be prepended to the table names, or if more than one set of be changed:
meta data tables is needed within the same schema, then the table prefix
will need to be changed:
[role="xmlContent"]
The following example shows how to change the table prefix in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -703,6 +672,9 @@ Another modifiable property of the
table-prefix="SYSTEM.TEST_" /> table-prefix="SYSTEM.TEST_" />
---- ----
[role="xmlContent"]
The following example shows how to change the table prefix in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -717,31 +689,25 @@ protected JobRepository createJobRepository() throws Exception {
} }
---- ----
Given the above changes, every query to the meta data tables will Given the preceding changes, every query to the meta-data tables is prefixed with
be prefixed with "SYSTEM.TEST_". BATCH_JOB_EXECUTION will be referred to `SYSTEM.TEST_`. `BATCH_JOB_EXECUTION` is referred to as SYSTEM.`TEST_JOB_EXECUTION`.
as SYSTEM.TEST_JOB_EXECUTION.
[NOTE] [NOTE]
==== ====
Only the table prefix is configurable. The table and column Only the table prefix is configurable. The table and column names are not.
names are not.
==== ====
[[inMemoryRepository]] [[inMemoryRepository]]
==== In-Memory Repository ==== In-Memory Repository
There are scenarios in which you may not want to persist your There are scenarios in which you may not want to persist your domain objects to the
domain objects to the database. One reason may be speed; storing domain database. One reason may be speed; storing domain objects at each commit point takes extra
objects at each commit point takes extra time. Another reason may be time. Another reason may be that you just don't need to persist status for a particular
that you just don't need to persist status for a particular job. For job. For this reason, Spring batch provides an in-memory `Map` version of the job
this reason, Spring batch provides an in-memory Map version of the job repository.
repository:
[role="xmlContent"]
The following example shows the inclusion of `MapJobRepositoryFactoryBean` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -752,6 +718,9 @@ There are scenarios in which you may not want to persist your
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows the inclusion of `MapJobRepositoryFactoryBean` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -765,30 +734,30 @@ protected JobRepository createJobRepository() throws Exception {
---- ----
Note that the in-memory repository is volatile and so does not Note that the in-memory repository is volatile and so does not allow restart between JVM
allow restart between JVM instances. It also cannot guarantee that two instances. It also cannot guarantee that two job instances with the same parameters are
job instances with the same parameters are launched simultaneously, and launched simultaneously, and is not suitable for use in a multi-threaded Job, or a locally
is not suitable for use in a multi-threaded Job, or a locally partitioned `Step`. So use the database version of the repository wherever you need those
partitioned `Step`. So use the database version of the repository wherever features.
you need those features.
However it does require a transaction manager to be defined because there are rollback
semantics within the repository, and because the business logic might still be
transactional (such as RDBMS access). For testing purposes many people find the
`ResourcelessTransactionManager` useful.
However it does require a transaction manager to be defined
because there are rollback semantics within the repository, and because
the business logic might still be transactional (e.g. RDBMS access). For
testing purposes many people find the
`ResourcelessTransactionManager` useful.
[[nonStandardDatabaseTypesInRepository]] [[nonStandardDatabaseTypesInRepository]]
==== Non-standard Database Types in a Repository ==== Non-standard Database Types in a Repository
If you are using a database platform that is not in the list of If you are using a database platform that is not in the list of supported platforms, you
supported platforms, you may be able to use one of the supported types, may be able to use one of the supported types, if the SQL variant is close enough. To do
if the SQL variant is close enough. To do this you can use the raw this, you can use the raw `JobRepositoryFactoryBean` instead of the namespace shortcut and
`JobRepositoryFactoryBean` instead of the namespace use it to set the database type to the closest match.
shortcut and use it to set the database type to the closest
match: [role="xmlContent"]
The following example shows how to use `JobRepositoryFactoryBean` to set the database type
to the closest match in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -799,6 +768,10 @@ If you are using a database platform that is not in the list of
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to use `JobRepositoryFactoryBean` to set the database type
to the closest match in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -811,7 +784,6 @@ protected JobRepository createJobRepository() throws Exception {
factory.setTransactionManager(transactionManager); factory.setTransactionManager(transactionManager);
return factory.getObject(); return factory.getObject();
} }
---- ----
(The `JobRepositoryFactoryBean` tries to (The `JobRepositoryFactoryBean` tries to
@@ -836,11 +808,11 @@ If even that doesn't work, or you are not using an RDBMS, then the
When using `@EnableBatchProcessing`, a `JobRegistry` is provided out of the box for you. When using `@EnableBatchProcessing`, a `JobRegistry` is provided out of the box for you.
This section addresses configuring your own. This section addresses configuring your own.
The most basic implementation of the The most basic implementation of the `JobLauncher` interface is the `SimpleJobLauncher`.
`JobLauncher` interface is the Its only required dependency is a `JobRepository`, in order to obtain an execution.
`SimpleJobLauncher`. Its only required dependency is
a `JobRepository`, in order to obtain an [role="xmlContent"]
execution: The following example shows a `SimpleJobLauncher` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -851,6 +823,9 @@ The most basic implementation of the
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows a `SimpleJobLauncher` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -866,29 +841,29 @@ protected JobLauncher createJobLauncher() throws Exception {
... ...
---- ----
Once a <<domain.adoc#domainLanguageOfBatch,JobExecution>> is Once a <<domain.adoc#domainLanguageOfBatch,JobExecution>> is obtained, it is passed to the
obtained, it is passed to the execute method of execute method of `Job`, ultimately returning the `JobExecution` to the caller, as shown
Job, ultimately returning the in the following image:
`JobExecution` to the caller:
.Job Launcher Sequence .Job Launcher Sequence
image::{batch-asciidoc}images/job-launcher-sequence-sync.png[Job Launcher Sequence, scaledwidth="60%"] image::{batch-asciidoc}images/job-launcher-sequence-sync.png[Job Launcher Sequence, scaledwidth="60%"]
The sequence is straightforward and works well when launched from a The sequence is straightforward and works well when launched from a scheduler. However,
scheduler. However, issues arise when trying to launch from an HTTP issues arise when trying to launch from an HTTP request. In this scenario, the launching
request. In this scenario, the launching needs to be done asynchronously needs to be done asynchronously so that the `SimpleJobLauncher` returns immediately to its
so that the `SimpleJobLauncher` returns immediately caller. This is because it is not good practice to keep an HTTP request open for the
to its caller. This is because it is not good practice to keep an HTTP amount of time needed by long running processes such as batch. The following image shows
request open for the amount of time needed by long running processes such an example sequence:
as batch. An example sequence is below:
.Asynchronous Job Launcher Sequence .Asynchronous Job Launcher Sequence
image::{batch-asciidoc}images/job-launcher-sequence-async.png[Async Job Launcher Sequence, scaledwidth="60%"] image::{batch-asciidoc}images/job-launcher-sequence-async.png[Async Job Launcher Sequence, scaledwidth="60%"]
The `SimpleJobLauncher` can easily be The `SimpleJobLauncher` can be configured to allow for this scenario by configuring a
configured to allow for this scenario by configuring a `TaskExecutor`.
`TaskExecutor`:
[role="xmlContent"]
The following XML example shows a `SimpleJobLauncher` configured to return immediately:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -902,6 +877,9 @@ The `SimpleJobLauncher` can easily be
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example shows a `SimpleJobLauncher` configured to return immediately:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -994,16 +972,21 @@ All of these tasks are accomplished using only the arguments
|=============== |===============
These arguments must be passed in with the path first and the These arguments must be passed in with the path first and the name second. All arguments
name second. All arguments after these are considered to be after these are considered to be job parameters, are turned into a JobParameters object,
`JobParameters` and must be in the format of 'name=value': and must be in the format of 'name=value'.
[role="xmlContent"]
The following example shows a date passed as a job parameter to a job defied in XML:
[source, role="xmlContent"] [source, role="xmlContent"]
---- ----
<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05 <bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05
---- ----
[role="javaContent"]
The following example shows a date passed as a job parameter to a job defined in Java:
[source, role="javaContent"] [source, role="javaContent"]
---- ----
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05 <bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05
@@ -1011,16 +994,15 @@ These arguments must be passed in with the path first and the
ifdef::backend-html5[] ifdef::backend-html5[]
[role="xmlContent"] [role="xmlContent"]
In most cases you would want to use a manifest to declare your In most cases, you would want to use a manifest to declare your main class in a jar, but,
main class in a jar, but for simplicity, the class was used directly. for simplicity, the class was used directly. This example is using the same 'EndOfDay'
This example is using the same 'EndOfDay' example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first argument is example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first
'endOfDayJob.xml', which is the Spring argument is 'endOfDayJob.xml', which is the Spring ApplicationContext containing the
`ApplicationContext` containing the `Job`. The second argument, 'endOfDay' represents the job name. The final argument,
Job. The second argument, 'endOfDay' represents 'schedule.date(date)=2007/05/05', is converted into a JobParameters object.
the job name. The final argument, 'schedule.date(date)=2007/05/05'
will be converted into `JobParameters`. An
example of the XML configuration is below:
[role="xmlContent"]
The following example shows a sample configuration for `endOfDay` in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1034,15 +1016,16 @@ In most cases you would want to use a manifest to declare your
---- ----
[role="javaContent"] [role="javaContent"]
In most cases you would want to use a manifest to declare your In most cases you would want to use a manifest to declare your main class in a jar, but,
main class in a jar, but for simplicity, the class was used directly. for simplicity, the class was used directly. This example is using the same 'EndOfDay'
This example is using the same 'EndOfDay' example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first argument is example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first
'io.spring.EndOfDayJobConfiguration', which is the fully qualified class name to argument is 'io.spring.EndOfDayJobConfiguration', which is the fully qualified class name
the configuration class containing the to the configuration class containing the Job. The second argument, 'endOfDay' represents
Job. The second argument, 'endOfDay' represents the job name. The final argument, 'schedule.date(date)=2007/05/05' is converted into a
the job name. The final argument, 'schedule.date(date)=2007/05/05' `JobParameters` object. An example of the java configuration follows:
will be converted into JobParameters. An
example of the java configuration is below: [role="javaContent"]
The following example shows a sample configuration for `endOfDay` in Java:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1074,14 +1057,18 @@ public class EndOfDayJobConfiguration {
endif::backend-html5[] endif::backend-html5[]
ifdef::backend-pdf[] ifdef::backend-pdf[]
In most cases you would want to use a manifest to declare your In most cases, you would want to use a manifest to declare your main class in a jar, but,
main class in a jar, but for simplicity, the class was used directly. for simplicity, the class was used directly. This example is using the same 'EndOfDay'
This example is using the same 'EndOfDay' example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first argument is example from the <<domain.adoc#domainLanguageOfBatch,domainLanguageOfBatch>>. The first
where your job is configured (either an XML file or a fully qualified class name). argument is where your job is configured (either an XML file or a fully qualified class
The second argument, 'endOfDay' represents name). The second argument, 'endOfDay' represents the job name. The final argument,
the job name. The final argument, 'schedule.date(date)=2007/05/05' 'schedule.date(date)=2007/05/05' is converted into JobParameters.
will be converted into `JobParameters`. An
example of the configuration is below: // TODO Given that this block is for PDF output, should it have the xmlContent and
// javaContent markers?
[role="xmlContent"]
The following example shows a sample configuration for `endOfDay` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1095,6 +1082,9 @@ In most cases you would want to use a manifest to declare your
class="org.springframework.batch.core.launch.support.SimpleJobLauncher" /> class="org.springframework.batch.core.launch.support.SimpleJobLauncher" />
---- ----
[role="javaContent"]
The following example shows a sample configuration for `endOfDay` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1126,16 +1116,13 @@ public class EndOfDayJobConfiguration {
endif::backend-pdf[] endif::backend-pdf[]
This example is overly simplistic, since there are many more The preceding example is overly simplistic, since there are many more requirements to a
requirements to a run a batch job in Spring Batch in general, but it run a batch job in Spring Batch in general, but it serves to show the two main
serves to show the two main requirements of the requirements of the `CommandLineJobRunner`: `Job` and `JobLauncher`.
`CommandLineJobRunner`:
`Job` and
`JobLauncher`
[[exitCodes]] [[exitCodes]]
===== ExitCodes ===== ExitCodes
When launching a batch job from the command-line, an enterprise When launching a batch job from the command-line, an enterprise
@@ -1300,11 +1287,12 @@ public interface JobExplorer {
} }
---- ----
As is evident from the method signatures above, As is evident from the method signatures above, `JobExplorer` is a read-only version of
`JobExplorer` is a read-only version of the the `JobRepository`, and, like the `JobRepository`, it can be easily configured by using a
`JobRepository`, and like the factory bean:
`JobRepository`, it can be easily configured via a
factory bean: [role="xmlContent"]
The following example shows how to configure a `JobExplorer` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1313,6 +1301,9 @@ As is evident from the method signatures above,
p:dataSource-ref="dataSource" /> p:dataSource-ref="dataSource" />
---- ----
[role="javaContent"]
The following example shows how to configure a `JobExplorer` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1327,11 +1318,12 @@ public JobExplorer getJobExplorer() throws Exception {
... ...
---- ----
<<job.adoc#repositoryTablePrefix,Earlier in this chapter>>, it was mentioned that the table prefix of the <<job.adoc#repositoryTablePrefix,Earlier in this chapter>>, we noted that the table prefix
`JobRepository` can be modified to allow for of the `JobRepository` can be modified to allow for different versions or schemas. Because
different versions or schemas. Because the the `JobExplorer` works with the same tables, it too needs the ability to set a prefix.
`JobExplorer` is working with the same tables, it
too needs the ability to set a prefix: [role="xmlContent"]
The following example shows how to set the table prefix for a `JobExplorer` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1340,6 +1332,9 @@ public JobExplorer getJobExplorer() throws Exception {
p:tablePrefix="SYSTEM."/> p:tablePrefix="SYSTEM."/>
---- ----
[role="javaContent"]
The following example shows how to set the table prefix for a `JobExplorer` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1358,21 +1353,25 @@ public JobExplorer getJobExplorer() throws Exception {
==== JobRegistry ==== JobRegistry
A `JobRegistry` (and its parent interface `JobLocator`) is not A `JobRegistry` (and its parent interface `JobLocator`) is not mandatory, but it can be
mandatory, but it can be useful if you want to keep track of which jobs useful if you want to keep track of which jobs are available in the context. It is also
are available in the context. It is also useful for collecting jobs useful for collecting jobs centrally in an application context when they have been created
centrally in an application context when they have been created elsewhere (for example, in child contexts). Custom `JobRegistry` implementations can also
elsewhere (e.g. in child contexts). Custom `JobRegistry` implementations be used to manipulate the names and other properties of the jobs that are registered.
can also be used to manipulate the names and other properties of the There is only one implementation provided by the framework and this is based on a simple
jobs that are registered. There is only one implementation provided by map from job name to job instance.
the framework and this is based on a simple map from job name to job
instance. [role="xmlContent"]
The following example shows how to include a `JobRegistry` for a job defined in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" /> <bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
---- ----
[role="javaContent"]
The following example shows how to include a `JobRegistry` for a job defined in Java:
[role="javaContent"] [role="javaContent"]
When using `@EnableBatchProcessing`, a `JobRegistry` is provided out of the box for you. When using `@EnableBatchProcessing`, a `JobRegistry` is provided out of the box for you.
If you want to configure your own: If you want to configure your own:
@@ -1396,9 +1395,11 @@ There are two ways to populate a `JobRegistry` automatically: using
===== JobRegistryBeanPostProcessor ===== JobRegistryBeanPostProcessor
This is a bean post-processor that can register all jobs as they This is a bean post-processor that can register all jobs as they are created.
are created:
[role="xmlContent"]
The following example shows how to include the `JobRegistryBeanPostProcessor` for a job
defined in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1408,6 +1409,10 @@ This is a bean post-processor that can register all jobs as they
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to include the `JobRegistryBeanPostProcessor` for a job
defined in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1426,22 +1431,21 @@ Although it is not strictly necessary, the post-processor in the
===== AutomaticJobRegistrar ===== `AutomaticJobRegistrar`
This is a lifecycle component that creates child contexts and This is a lifecycle component that creates child contexts and registers jobs from those
registers jobs from those contexts as they are created. One advantage contexts as they are created. One advantage of doing this is that, while the job names in
of doing this is that, while the job names in the child contexts still the child contexts still have to be globally unique in the registry, their dependencies
have to be globally unique in the registry, their dependencies can can have "natural" names. So for example, you can create a set of XML configuration files
have "natural" names. So for example, you can create a set of XML each having only one Job, but all having different definitions of an `ItemReader` with the
configuration files each having only one Job, same bean name, such as "reader". If all those files were imported into the same context,
but all having different definitions of an the reader definitions would clash and override one another, but with the automatic
`ItemReader` with the same bean name, e.g. regsistrar this is avoided. This makes it easier to integrate jobs contributed from
"reader". If all those files were imported into the same context, the separate modules of an application.
reader definitions would clash and override one another, but with the
automatic registrar this is avoided. This makes it easier to
integrate jobs contributed from separate modules of an
application.
[role="xmlContent"]
The following example shows how to include the `AutomaticJobRegistrar` for a job defined
in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1460,6 +1464,10 @@ This is a lifecycle component that creates child contexts and
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to include the `AutomaticJobRegistrar` for a job defined
in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1552,14 +1560,12 @@ public interface JobOperator {
} }
---- ----
The above operations represent methods from many different The above operations represent methods from many different interfaces, such as
interfaces, such as `JobLauncher`, `JobLauncher`, `JobRepository`, `JobExplorer`, and `JobRegistry`. For this reason, the
`JobRepository`, provided implementation of `JobOperator`, `SimpleJobOperator`, has many dependencies.
`JobExplorer`, and
`JobRegistry`. For this reason, the provided
implementation of `JobOperator`,
`SimpleJobOperator`, has many dependencies:
[role="xmlContent"]
The following example shows a typical bean definition for `SimpleJobOperator` in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1575,6 +1581,9 @@ The above operations represent methods from many different
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows a typical bean definition for `SimpleJobOperator` in Java:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
/** /**
@@ -1674,10 +1683,8 @@ In this example, the value with a key of 'run.id' is used to
ifdef::backend-html5[] ifdef::backend-html5[]
[role="xmlContent"] [role="xmlContent"]
An incrementer can For jobs defined in XML, an incrementer can be associated with `Job` through the
be associated with `Job` via the 'incrementer' 'incrementer' attribute in the namespace, as follows:
attribute in the namespace:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1687,8 +1694,8 @@ An incrementer can
---- ----
[role="javaContent"] [role="javaContent"]
An incrementer can be associated with a 'Job' via the `incrementer` method provided in the For jobs defined in Java, an incrementer can be associated with a 'Job' through the
builders: `incrementer` method provided in the builders, as follows:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----

View File

@@ -0,0 +1,62 @@
$(document).ready(function(){
// Make Java the default
setJava();
// Initial cookie handler. This part remembers the reader's choice and sets the toggle
// accordingly.
var docToggleCookieString = Cookies.get("docToggle");
if (docToggleCookieString != null) {
if (docToggleCookieString === "xml") {
$("#xmlButton").prop("checked", true);
setXml();
} else if (docToggleCookieString === "java") {
$("#javaButton").prop("checked", true);
setJava();
} else if (docToggleCookieString === "both") {
$("#bothButton").prop("checked", true);
setBoth();
}
}
// Click handlers
$("#xmlButton").on("click", function() {
setXml();
});
$("#javaButton").on("click", function() {
setJava();
});
$("#bothButton").on("click", function() {
setBoth();
});
// Functions to do the work of handling the reader's choice, whether through a click
// or through a cookie. 3652 days is 10 years, give or take a leap day.
function setXml() {
$("*.xmlContent").show();
$("*.javaContent").hide();
$("*.javaContent > *").addClass("js-toc-ignore");
$("*.xmlContent > *").removeClass("js-toc-ignore");
window.dispatchEvent(new Event("tocRefresh"));
Cookies.set('docToggle', 'xml', { expires: 3652 });
};
function setJava() {
$("*.javaContent").show();
$("*.xmlContent").hide();
$("*.xmlContent > *").addClass("js-toc-ignore");
$("*.javaContent > *").removeClass("js-toc-ignore");
window.dispatchEvent(new Event("tocRefresh"));
Cookies.set('docToggle', 'java', { expires: 3652 });
};
function setBoth() {
$("*.javaContent").show();
$("*.xmlContent").show();
$("*.javaContent > *").removeClass("js-toc-ignore");
$("*.xmlContent > *").removeClass("js-toc-ignore");
window.dispatchEvent(new Event("tocRefresh"));
Cookies.set('docToggle', 'both', { expires: 3652 });
};
});

View File

@@ -10,66 +10,60 @@ ifndef::onlyonetoggle[]
include::toggle.adoc[] include::toggle.adoc[]
endif::onlyonetoggle[] endif::onlyonetoggle[]
As of Spring Batch 3.0 support for JSR-352 has been fully implemented. This section is not a replacement for As of Spring Batch 3.0 support for JSR-352 has been fully implemented. This section is not
the spec itself and instead, intends to explain how the JSR-352 specific concepts apply to Spring Batch. a replacement for the spec itself and instead, intends to explain how the JSR-352 specific
Additional information on JSR-352 can be found via the concepts apply to Spring Batch. Additional information on JSR-352 can be found via the
JCP here: link:$$https://jcp.org/en/jsr/detail?id=352$$[https://jcp.org/en/jsr/detail?id=352] JCP here:
link:$$https://jcp.org/en/jsr/detail?id=352$$[https://jcp.org/en/jsr/detail?id=352]
[[jsrGeneralNotes]] [[jsrGeneralNotes]]
=== General Notes about Spring Batch and JSR-352 === General Notes about Spring Batch and JSR-352
Spring Batch and JSR-352 are structurally the same. They both have jobs that are made up of steps. They Spring Batch and JSR-352 are structurally the same. They both have jobs that are made up
both have readers, processors, writers, and listeners. However, their interactions are subtly different. of steps. They both have readers, processors, writers, and listeners. However, their
For example, the `org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)` interactions are subtly different. For example, the
within Spring Batch receives two parameters: the item that was skipped and the Exception that caused the `org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)` within
skip. The JSR-352 version of the same method Spring Batch receives two parameters: the item that was skipped and the Exception that
(`javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List&lt;Object&gt; items, Exception ex)`) caused the skip. The JSR-352 version of the same method
also receives two parameters. However the first one is a `List` of all the items (`javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List&lt;Object&gt; items, Exception ex)`)
within the current chunk with the second being the `Exception` that caused the skip. also receives two parameters. However the first one is a `List` of all the items within
Because of these differences, it is important to note that there are two paths to execute a job within the current chunk with the second being the `Exception` that caused the skip. Because of
Spring Batch: either a traditional Spring Batch job or a JSR-352 based job. While the use of Spring Batch these differences, it is important to note that there are two paths to execute a job
artifacts (readers, writers, etc) will work within a job configured via JSR-352's JSL and executed via the within Spring Batch: either a traditional Spring Batch job or a JSR-352 based job. While
`JsrJobOperator`, they will behave according to the rules of JSR-352. It is also the use of Spring Batch artifacts (readers, writers, etc) will work within a job
important to note that batch artifacts that have been developed against the JSR-352 interfaces will not work configured with JSR-352's JSL and executed with the `JsrJobOperator`, they behave
within a traditional Spring Batch job. according to the rules of JSR-352. It is also important to note that batch artifacts that
have been developed against the JSR-352 interfaces will not work within a traditional
Spring Batch job.
[[jsrSetup]] [[jsrSetup]]
=== Setup === Setup
[[jsrSetupContexts]] [[jsrSetupContexts]]
==== Application Contexts ==== Application Contexts
All JSR-352 based jobs within Spring Batch consist of two application contexts. A parent context, that All JSR-352 based jobs within Spring Batch consist of two application contexts. A parent
contains beans related to the infrastructure of Spring Batch such as the `JobRepository`, context, that contains beans related to the infrastructure of Spring Batch such as the
`PlatformTransactionManager`, etc and a child context that consists of the configuration `JobRepository`, `PlatformTransactionManager`, etc and a child context that consists of
of the job to be run. The parent context is defined via the `jsrBaseContext.xml` provided the configuration of the job to be run. The parent context is defined via the
by the framework. This context may be overridden via the `JSR-352-BASE-CONTEXT` system `baseContext.xml` provided by the framework. This context may be overridden by setting
property. the `JSR-352-BASE-CONTEXT` system property.
[NOTE] [NOTE]
==== ====
The base context is not processed by the JSR-352 processors for things like property injection so The base context is not processed by the JSR-352 processors for things like property
no components requiring that additional processing should be configured there. injection so that no components requiring that additional processing should be configured
there.
==== ====
[[jsrSetupLaunching]] [[jsrSetupLaunching]]
==== Launching a JSR-352 based job ==== Launching a JSR-352 based job
JSR-352 requires a very simple path to executing a batch job. The following code is all that is needed to JSR-352 requires a very simple path to executing a batch job. The following code is all
execute your first batch job: that is needed to execute your first batch job:
[source, java] [source, java]
---- ----
@@ -77,114 +71,104 @@ JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties()); jobOperator.start("myJob", new Properties());
---- ----
While that is convenient for developers, the devil is in the details. Spring Batch bootstraps a bit of While that is convenient for developers, the devil is in the details. Spring Batch
infrastructure behind the scenes that a developer may want to override. The following is bootstrapped the bootstraps a bit of infrastructure behind the scenes that a developer may want to
first time `BatchRuntime.getJobOperator()` is called: override. The following is bootstrapped the first time `BatchRuntime.getJobOperator()`
is called:
|=============== |===============
|__Bean Name__|__Default Configuration__|__Notes__ |__Bean Name__|__Default Configuration__|__Notes__
| |
dataSource dataSource
| |
Apache DBCP BasicDataSource with configured values. Apache DBCP BasicDataSource with configured values.
| |
By default, HSQLDB is bootstrapped. By default, HSQLDB is bootstrapped.
|`transactionManager`|`org.springframework.jdbc.datasource.DataSourceTransactionManager`| |`transactionManager`|`org.springframework.jdbc.datasource.DataSourceTransactionManager`|
References the dataSource bean defined above. References the dataSource bean defined above.
| |
A Datasource initializer A Datasource initializer
|| ||
This is configured to execute the scripts configured via the This is configured to execute the scripts configured via the `batch.drop.script` and
`batch.drop.script` and `batch.schema.script` properties. By `batch.schema.script` properties. By default, the schema scripts for HSQLDB are executed.
default, the schema scripts for HSQLDB are executed. This behavior can be disabled via This behavior can be disabled by setting the `batch.data.source.init` property.
`batch.data.source.init` property.
| |
jobRepository jobRepository
| |
A JDBC based `SimpleJobRepository`. A JDBC based `SimpleJobRepository`.
| |
This `JobRepository` uses the previously mentioned data source and transaction This `JobRepository` uses the previously mentioned data source and transaction
manager. The schema's table prefix is configurable (defaults to BATCH_) via the manager. The schema's table prefix is configurable (defaults to BATCH_) via the
`batch.table.prefix` property. `batch.table.prefix` property.
| |
jobLauncher jobLauncher
|`org.springframework.batch.core.launch.support.SimpleJobLauncher`| |`org.springframework.batch.core.launch.support.SimpleJobLauncher`|
Used to launch jobs. Used to launch jobs.
| |
batchJobOperator batchJobOperator
|`org.springframework.batch.core.launch.support.SimpleJobOperator`| |`org.springframework.batch.core.launch.support.SimpleJobOperator`|
The `JsrJobOperator` wraps this to provide most of it's functionality. The `JsrJobOperator` wraps this to provide most of it's functionality.
| |
jobExplorer jobExplorer
|`org.springframework.batch.core.explore.support.JobExplorerFactoryBean`| |`org.springframework.batch.core.explore.support.JobExplorerFactoryBean`|
Used to address lookup functionality provided by the `JsrJobOperator`. Used to address lookup functionality provided by the `JsrJobOperator`.
| |
jobParametersConverter jobParametersConverter
|`org.springframework.batch.core.jsr.JsrJobParametersConverter`| |`org.springframework.batch.core.jsr.JsrJobParametersConverter`|
JSR-352 specific implementation of the `JobParametersConverter`. JSR-352 specific implementation of the `JobParametersConverter`.
| |
jobRegistry jobRegistry
|`org.springframework.batch.core.configuration.support.MapJobRegistry`| |`org.springframework.batch.core.configuration.support.MapJobRegistry`|
Used by the `SimpleJobOperator`. Used by the `SimpleJobOperator`.
| |
placeholderProperties placeholderProperties
|`org.springframework.beans.factory.config.PropertyPlaceholderConfigure`| |`org.springframework.beans.factory.config.PropertyPlaceholderConfigure`|
Loads the properties file `batch-${ENVIRONMENT:hsql}.properties` to configure Loads the properties file `batch-${ENVIRONMENT:hsql}.properties` to configure the
the properties mentioned above. ENVIRONMENT is a System property (defaults to hsql) properties mentioned above. ENVIRONMENT is a System property (defaults to `hsql`) that
that can be used to specify any of the supported databases Spring Batch currently can be used to specify any of the supported databases Spring Batch currently supports.
supports.
|=============== |===============
[NOTE] [NOTE]
==== ====
None of the above beans are optional for executing JSR-352 based jobs. All may be overriden to None of the above beans are optional for executing JSR-352 based jobs. All may be
provide customized functionality as needed. overridden to provide customized functionality as needed.
==== ====
[[dependencyInjection]] [[dependencyInjection]]
=== Dependency Injection === Dependency Injection
JSR-352 is based heavily on the Spring Batch programming model. As such, while not explicitly requiring a JSR-352 is based heavily on the Spring Batch programming model. As such, while not
formal dependency injection implementation, DI of some kind implied. Spring Batch supports all three explicitly requiring a formal dependency injection implementation, DI of some kind
methods for loading batch artifacts defined by JSR-352: implied. Spring Batch supports all three methods for loading batch artifacts defined by
JSR-352:
* Implementation Specific Loader: Spring Batch is built upon Spring and so supports
Spring dependency injection within JSR-352 batch jobs.
* Archive Loader: JSR-352 defines the existing of a batch.xml file that provides mappings
between a logical name and a class name. This file must be found within the /META-INF/
directory if it is used.
* Thread Context Class Loader: JSR-352 allows configurations to specify batch artifact
implementations in their JSL by providing the fully qualified class name inline. Spring
Batch supports this as well in JSR-352 configured jobs.
* Implementation Specific Loader - Spring Batch is built upon Spring and so supports Spring To use Spring dependency injection within a JSR-352 based batch job consists of
dependency injection within JSR-352 batch jobs. configuring batch artifacts using a Spring application context as beans. Once the beans
have been defined, a job can refer to them as it would any bean defined within the
batch.xml file.
* Archive Loader - JSR-352 defines the existing of a batch.xml file that provides mappings between a
logical name and a class name. This file must be found within the /META-INF/ directory if it is
used.
* Thread Context Class Loader - JSR-352 allows configurations to specify batch artifact
implementations in their JSL by providing the fully qualified class name inline. Spring Batch
supports this as well in JSR-352 configured jobs.
To use Spring dependency injection within a JSR-352 based batch job consists of configuring batch
artifacts using a Spring application context as beans. Once the beans have been defined, a job can refer to
them as it would any bean defined within the batch.xml.
[role="xmlContent"]
The following example shows how to use Spring dependency injection within a JSR-352 based
batch job in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -211,6 +195,10 @@ To use Spring dependency injection within a JSR-352 based batch job consists of
</beans> </beans>
---- ----
[role="javaContent"]
The following example shows how to use Spring dependency injection within a JSR-352 based
batch job in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -234,14 +222,14 @@ public class BatchConfiguration {
</job> </job>
---- ----
The assembly of Spring contexts (imports, etc) works with JSR-352 jobs just as it would with any other The assembly of Spring contexts (imports, etc) works with JSR-352 jobs just as it would
Spring based application. The only difference with a JSR-352 based job is that the entry point for the with any other Spring based application. The only difference with a JSR-352 based job is
context definition will be the job definition found in /META-INF/batch-jobs/. that the entry point for the context definition will be the job definition found in /META-INF/batch-jobs/.
To use the thread context class loader approach, all you need to do is provide the fully qualified class
name as the ref. It is important to note that when using this approach or the batch.xml approach, the class
referenced requires a no argument constructor which will be used to create the bean.
To use the thread context class loader approach, all you need to do is provide the fully
qualified class name as the ref. It is important to note that when using this approach or
the batch.xml approach, the class referenced requires a no argument constructor which is
used to create the bean.
[source, xml] [source, xml]
---- ----
@@ -255,8 +243,6 @@ To use the thread context class loader approach, all you need to do is provide t
---- ----
[[jsrJobProperties]] [[jsrJobProperties]]
=== Batch Properties === Batch Properties
[[jsrPropertySupport]] [[jsrPropertySupport]]
@@ -264,9 +250,9 @@ To use the thread context class loader approach, all you need to do is provide t
==== Property Support ==== Property Support
JSR-352 allows for properties to be defined at the Job, Step and batch artifact level by way of JSR-352 allows for properties to be defined at the Job, Step and batch artifact level by
configuration in the JSL. Batch properties are configured at each level in the following way: way of configuration in the JSL. Batch properties are configured at each level in the
following way:
[source, xml] [source, xml]
---- ----
@@ -276,22 +262,18 @@ JSR-352 allows for properties to be defined at the Job, Step and batch artifact
</properties> </properties>
---- ----
`Properties` may be configured on any batch artifact. `Properties` may be configured on any batch artifact.
[[jsrBatchPropertyAnnotation]] [[jsrBatchPropertyAnnotation]]
==== @BatchProperty annotation ==== @BatchProperty annotation
`Properties` are referenced in batch artifacts by annotating class fields with the `Properties` are referenced in batch artifacts by annotating class fields with the
`@BatchProperty` and `@Inject` annotations (both annotations `@BatchProperty` and `@Inject` annotations (both annotations are required by the spec). As
are required by the spec). As defined by JSR-352, fields for properties must be String typed. Any type defined by JSR-352, fields for properties must be String typed. Any type conversion is up
conversion is up to the implementing developer to perform. to the implementing developer to perform.
An `javax.batch.api.chunk.ItemReader` artifact could be configured with a
properties block such as the one described above and accessed as such:
An `javax.batch.api.chunk.ItemReader` artifact could be configured with a properties block
such as the one described above and accessed as such:
[source, java] [source, java]
---- ----
@@ -304,73 +286,51 @@ public class MyItemReader extends AbstractItemReader {
} }
---- ----
The value of the field "propertyName1" will be "propertyValue1" The value of the field "propertyName1" will be "propertyValue1"
[[jsrPropertySubstitution]] [[jsrPropertySubstitution]]
==== Property Substitution ==== Property Substitution
Property substitution is provided by way of operators and simple conditional expressions. The general Property substitution is provided by way of operators and simple conditional expressions.
usage is `#{operator['key']}`. The general usage is `#{operator['key']}`.
Supported operators: Supported operators:
* jobParameters - access job parameter values that the job was started/restarted with. * `jobParameters`: access job parameter values that the job was started/restarted with.
* `jobProperties`: access properties configured at the job level of the JSL.
* `systemProperties`: access named system properties.
* jobProperties - access properties configured at the job level of the JSL. * `partitionPlan`: access named property from the partition plan of a partitioned step.
* systemProperties - access named system properties.
* partitionPlan - access named property from the partition plan of a partitioned step.
---- ----
#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']} #{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}
---- ----
The left hand side of the assignment is the expected value, the right hand side is the default value. In The left hand side of the assignment is the expected value, the right hand side is the
this example, the result will resolve to a value of the system property `file.separator` as default value. In the preceding
`#{jobParameters['unresolving.prop']}` is assumed to not be resolvable. If neither expressions can be example, the result will resolve to a value of the system property file.separator as
resolved, an empty String will be returned. Multiple conditions can be used, which are separated by a #{jobParameters['unresolving.prop']} is assumed to not be resolvable. If neither
';'. expressions can be resolved, an empty String will be returned. Multiple conditions can be
used, which are separated by a ';'.
[[jsrProcessingModels]] [[jsrProcessingModels]]
=== Processing Models === Processing Models
JSR-352 provides the same two basic processing models that Spring Batch does: JSR-352 provides the same two basic processing models that Spring Batch does:
* Item based processing - Using an `javax.batch.api.chunk.ItemReader`, an optional
`javax.batch.api.chunk.ItemProcessor`, and an `javax.batch.api.chunk.ItemWriter`.
* Item based processing - Using an `javax.batch.api.chunk.ItemReader`, an * Task based processing - Using a `javax.batch.api.Batchlet` implementation. This
optional `javax.batch.api.chunk.ItemProcessor`, and an processing model is the same as the `org.springframework.batch.core.step.tasklet.Tasklet`
`javax.batch.api.chunk.ItemWriter`. based processing currently available.
* Task based processing - Using a `javax.batch.api.Batchlet`
implementation. This processing model is the same as the
`org.springframework.batch.core.step.tasklet.Tasklet` based processing
currently available.
==== Item based processing ==== Item based processing
Item based processing in this context is a chunk size being set by the number of items read by an Item based processing in this context is a chunk size being set by the number of items
`ItemReader`. To configure a step this way, specify the read by an `ItemReader`. To configure a step this way, specify the `item-count` (which
`item-count` (which defaults to 10) and optionally configure the defaults to 10) and optionally configure the `checkpoint-policy` as item (this is the default).
`checkpoint-policy` as item (this is the default).
[source, xml] [source, xml]
---- ----
@@ -385,27 +345,22 @@ Item based processing in this context is a chunk size being set by the number of
... ...
---- ----
If item-based checkpointing is chosen, an additional attribute `time-limit` is supported.
If item based checkpointing is chosen, an additional attribute `time-limit` is This sets a time limit for how long the number of items specified has to be processed. If
supported. This sets a time limit for how long the number of items specified has to be processed. If the timeout is reached, the chunk will complete with however many items have been read by
the timeout is reached, the chunk will complete with however many items have been read by then then regardless of what the `item-count` is configured to be.
regardless of what the `item-count` is configured to be.
==== Custom checkpointing ==== Custom checkpointing
JSR-352 calls the process around the commit interval within a step "checkpointing". Item based JSR-352 calls the process around the commit interval within a step "checkpointing".
checkpointing is one approach as mentioned above. However, this will not be robust enough in many Item-based checkpointing is one approach as mentioned above. However, this is not robust
cases. Because of this, the spec allows for the implementation of a custom checkpointing algorithm by enough in many cases. Because of this, the spec allows for the implementation of a custom
implementing the `javax.batch.api.chunk.CheckpointAlgorithm` interface. This checkpointing algorithm by implementing the `javax.batch.api.chunk.CheckpointAlgorithm`
functionality is functionally the same as Spring Batch's custom completion policy. To use an interface. This functionality is functionally the same as Spring Batch's custom completion
implementation of `CheckpointAlgorithm`, configure your step with the custom policy. To use an implementation of `CheckpointAlgorithm`, configure your step with the
`checkpoint-policy` as shown below where `fooCheckpointer` refers to an custom `checkpoint-policy` as shown below where fooCheckpointer refers to an
implementation of `CheckpointAlgorithm`. implementation of `CheckpointAlgorithm`.
[source, xml] [source, xml]
---- ----
@@ -422,86 +377,66 @@ JSR-352 calls the process around the commit interval within a step "checkpointin
---- ----
[[jsrRunningAJob]] [[jsrRunningAJob]]
=== Running a job === Running a job
The entrance to executing a JSR-352 based job is through the The entrance to executing a JSR-352 based job is through the
`javax.batch.operations.JobOperator`. Spring Batch provides its own implementation of `javax.batch.operations.JobOperator`. Spring Batch provides our own implementation to
this interface (`org.springframework.batch.core.jsr.launch.JsrJobOperator`). This this interface (`org.springframework.batch.core.jsr.launch.JsrJobOperator`). This
implementation is loaded via the `javax.batch.runtime.BatchRuntime`. Launching a implementation is loaded via the `javax.batch.runtime.BatchRuntime`. Launching a
JSR-352 based batch job is implemented as follows: JSR-352 based batch job is implemented as follows:
[source, java] [source, java]
---- ----
JobOperator jobOperator = BatchRuntime.getJobOperator(); JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties()); long jobExecutionId = jobOperator.start("fooJob", new Properties());
---- ----
The above code does the following: The above code does the following:
* Bootstraps a base `ApplicationContext`: In order to provide batch functionality, the
framework needs some infrastructure bootstrapped. This occurs once per JVM. The
* Bootstraps a base `ApplicationContext` - In order to provide batch functionality, the framework components that are bootstrapped are similar to those provided by
needs some infrastructure bootstrapped. This occurs once per JVM. The components that are `@EnableBatchProcessing`. Specific details can be found in the javadoc for the
bootstrapped are similar to those provided by `@EnableBatchProcessing`. `JsrJobOperator`.
Specific details can be found in the javadoc for the `JsrJobOperator`. * Loads an `ApplicationContext` for the job requested: In the example
above, the framework looks in /META-INF/batch-jobs for a file named fooJob.xml and load a
context that is a child of the shared context mentioned previously.
* Launch the job: The job defined within the context will be executed asynchronously.
* Loads an `ApplicationContext` for the job requested - In the example The `JobExecution's` ID is returned.
above, the framework will look in /META-INF/batch-jobs for a file named fooJob.xml and load a
context that is a child of the shared context mentioned previously.
* Launch the job - The job defined within the context will be executed asynchronously. The
`JobExecution's` id will be returned.
[NOTE] [NOTE]
==== ====
All JSR-352 based batch jobs are executed asynchronously. All JSR-352 based batch jobs are executed asynchronously.
==== ====
When `JobOperator#start` is called using `SimpleJobOperator`, Spring Batch determines if
the call is an initial run or a retry of a previously executed run. Using the JSR-352
based `JobOperator#start(String jobXMLName, Properties jobParameters)`, the framework
always creates a new JobInstance (JSR-352 job parameters are non-identifying). In order to
restart a job, a call to
`JobOperator#restart(long executionId, Properties restartParameters)` is required.
When `JobOperator#start` is called using `SimpleJobOperator`,
Spring Batch determines if the call is an initial run or a retry of a previously executed run. Using the
JSR-352 based `JobOperator#start(String jobXMLName, Properties jobParameters)`, the
framework will always create a new JobInstance (JSR-352 job parameters are
non-identifying). In order to restart a job, a call to
`JobOperator#restart(long executionId, Properties restartParameters)` is required.
[[jsrContexts]] [[jsrContexts]]
=== Contexts === Contexts
JSR-352 defines two context objects that are used to interact with the meta-data of a job or step from JSR-352 defines two context objects that are used to interact with the meta-data of a job
within a batch artifact: `javax.batch.runtime.context.JobContext` and or step from within a batch artifact: `javax.batch.runtime.context.JobContext` and
`javax.batch.runtime.context.StepContext`. Both of these are available in any step `javax.batch.runtime.context.StepContext`. Both of these are available in any step-level
level artifact (`Batchlet`, `ItemReader`, etc) with the artifact (`Batchlet`, `ItemReader`, and others) with the `JobContext` being available to
`JobContext` being available to job level artifacts as well job-level artifacts as well (JobListener for example).
(`JobListener` for example).
To obtain a reference to the `JobContext` or `StepContext`
within the current scope, simply use the `@Inject` annotation:
To obtain a reference to the `JobContext` or `StepContext` within the current scope, use
the `@Inject` annotation, as follows:
[source, java] [source, java]
---- ----
@Inject @Inject
JobContext jobContext; JobContext jobContext;
---- ----
[NOTE] [NOTE]
.@Autowire for JSR-352 contexts .@Autowire for JSR-352 contexts
==== ====
@@ -509,47 +444,35 @@ Using Spring's @Autowire is not supported for the injection of these contexts.
==== ====
In Spring Batch, the `JobContext` and `StepContext` wrap their In Spring Batch, the `JobContext` and `StepContext` wrap their corresponding execution
corresponding execution objects (`JobExecution` and objects (`JobExecution` and `StepExecution` respectively). Data stored through
`StepExecution` respectively). Data stored via `StepContext#persistent#setPersistentUserData(Serializable data)` is stored in the Spring
`StepContext#setPersistentUserData(Serializable data)` is stored in the Batch `StepExecution#executionContext`.
Spring Batch `StepExecution#executionContext`.
[[jsrStepFlow]] [[jsrStepFlow]]
=== Step Flow === Step Flow
Within a JSR-352 based job, the flow of steps works similarly as it does within Spring Batch. Within a JSR-352 based job, the flow of steps works similarly as it does within Spring
However, there are a few subtle differences: Batch. However, there are a few subtle differences:
* Decision's are steps - In a regular Spring Batch job, a decision is a state that does not
have an independent `StepExecution` or any of the rights and
responsibilities that go along with being a full step.. However, with JSR-352, a decision
is a step just like any other and will behave just as any other steps (transactionality,
it gets a `StepExecution`, etc). This means that they are treated the
same as any other step on restarts as well.
* `next` attribute and step transitions - In a regular job, these are
allowed to appear together in the same step. JSR-352 allows them to both be used in the
same step with the next attribute taking precedence in evaluation.
* Decision's are steps - In a regular Spring Batch job, a decision is a state that does
not have an independent `StepExecution` or any of the rights and responsibilities that go
along with being a full step. However, with JSR-352, a decision is a step just like any
other and will behave just as any other steps (transactionality, it gets a
`StepExecution`, and so on). This means that they are treated the same as any other step
on restarts as well.
* `next` attribute and step transitions - In a regular job, these are allowed to appear
together in the same step. JSR-352 allows them to both be used in the same step with the
next attribute taking precedence in evaluation.
* Transition element ordering - In a standard Spring Batch job, transition elements are * Transition element ordering - In a standard Spring Batch job, transition elements are
sorted from most specific to least specific and evaluated in that order. JSR-352 jobs sorted from most specific to least specific and evaluated in that order. JSR-352 jobs
evaluate transition elements in the order they are specified in the XML. evaluate transition elements in the order they are specified in the XML.
[[jsrScaling]] [[jsrScaling]]
=== Scaling a JSR-352 batch job === Scaling a JSR-352 batch job
<<<<<<< HEAD
Traditional Spring Batch jobs have four ways of scaling (the last two capable of being executed across Traditional Spring Batch jobs have four ways of scaling (the last two capable of being executed across
multiple JVMs): multiple JVMs):
@@ -573,16 +496,28 @@ JSR-352 provides two options for scaling batch jobs. Both options support only
* Partitioning - Conceptually the same as Spring Batch however implemented slightly different. * Partitioning - Conceptually the same as Spring Batch however implemented slightly different.
=======
Traditional Spring Batch jobs have four ways of scaling (the last two capable of being
executed across multiple JVMs):
>>>>>>> Made the "Both" option make sense
* Split: Running multiple steps in parallel.
* Multiple threads: Executing a single step via multiple threads.
* Partitioning: Dividing the data up for parallel processing (master/slave).
* Remote Chunking: Executing the processor piece of logic remotely.
JSR-352 provides two options for scaling batch jobs. Both options support only a single
JVM:
* Split: Same as Spring Batch
* Partitioning: Conceptually the same as Spring Batch however implemented slightly
different.
[[jsrPartitioning]] [[jsrPartitioning]]
==== Partitioning ==== Partitioning
<<<<<<< HEAD
Conceptually, partitioning in JSR-352 is the same as it is in Spring Batch. Meta-data is provided Conceptually, partitioning in JSR-352 is the same as it is in Spring Batch. Meta-data is provided
to each worker to identify the input to be processed, with the workers reporting back to the manager the to each worker to identify the input to be processed, with the workers reporting back to the manager the
results upon completion. However, there are some important differences: results upon completion. However, there are some important differences:
@@ -614,15 +549,39 @@ Conceptually, partitioning in JSR-352 is the same as it is in Spring Batch. Met
not get official `StepExecutions`. Because of that, calls to not get official `StepExecutions`. Because of that, calls to
`JsrJobOperator#getStepExecutions(long jobExecutionId)` will only `JsrJobOperator#getStepExecutions(long jobExecutionId)` will only
return the `StepExecution` for the manager. return the `StepExecution` for the manager.
=======
Conceptually, partitioning in JSR-352 is the same as it is in Spring Batch. Meta-data is
provided to each slave to identify the input to be processed with the slaves reporting
back to the master the results upon completion. However, there are some important
differences:
* Partitioned `Batchlet`: This runs multiple instances of the configured `Batchlet` on
multiple threads. Each instance has its own set of properties as provided by the JSL or
the `PartitionPlan`.
* `PartitionPlan`: With Spring Batch's partitioning, an `ExecutionContext` is provided
for each partition. With JSR-352, a single `javax.batch.api.partition.PartitionPlan` is
provided with an array of `Properties` providing the meta-data for each partition.
* `PartitionMapper`: JSR-352 provides two ways to generate partition meta-data. One is by
setting the JSL (partition properties). The second is via an implementation of the
`javax.batch.api.partition.PartitionMapper` interface. Functionally, this interface is
similar to the `org.springframework.batch.core.partition.support.Partitioner` interface
provided by Spring Batch in that it provides a way to programmatically generate meta-data
for partitioning.
* `StepExecutions`: In Spring Batch, partitioned steps are run as master/slave. Within
JSR-352, the same configuration occurs. However, the slave steps do not get official
`StepExecutions`. Because of that, calls to
`JsrJobOperator#getStepExecutions(long jobExecutionId)` return only the `StepExecution`
for the master.
>>>>>>> Made the "Both" option make sense
[NOTE] [NOTE]
==== ====
The child `StepExecutions` still exist in the job repository and are available The child `StepExecutions` still exist in the job repository and are available
via the `JobExplorer` and Spring Batch Admin. through the `JobExplorer` and Spring Batch Admin.
==== ====
<<<<<<< HEAD
* Compensating logic - Since Spring Batch implements the manager/worker logic of * Compensating logic - Since Spring Batch implements the manager/worker logic of
partitioning using steps, `StepExecutionListeners` can be used to partitioning using steps, `StepExecutionListeners` can be used to
handle compensating logic if something goes wrong. However, since the workers JSR-352 handle compensating logic if something goes wrong. However, since the workers JSR-352
@@ -639,15 +598,32 @@ via the `JobExplorer` and Spring Batch Admin.
|`javax.batch.api.partition.PartitionReducer`|Provides the ability to provide compensating logic for a partitioned |`javax.batch.api.partition.PartitionReducer`|Provides the ability to provide compensating logic for a partitioned
step. step.
=======
* Compensating logic - Since Spring Batch implements the master/slave logic of
partitioning using steps, `StepExecutionListeners` can be used to handle compensating
logic if something goes wrong. However, since the slaves JSR-352 provides a collection of
other components for the ability to provide compensating logic when errors occur and to
dynamically set the exit status. These components include the following:
|===============
|__Artifact Interface__|__Description__
|`javax.batch.api.partition.PartitionCollector`|Provides a way for slave steps to send
information back to the master. There is one instance per slave thread.
|`javax.batch.api.partition.PartitionAnalyzer`|End point that receives the information
collected by the `PartitionCollector` as well as the resulting statuses from a completed
partition.
|`javax.batch.api.partition.PartitionReducer`|Provides the ability to provide compensating
logic for a partitioned step.
>>>>>>> Made the "Both" option make sense
|=============== |===============
[[jsrTesting]]
[[jsrTesting]]
=== Testing === Testing
Since all JSR-352 based jobs are executed asynchronously, it can be difficult to determine when a job has Since all JSR-352 based jobs are executed asynchronously, it can be difficult to determine
completed. To help with testing, Spring Batch provides the when a job has completed. To help with testing, Spring Batch provides the
`org.springframework.batch.test.JsrTestUtils`. This utility class provides the `org.springframework.batch.core.jsr.JsrTestUtils`. This utility class provides the
ability to start a job and restart a job and wait for it to complete. Once the job completes, the ability to start a job and restart a job and wait for it to complete. Once the job
associated `JobExecution` is returned. completes, the associated `JobExecution` is returned.

View File

@@ -174,7 +174,10 @@ In the preceding example, there is a class `Foo`, a class `Bar`, and a class
simple, but any type of transformation could be done here. The `BarWriter` writes `Bar` simple, but any type of transformation could be done here. The `BarWriter` writes `Bar`
objects, throwing an exception if any other type is provided. Similarly, the objects, throwing an exception if any other type is provided. Similarly, the
`FooProcessor` throws an exception if anything but a `Foo` is provided. The `FooProcessor` throws an exception if anything but a `Foo` is provided. The
`FooProcessor` can then be injected into a `Step`, as shown in the following example: `FooProcessor` can then be injected into a `Step`.
[role="xmlContent"]
The following example shows how to inject the `FooProcessor` into a step in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -189,6 +192,9 @@ objects, throwing an exception if any other type is provided. Similarly, the
</job> </job>
---- ----
[role="javaContent"]
The following example shows how to inject the `FooProcessor` into a step in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -267,7 +273,10 @@ compositeProcessor.setDelegates(itemProcessors);
---- ----
Just as with the previous example, the composite processor can be configured into the Just as with the previous example, the composite processor can be configured into the
`Step`: `Step`.
[role="xmlContent"]
The following example shows how to configure the composite processor into the step in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -292,6 +301,10 @@ Just as with the previous example, the composite processor can be configured int
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to configure the composite processor into the step in
Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -405,8 +418,10 @@ Batch Core as part of a `Step` in a `Job`, then they almost certainly need to be
registered manually with the `Step`. A reader, writer, or processor that is directly registered manually with the `Step`. A reader, writer, or processor that is directly
wired into the `Step` gets registered automatically if it implements `ItemStream` or a wired into the `Step` gets registered automatically if it implements `ItemStream` or a
`StepListener` interface. However, because the delegates are not known to the `Step`, `StepListener` interface. However, because the delegates are not known to the `Step`,
they need to be injected as listeners or streams (or both if appropriate), as shown in they need to be injected as listeners or streams (or both if appropriate).
the following example:
[role="xmlContent"]
The following example shows how to inject a delegate as a stream in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -431,6 +446,9 @@ the following example:
<bean id="barWriter" class="...BarWriter" /> <bean id="barWriter" class="...BarWriter" />
---- ----
[role="xmlContent"]
The following example shows how to inject a delegate as a stream in XML:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -808,8 +826,11 @@ public class PlayerMapper implements FieldSetMapper<Player> {
For many, having to write a specific `FieldSetMapper` is equally as cumbersome as writing For many, having to write a specific `FieldSetMapper` is equally as cumbersome as writing
a specific `RowMapper` for a `JdbcTemplate`. Spring Batch makes this easier by providing a specific `RowMapper` for a `JdbcTemplate`. Spring Batch makes this easier by providing
a `FieldSetMapper` that automatically maps fields by matching a field name with a setter a `FieldSetMapper` that automatically maps fields by matching a field name with a setter
on the object using the JavaBean specification. Again using the football example, the on the object using the JavaBean specification.
`BeanWrapperFieldSetMapper` configuration looks like the following snippet:
[role="xmlContent"]
Again using the football example, the `BeanWrapperFieldSetMapper` configuration looks like
the following snippet in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -824,6 +845,10 @@ on the object using the JavaBean specification. Again using the football example
scope="prototype" /> scope="prototype" />
---- ----
[role="javaContent"]
Again using the football example, the `BeanWrapperFieldSetMapper` configuration looks like
the following snippet in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -872,7 +897,11 @@ While this looks like one large field, it actually represent 4 distinct fields:
. Customer: ID of the customer ordering the item - 9 characters long. . Customer: ID of the customer ordering the item - 9 characters long.
When configuring the `FixedLengthLineTokenizer`, each of these lengths must be provided When configuring the `FixedLengthLineTokenizer`, each of these lengths must be provided
in the form of ranges, as shown in the following example: in the form of ranges.
[role="xmlContent"]
The following example shows how to define ranges for the `FixedLengthLineTokenizer` in
XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -886,17 +915,21 @@ in the form of ranges, as shown in the following example:
[role="xmlContent"] [role="xmlContent"]
Because the `FixedLengthLineTokenizer` uses the same `LineTokenizer` interface as Because the `FixedLengthLineTokenizer` uses the same `LineTokenizer` interface as
discussed above, it returns the same `FieldSet` as if a delimiter had been used. This discussed earlier, it returns the same `FieldSet` as if a delimiter had been used. This
allows the same approaches to be used in handling its output, such as using the allows the same approaches to be used in handling its output, such as using the
`BeanWrapperFieldSetMapper`. `BeanWrapperFieldSetMapper`.
[NOTE] [NOTE]
==== ====
Supporting the above syntax for ranges requires that a specialized property editor, Supporting the preceding syntax for ranges requires that a specialized property editor,
`RangeArrayPropertyEditor`, be configured in the `ApplicationContext`. However, this bean `RangeArrayPropertyEditor`, be configured in the `ApplicationContext`. However, this bean
is automatically declared in an `ApplicationContext` where the batch namespace is used. is automatically declared in an `ApplicationContext` where the batch namespace is used.
==== ====
[role="javaContent"]
The following example shows how to define ranges for the `FixedLengthLineTokenizer` in
Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -941,8 +974,11 @@ though a "LINEA" has more information than a "LINEB".
The `ItemReader` reads each line individually, but we must specify different The `ItemReader` reads each line individually, but we must specify different
`LineTokenizer` and `FieldSetMapper` objects so that the `ItemWriter` receives the `LineTokenizer` and `FieldSetMapper` objects so that the `ItemWriter` receives the
correct items. The `PatternMatchingCompositeLineMapper` makes this easy by allowing maps correct items. The `PatternMatchingCompositeLineMapper` makes this easy by allowing maps
of patterns to `LineTokenizer` instances and patterns to `FieldSetMapper` instances to be of patterns to `LineTokenizers` and patterns to `FieldSetMappers` to be configured.
configured, as shown in the following example:
[role="xmlContent"]
The following example shows how to define ranges for the `FixedLengthLineTokenizer` in
XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1002,7 +1038,10 @@ prefixes to lines. The `PatternMatcher` always matches the most specific patter
possible, regardless of the order in the configuration. So if "LINE*" and "LINEA*" were possible, regardless of the order in the configuration. So if "LINE*" and "LINEA*" were
both listed as patterns, "LINEA" would match pattern "LINEA*", while "LINEB" would match both listed as patterns, "LINEA" would match pattern "LINEA*", while "LINEB" would match
pattern "LINE*". Additionally, a single asterisk ("*") can serve as a default by matching pattern "LINE*". Additionally, a single asterisk ("*") can serve as a default by matching
any line not matched by any other pattern, as shown in the following example. any line not matched by any other pattern.
[role="xmlContent"]
The following example shows how to match a line not matched by any other pattern in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1010,6 +1049,9 @@ any line not matched by any other pattern, as shown in the following example.
<entry key="*" value-ref="defaultLineTokenizer" /> <entry key="*" value-ref="defaultLineTokenizer" />
---- ----
[role="javaContent"]
The following example shows how to match a line not matched by any other pattern in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1178,7 +1220,8 @@ public void write(T item) throws Exception {
} }
---- ----
A simple configuration might look like the following: [role="xmlContent"]
In XML, a simple example of configuration might look like the following:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1191,6 +1234,9 @@ A simple configuration might look like the following:
</bean> </bean>
---- ----
[role="javaContent"]
In Java, a simple example of configuration might look like the following:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1302,8 +1348,10 @@ public class CustomerCredit {
---- ----
Because a domain object is being used, an implementation of the `FieldExtractor` Because a domain object is being used, an implementation of the `FieldExtractor`
interface must be provided, along with the delimiter to use, as shown in the following interface must be provided, along with the delimiter to use.
example:
[role="xmlContent"]
The following example shows how to use the `FieldExtractor` with a delimiter in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1323,6 +1371,9 @@ example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to use the `FieldExtractor` with a delimiter in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1372,8 +1423,11 @@ public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) th
Delimited is not the only type of flat file format. Many prefer to use a set width for Delimited is not the only type of flat file format. Many prefer to use a set width for
each column to delineate between fields, which is usually referred to as 'fixed width'. each column to delineate between fields, which is usually referred to as 'fixed width'.
Spring Batch supports this in file writing with the `FormatterLineAggregator`. Using the Spring Batch supports this in file writing with the `FormatterLineAggregator`.
same `CustomerCredit` domain object described above, it can be configured as follows:
[role="xmlContent"]
Using the same `CustomerCredit` domain object described above, it can be configured as
follows in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1393,6 +1447,10 @@ same `CustomerCredit` domain object described above, it can be configured as fol
</bean> </bean>
---- ----
[role="xmlContent"]
Using the same `CustomerCredit` domain object described above, it can be configured as
follows in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1415,13 +1473,19 @@ public FlatFileItemWriter<CustomerCredit> itemWriter(Resource outputResource) th
---- ----
Most of the preceding example should look familiar. However, the value of the format Most of the preceding example should look familiar. However, the value of the format
property is new and is shown in the following element: property is new.
[role="xmlContent"]
The following example shows the format property in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
<property name="format" value="%-9s%-2.0f" /> <property name="format" value="%-9s%-2.0f" />
---- ----
[role="javaContent"]
The following example shows the format property in Java:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
... ...
@@ -1551,9 +1615,10 @@ object to be mapped. The example configuration demonstrates this with the value
* `Unmarshaller`: An unmarshalling facility provided by Spring OXM for mapping the XML * `Unmarshaller`: An unmarshalling facility provided by Spring OXM for mapping the XML
fragment to an object. fragment to an object.
[role="xmlContent"]
The following example shows how to define a `StaxEventItemReader` that works with a root The following example shows how to define a `StaxEventItemReader` that works with a root
element named `trade`, a resource of `org/springframework/batch/item/xml/domain/trades.xml`, and an unmarshaller element named `trade`, a resource of `data/iosample/input/input.xml`, and an unmarshaller
called `tradeMarshaller`. called `tradeMarshaller` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1565,6 +1630,11 @@ called `tradeMarshaller`.
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to define a `StaxEventItemReader` that works with a root
element named `trade`, a resource of `data/iosample/input/input.xml`, and an unmarshaller
called `tradeMarshaller` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1585,7 +1655,10 @@ an alias passed in as a map with the first key and value being the name of the f
(that is, a root element) and the object type to bind. Then, similar to a `FieldSet`, the (that is, a root element) and the object type to bind. Then, similar to a `FieldSet`, the
names of the other elements that map to fields within the object type are described as names of the other elements that map to fields within the object type are described as
key/value pairs in the map. In the configuration file, we can use a Spring configuration key/value pairs in the map. In the configuration file, we can use a Spring configuration
utility to describe the required alias, as follows: utility to describe the required alias.
[role="xmlContent"]
The following example shows how to describe the alias in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1605,6 +1678,9 @@ utility to describe the required alias, as follows:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to describe the alias in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1674,8 +1750,12 @@ Output works symmetrically to input. The `StaxEventItemWriter` needs a `Resource
marshaller, and a `rootTagName`. A Java object is passed to a marshaller (typically a marshaller, and a `rootTagName`. A Java object is passed to a marshaller (typically a
standard Spring OXM Marshaller) which writes to a `Resource` by using a custom event standard Spring OXM Marshaller) which writes to a `Resource` by using a custom event
writer that filters the `StartDocument` and `EndDocument` events produced for each writer that filters the `StartDocument` and `EndDocument` events produced for each
fragment by the OXM tools. The following example uses the fragment by the OXM tools.
`StaxEventItemWriter`: // TODO How does `MarshallingEventWriterSerializer` get involved? Because there's a
// property whose name is `marshaller`?
[role="xmlContent"]
The following XML example uses the `MarshallingEventWriterSerializer`:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1688,6 +1768,9 @@ fragment by the OXM tools. The following example uses the
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example uses the `MarshallingEventWriterSerializer`:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1705,10 +1788,12 @@ public StaxEventItemWriter itemWriter(Resource outputResource) {
---- ----
The preceding configuration sets up the three required properties and sets the optional The preceding configuration sets up the three required properties and sets the optional
`overwriteOutput=true` attribute, mentioned earlier in this chapter for specifying whether `overwriteOutput=true` attrbute, mentioned earlier in this chapter for specifying whether
an existing file can be overwritten. It should be noted the marshaller used for the an existing file can be overwritten.
writer in the following example is the exact same as the one used in the reading example
from earlier in the chapter: [role="xmlContent"]
The following XML example uses the same marshaller as the one used in the reading example
shown earlier in the chapter:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1728,6 +1813,10 @@ from earlier in the chapter:
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example uses the same marshaller as the one used in the reading example
shown earlier in the chapter:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1883,9 +1972,12 @@ input for both XML and flat file processing. Consider the following files in a d
file-1.txt file-2.txt ignored.txt file-1.txt file-2.txt ignored.txt
---- ----
`file-1.txt` and `file-2.txt` are formatted the same and, for business reasons, should be file-1.txt and file-2.txt are formatted the same and, for business reasons, should be
processed together. The `MultiResourceItemReader` can be used to read in both files by processed together. The `MuliResourceItemReader` can be used to read in both files by
using wildcards, as shown in the following example: using wildcards.
[role="xmlContent"]
The following example shows how to read files with wildcards in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1896,6 +1988,9 @@ using wildcards, as shown in the following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to read files with wildcards in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2040,8 +2135,10 @@ is that it allows items to be 'streamed'. The `read` method can be called once,
can be written out by an `ItemWriter`, and then the next item can be obtained with can be written out by an `ItemWriter`, and then the next item can be obtained with
`read`. This allows item reading and writing to be done in 'chunks' and committed `read`. This allows item reading and writing to be done in 'chunks' and committed
periodically, which is the essence of high performance batch processing. Furthermore, it periodically, which is the essence of high performance batch processing. Furthermore, it
is very easily configured for injection into a Spring Batch `Step`, as shown in the is easily configured for injection into a Spring Batch `Step`.
following example:
[role="xmlContent"]
The following example shows how to inject an `ItemReader` into a `Step` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2055,6 +2152,9 @@ following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to inject an `ItemReader` into a `Step` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2156,9 +2256,12 @@ This configured `ItemReader` returns `CustomerCredit` objects in the exact same
as described by the `JdbcCursorItemReader`, assuming hibernate mapping files have been as described by the `JdbcCursorItemReader`, assuming hibernate mapping files have been
created correctly for the `Customer` table. The 'useStatelessSession' property defaults created correctly for the `Customer` table. The 'useStatelessSession' property defaults
to true but has been added here to draw attention to the ability to switch it on or off. to true but has been added here to draw attention to the ability to switch it on or off.
It is also worth noting that the fetch size of the underlying cursor can be set via the It is also worth noting that the fetch size of the underlying cursor can be set with the
`setFetchSize` property. As with `JdbcCursorItemReader`, configuration is `setFetchSize` property. As with `JdbcCursorItemReader`, configuration is
straightforward, as shown in the following example: straightforward.
[role="xmlContent"]
The following example shows how to inject a Hibernate `ItemReader` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2170,6 +2273,9 @@ straightforward, as shown in the following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to inject a Hibernate `ItemReader` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2196,7 +2302,8 @@ The stored procedure can return the cursor in three different ways:
* As a ref-cursor returned as an out parameter (used by Oracle and PostgreSQL). * As a ref-cursor returned as an out parameter (used by Oracle and PostgreSQL).
* As the return value of a stored function call. * As the return value of a stored function call.
The following example configuration uses the same 'customer credit' example as earlier [role="xmlContent"]
The following XML example configuration uses the same 'customer credit' example as earlier
examples: examples:
.XML Configuration .XML Configuration
@@ -2211,6 +2318,10 @@ examples:
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example configuration uses the same 'customer credit' example as
earlier examples:
.Java Configuration .Java Configuration
[source, xml, role="javaContent"] [source, xml, role="javaContent"]
---- ----
@@ -2231,8 +2342,11 @@ The preceding example relies on the stored procedure to provide a `ResultSet` as
returned result (option 1 from earlier). returned result (option 1 from earlier).
If the stored procedure returned a `ref-cursor` (option 2), then we would need to provide If the stored procedure returned a `ref-cursor` (option 2), then we would need to provide
the position of the out parameter that is the returned `ref-cursor`. The following the position of the out parameter that is the returned `ref-cursor`.
example shows how to work with the first parameter being a ref-cursor:
[role="xmlContent"]
The following example shows how to work with the first parameter being a ref-cursor in
XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2247,6 +2361,10 @@ example shows how to work with the first parameter being a ref-cursor:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to work with the first parameter being a ref-cursor in
Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2264,8 +2382,10 @@ public StoredProcedureItemReader reader(DataSource dataSource) {
---- ----
If the cursor was returned from a stored function (option 3), we would need to set the If the cursor was returned from a stored function (option 3), we would need to set the
property "[maroon]#function#" to `true`. It defaults to `false`. The following example property "[maroon]#function#" to `true`. It defaults to `false`.
shows what that would look like:
[role="xmlContent"]
The following example shows property to `true` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2280,6 +2400,9 @@ shows what that would look like:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows property to `true` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2300,10 +2423,13 @@ In all of these cases, we need to define a `RowMapper` as well as a `DataSource`
actual procedure name. actual procedure name.
If the stored procedure or function takes in parameters, then they must be declared and If the stored procedure or function takes in parameters, then they must be declared and
set via the `parameters` property. The following example, for Oracle, declares three set by using the `parameters` property. The following example, for Oracle, declares three
parameters. The first one is the out parameter that returns the ref-cursor, and the parameters. The first one is the `out` parameter that returns the ref-cursor, and the
second and third are in parameters that takes a value of type `INTEGER`. second and third are in parameters that takes a value of type `INTEGER`.
[role="xmlContent"]
The following example shows how to work with parameters in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -2338,6 +2464,9 @@ second and third are in parameters that takes a value of type `INTEGER`.
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to work with parameters in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2396,7 +2525,8 @@ After the reader has been opened, it passes back one item per call to `read` in
basic fashion as any other `ItemReader`. The paging happens behind the scenes when basic fashion as any other `ItemReader`. The paging happens behind the scenes when
additional rows are needed. additional rows are needed.
The following example configuration uses a similar 'customer credit' example as the [role="xmlContent"]
The following XML example configuration uses a similar 'customer credit' example as the
cursor-based `ItemReaders` shown previously: cursor-based `ItemReaders` shown previously:
.XML Configuration .XML Configuration
@@ -2422,6 +2552,10 @@ cursor-based `ItemReaders` shown previously:
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example configuration uses a similar 'customer credit' example as the
cursor-based `ItemReaders` shown previously:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2475,8 +2609,11 @@ be garbage collected once the page is processed.
The `JpaPagingItemReader` lets you declare a JPQL statement and pass in a The `JpaPagingItemReader` lets you declare a JPQL statement and pass in a
`EntityManagerFactory`. It then passes back one item per call to read in the same basic `EntityManagerFactory`. It then passes back one item per call to read in the same basic
fashion as any other `ItemReader`. The paging happens behind the scenes when additional fashion as any other `ItemReader`. The paging happens behind the scenes when additional
entities are needed. The following example configuration uses the same 'customer credit' entities are needed.
example as the JDBC reader shown previously:
[role="xmlContent"]
The following XML example configuration uses the same 'customer credit' example as the
JDBC reader shown previously:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2488,6 +2625,10 @@ example as the JDBC reader shown previously:
</bean> </bean>
---- ----
[role="xmlContent"]
The following Java example configuration uses the same 'customer credit' example as the
JDBC reader shown previously:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2563,8 +2704,10 @@ another Spring Batch class or because it truly is the main `ItemReader` for a st
fairly trivial to write an adapter class for each service that needs wrapping, but fairly trivial to write an adapter class for each service that needs wrapping, but
because it is such a common concern, Spring Batch provides implementations: because it is such a common concern, Spring Batch provides implementations:
`ItemReaderAdapter` and `ItemWriterAdapter`. Both classes implement the standard Spring `ItemReaderAdapter` and `ItemWriterAdapter`. Both classes implement the standard Spring
method by invoking the delegate pattern and are fairly simple to set up. The following method by invoking the delegate pattern and are fairly simple to set up.
example uses the `ItemReaderAdapter`:
[role="xmlContent"]
The following XML example uses the `ItemReaderAdapter`:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2577,6 +2720,9 @@ example uses the `ItemReaderAdapter`:
<bean id="fooService" class="org.springframework.batch.item.sample.FooService" /> <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
---- ----
[role="javaContent"]
The following Java example uses the `ItemReaderAdapter`:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2600,7 +2746,10 @@ One important point to note is that the contract of the `targetMethod` must be t
as the contract for `read`: When exhausted, it returns `null`. Otherwise, it returns an as the contract for `read`: When exhausted, it returns `null`. Otherwise, it returns an
`Object`. Anything else prevents the framework from knowing when processing should end, `Object`. Anything else prevents the framework from knowing when processing should end,
either causing an infinite loop or incorrect failure, depending upon the implementation either causing an infinite loop or incorrect failure, depending upon the implementation
of the `ItemWriter`. The following example uses the `ItemWriterAdapter`: of the `ItemWriter`.
[role="xmlContent"]
The following XML example uses the `ItemWriterAdapter`:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2613,6 +2762,9 @@ of the `ItemWriter`. The following example uses the `ItemWriterAdapter`:
<bean id="fooService" class="org.springframework.batch.item.sample.FooService" /> <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
---- ----
[role="javaContent"]
The following Java example uses the `ItemWriterAdapter`:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2659,7 +2811,10 @@ public interface Validator<T> {
The contract is that the `validate` method throws an exception if the object is invalid The contract is that the `validate` method throws an exception if the object is invalid
and returns normally if it is valid. Spring Batch provides an out of the box and returns normally if it is valid. Spring Batch provides an out of the box
`ValidatingItemProcessor`, as shown in the following bean definition: `ValidatingItemProcessor`.
[role="xmlContent"]
The following bean definition shows how to configure a `ValidatingItemProcessor` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2675,6 +2830,9 @@ and returns normally if it is valid. Spring Batch provides an out of the box
</bean> </bean>
---- ----
[role="javaContent"]
The following bean definition shows how to configure a `ValidatingItemProcessor` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2749,7 +2907,10 @@ contain an extra statement in the `where` clause, such as `where PROCESSED_IND =
thereby ensuring that only unprocessed records are returned in the case of a restart. In thereby ensuring that only unprocessed records are returned in the case of a restart. In
this scenario, it is preferable to not store any state, such as the current row number, this scenario, it is preferable to not store any state, such as the current row number,
since it is irrelevant upon restart. For this reason, all readers and writers include the since it is irrelevant upon restart. For this reason, all readers and writers include the
'saveState' property, as shown in the following example: 'saveState' property.
[role="xmlContent"]
The following bean definition shows how to prevent state persistence in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2773,6 +2934,9 @@ since it is irrelevant upon restart. For this reason, all readers and writers in
</bean> </bean>
---- ----
[role="javaContent"]
The following bean definition shows how to prevent state persistence in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----

View File

@@ -236,7 +236,7 @@ configure AOP interceptors, see the Spring User Guide):
---- ----
[role="javaContent"] [role="javaContent"]
The following example demonstrates using java configuration to The following example demonstrates using Java configuration to
repeat a service call to a method called `processMessage` (for more detail on how to repeat a service call to a method called `processMessage` (for more detail on how to
configure AOP interceptors, see the Spring User Guide): configure AOP interceptors, see the Spring User Guide):

View File

@@ -10,30 +10,23 @@ ifndef::onlyonetoggle[]
include::toggle.adoc[] include::toggle.adoc[]
endif::onlyonetoggle[] endif::onlyonetoggle[]
To make processing more robust and less prone to failure, it sometimes To make processing more robust and less prone to failure, it sometimes helps to
helps to automatically retry a failed operation in case it might automatically retry a failed operation in case it might succeed on a subsequent attempt.
succeed on a subsequent attempt. Errors that are susceptible to intermittent failure Errors that are susceptible to intermittent failure are often transient in nature.
are often transient in nature. Examples include remote calls to a web Examples include remote calls to a web service that fails because of a network glitch or a
service that fails because of a network glitch or a `DeadlockLoserDataAccessException` in a database update.
`DeadlockLoserDataAccessException` in a database update.
[[retryTemplate]] [[retryTemplate]]
=== `RetryTemplate` === `RetryTemplate`
[NOTE] [NOTE]
==== ====
The retry functionality was pulled out of Spring Batch as of 2.2.0. The retry functionality was pulled out of Spring Batch as of 2.2.0.
It is now part of a new library, https://github.com/spring-projects/spring-retry[Spring Retry]. It is now part of a new library, https://github.com/spring-projects/spring-retry[Spring Retry].
==== ====
To automate retry operations Spring Batch has the `RetryOperations` strategy. The
To automate retry following interface definition for `RetryOperations`:
operations Spring Batch has the `RetryOperations`
strategy. The following interface definition for `RetryOperations`:
[source, java] [source, java]
---- ----
@@ -53,9 +46,8 @@ public interface RetryOperations {
} }
---- ----
The basic callback is a simple interface that lets you The basic callback is a simple interface that lets you insert some business logic to be
insert some business logic to be retried, as shown in the following interface definition: retried, as shown in the following interface definition:
[source, java] [source, java]
---- ----
@@ -66,19 +58,15 @@ public interface RetryCallback<T, E extends Throwable> {
} }
---- ----
The callback runs and, if it fails (by throwing an The callback runs and, if it fails (by throwing an `Exception`), it is retried until
`Exception`), it is retried until either it is either it is successful or the implementation aborts. There are a number of overloaded
successful or the implementation aborts. There are a number of `execute` methods in the `RetryOperations` interface. Those methods deal with various use
overloaded `execute` methods in the cases for recovery when all retry attempts are exhausted and deal with retry state, which
`RetryOperations` interface. Those methods deal with various use lets clients and implementations store information between calls (we cover this in more
cases for recovery when all retry attempts are exhausted and deal with detail later in the chapter).
retry state, which allows clients and implementations to store information
between calls (we cover this in more detail later in the chapter).
The simplest general purpose implementation of
`RetryOperations` is
`RetryTemplate`. It can be used as follows:
The simplest general purpose implementation of `RetryOperations` is `RetryTemplate`. It
can be used as follows:
[source, java] [source, java]
---- ----
@@ -99,36 +87,26 @@ Foo result = template.execute(new RetryCallback<Foo>() {
}); });
---- ----
In the preceding example, we make a web service call and return the result In the preceding example, we make a web service call and return the result to the user. If
to the user. If that call fails, then it is retried until a timeout is that call fails, then it is retried until a timeout is reached.
reached.
[[retryContext]] [[retryContext]]
==== `RetryContext` ==== `RetryContext`
The method parameter for the `RetryCallback` The method parameter for the `RetryCallback` is a `RetryContext`. Many callbacks ignore
is a `RetryContext`. Many callbacks the context, but, if necessary, it can be used as an attribute bag to store data for the
ignore the context, but, if necessary, it can be used as an attribute bag duration of the iteration.
to store data for the duration of the iteration.
A `RetryContext` has a parent context A `RetryContext` has a parent context if there is a nested retry in progress in the same
if there is a nested retry in progress in the same thread. The parent thread. The parent context is occasionally useful for storing data that need to be shared
context is occasionally useful for storing data that need to be shared between calls to `execute`.
between calls to `execute`.
[[recoveryCallback]] [[recoveryCallback]]
==== `RecoveryCallback` ==== `RecoveryCallback`
When a retry is exhausted, the When a retry is exhausted, the `RetryOperations` can pass control to a different callback,
`RetryOperations` can pass control to a different called the `RecoveryCallback`. To use this feature, clients pass in the callbacks together
callback, called the `RecoveryCallback`. To use this to the same method, as shown in the following example:
feature, clients pass in the callbacks together to the same method,
as shown in the following example:
[source, java] [source, java]
---- ----
@@ -143,141 +121,100 @@ Foo foo = template.execute(new RetryCallback<Foo>() {
}); });
---- ----
If the business logic does not succeed before the template If the business logic does not succeed before the template decides to abort, then the
decides to abort, then the client is given the chance to do some client is given the chance to do some alternate processing through the recovery callback.
alternate processing through the recovery callback.
[[statelessRetry]] [[statelessRetry]]
==== Stateless Retry ==== Stateless Retry
In the simplest case, a retry is just a while loop. The In the simplest case, a retry is just a while loop. The `RetryTemplate` can just keep
`RetryTemplate` can just keep trying until it trying until it either succeeds or fails. The `RetryContext` contains some state to
either succeeds or fails. The `RetryContext` determine whether to retry or abort, but this state is on the stack and there is no need
contains some state to determine whether to retry or abort, but this to store it anywhere globally, so we call this stateless retry. The distinction between
state is on the stack and there is no need to store it anywhere stateless and stateful retry is contained in the implementation of the `RetryPolicy` (the
globally, so we call this stateless retry. The distinction between `RetryTemplate` can handle both). In a stateless retry, the retry callback is always
stateless and stateful retry is contained in the implementation of the executed in the same thread it was on when it failed.
`RetryPolicy` (the
`RetryTemplate` can handle both). In a stateless
retry, the retry callback is always executed in the same thread it was on
when it failed.
[[statefulRetry]] [[statefulRetry]]
==== Stateful Retry ==== Stateful Retry
Where the failure has caused a transactional resource to become Where the failure has caused a transactional resource to become invalid, there are some
invalid, there are some special considerations. This does not apply to a special considerations. This does not apply to a simple remote call because there is no
simple remote call because there is no transactional resource (usually), transactional resource (usually), but it does sometimes apply to a database update,
but it does sometimes apply to a database update, especially when using especially when using Hibernate. In this case it only makes sense to re-throw the
Hibernate. In this case it only makes sense to re-throw the exception exception that called the failure immediately, so that the transaction can roll back and
that called the failure immediately, so that the transaction can roll we can start a new, valid transaction.
back and we can start a new, valid transaction.
In cases involving transactions, a stateless retry is not good enough, because the In cases involving transactions, a stateless retry is not good enough, because the
re-throw and roll back necessarily involve leaving the re-throw and roll back necessarily involve leaving the `RetryOperations.execute()` method
`RetryOperations.execute()` method and potentially losing the and potentially losing the context that was on the stack. To avoid losing it we have to
context that was on the stack. To avoid losing it we have to introduce a introduce a storage strategy to lift it off the stack and put it (at a minimum) in heap
storage strategy to lift it off the stack and put it (at a minimum) in storage. For this purpose, Spring Batch provides a storage strategy called
heap storage. For this purpose, Spring Batch provides a storage strategy called `RetryContextCache`, which can be injected into the `RetryTemplate`. The default
`RetryContextCache`, which can be injected into the implementation of the `RetryContextCache` is in memory, using a simple `Map`. Advanced
`RetryTemplate`. The default implementation of the usage with multiple processes in a clustered environment might also consider implementing
`RetryContextCache` is in memory, using a simple the `RetryContextCache` with a cluster cache of some sort (however, even in a clustered
`Map`. Advanced usage with multiple processes in a environment, this might be overkill).
clustered environment might also consider implementing the
`RetryContextCache` with a cluster cache of some
sort (however, even in a clustered environment, this might be
overkill).
Part of the responsibility of the Part of the responsibility of the `RetryOperations` is to recognize the failed operations
`RetryOperations` is to recognize the failed when they come back in a new execution (and usually wrapped in a new transaction). To
operations when they come back in a new execution (and usually wrapped facilitate this, Spring Batch provides the `RetryState` abstraction. This works in
in a new transaction). To facilitate this, Spring Batch provides the conjunction with a special `execute` methods in the `RetryOperations` interface.
`RetryState` abstraction. This works in conjunction
with a special `execute` methods in the
`RetryOperations` interface.
The way the failed operations are recognized is by identifying the
state across multiple invocations of the retry. To identify the state,
the user can provide a `RetryState` object that is
responsible for returning a unique key identifying the item. The
identifier is used as a key in the
`RetryContextCache` interface.
The way the failed operations are recognized is by identifying the state across multiple
invocations of the retry. To identify the state, the user can provide a `RetryState`
object that is responsible for returning a unique key identifying the item. The identifier
is used as a key in the `RetryContextCache` interface.
[WARNING] [WARNING]
==== ====
Be very careful with the implementation of Be very careful with the implementation of `Object.equals()` and `Object.hashCode()` in
`Object.equals()` and `Object.hashCode()` in the the key returned by `RetryState`. The best advice is to use a business key to identify the
key returned by `RetryState`. The best advice is items. In the case of a JMS message, the message ID can be used.
to use a business key to identify the items. In the case of a JMS
message, the message ID can be used.
==== ====
When the retry is exhausted, there is also the option to handle the failed item in a
different way, instead of calling the `RetryCallback` (which is now presumed to be likely
to fail). Just like in the stateless case, this option is provided by the
`RecoveryCallback`, which can be provided by passing it in to the `execute` method of
`RetryOperations`.
When the retry is exhausted, there is also the option to handle the The decision to retry or not is actually delegated to a regular `RetryPolicy`, so the
failed item in a different way, instead of calling the usual concerns about limits and timeouts can be injected there (described later in this
`RetryCallback` (which is now presumed to be likely chapter).
to fail). Just like in the stateless case, this option is provided by
the `RecoveryCallback`, which can be provided by
passing it in to the `execute` method of
`RetryOperations`.
The decision to retry or not is actually delegated to a regular
`RetryPolicy`, so the usual concerns about limits
and timeouts can be injected there (described later in this chapter).
[[retryPolicies]] [[retryPolicies]]
=== Retry Policies === Retry Policies
Inside a `RetryTemplate`, the decision to retry Inside a `RetryTemplate`, the decision to retry or fail in the `execute` method is
or fail in the `execute` method is determined by a determined by a `RetryPolicy`, which is also a factory for the `RetryContext`. The
`RetryPolicy`, which is also a factory for the `RetryTemplate` has the responsibility to use the current policy to create a
`RetryContext`. The `RetryContext` and pass that in to the `RetryCallback` at every attempt. After a callback
`RetryTemplate` has the responsibility to use the fails, the `RetryTemplate` has to make a call to the `RetryPolicy` to ask it to update its
current policy to create a `RetryContext` and pass state (which is stored in the `RetryContext`) and then asks the policy if another attempt
that in to the `RetryCallback` at every attempt. can be made. If another attempt cannot be made (such as when a limit is reached or a
After a callback fails, the `RetryTemplate` has to timeout is detected) then the policy is also responsible for handling the exhausted state.
make a call to the `RetryPolicy` to ask it to update Simple implementations throw `RetryExhaustedException`, which causes any enclosing
its state (which is stored in the transaction to be rolled back. More sophisticated implementations might attempt to take
`RetryContext`) and then asks the policy if some recovery action, in which case the transaction can remain intact.
another attempt can be made. If another attempt cannot be made (such as when a
limit is reached or a timeout is detected) then the policy is also
responsible for handling the exhausted state. Simple implementations
throw `RetryExhaustedException`, which causes
any enclosing transaction to be rolled back. More sophisticated
implementations might attempt to take some recovery action, in which case
the transaction can remain intact.
[TIP] [TIP]
==== ====
Failures are inherently either retryable or not. If the same Failures are inherently either retryable or not. If the same exception is always going to
exception is always going to be thrown from the business logic, it be thrown from the business logic, it does no good to retry it. So do not retry on all
does no good to retry it. So do not retry on all exception types. Rather, try to exception types. Rather, try to focus on only those exceptions that you expect to be
focus on only those exceptions that you expect to be retryable. It is not retryable. It is not usually harmful to the business logic to retry more aggressively, but
usually harmful to the business logic to retry more aggressively, but it is wasteful, because, if a failure is deterministic, you spend time retrying something
it is wasteful, because, if a failure is deterministic, you spend time that you know in advance is fatal.
retrying something that you know in advance is fatal.
==== ====
Spring Batch provides some simple general purpose implementations of stateless
`RetryPolicy`, such as `SimpleRetryPolicy` and `TimeoutRetryPolicy` (used in the preceding example).
Spring Batch provides some simple general purpose implementations of The `SimpleRetryPolicy` allows a retry on any of a named list of exception types, up to a
stateless `RetryPolicy`, such as fixed number of times. It also has a list of "fatal" exceptions that should never be
`SimpleRetryPolicy` and retried, and this list overrides the retryable list so that it can be used to give finer
`TimeoutRetryPolicy` (used in the preceding example). control over the retry behavior, as shown in the following example:
The `SimpleRetryPolicy` allows a retry on
any of a named list of exception types, up to a fixed number of times. It
also has a list of "fatal" exceptions that should never be retried, and
this list overrides the retryable list so that it can be used to give
finer control over the retry behavior, as shown in the following example:
[source, java] [source, java]
---- ----
@@ -299,31 +236,24 @@ template.execute(new RetryCallback<Foo>() {
}); });
---- ----
There is also a more flexible implementation called There is also a more flexible implementation called `ExceptionClassifierRetryPolicy`,
`ExceptionClassifierRetryPolicy`, which allows the which lets the user configure different retry behavior for an arbitrary set of exception
user to configure different retry behavior for an arbitrary set of types though the `ExceptionClassifier` abstraction. The policy works by calling on the
exception types though the `ExceptionClassifier` classifier to convert an exception into a delegate `RetryPolicy`. For example, one
abstraction. The policy works by calling on the classifier to convert an exception type can be retried more times before failure than another by mapping it to a
exception into a delegate `RetryPolicy`. For different policy.
example, one exception type can be retried more times before failure than
another by mapping it to a different policy.
Users might need to implement their own retry policies for more Users might need to implement their own retry policies for more customized decisions. For
customized decisions. For instance, a custom retry policy makes sense when there is a well-known, instance, a custom retry policy makes sense when there is a well-known, solution-specific
solution-specific classification of exceptions into retryable and not classification of exceptions into retryable and not retryable.
retryable.
[[backoffPolicies]] [[backoffPolicies]]
=== Backoff Policies === Backoff Policies
When retrying after a transient failure, it often helps to wait a bit When retrying after a transient failure, it often helps to wait a bit before trying again,
before trying again, because usually the failure is caused by some problem because usually the failure is caused by some problem that can only be resolved by
that can only be resolved by waiting. If a waiting. If a `RetryCallback` fails, the `RetryTemplate` can pause execution according to
`RetryCallback` fails, the the `BackoffPolicy`.
`RetryTemplate` can pause execution according to the
`BackoffPolicy`.
The following code shows the interface definition for the `BackOffPolicy` interface: The following code shows the interface definition for the `BackOffPolicy` interface:
@@ -339,31 +269,22 @@ public interface BackoffPolicy {
} }
---- ----
A `BackoffPolicy` is free to implement A `BackoffPolicy` is free to implement the backOff in any way it chooses. The policies
the backOff in any way it chooses. The policies provided by Spring Batch provided by Spring Batch out of the box all use `Object.wait()`. A common use case is to
out of the box all use `Object.wait()`. A common use case is to backoff with an exponentially increasing wait period, to avoid two retries getting into
backoff with an exponentially increasing wait period, to avoid two retries lock step and both failing (this is a lesson learned from ethernet). For this purpose,
getting into lock step and both failing (this is a lesson learned from Spring Batch provides the `ExponentialBackoffPolicy`.
ethernet). For this purpose, Spring Batch provides the
`ExponentialBackoffPolicy`.
[[retryListeners]] [[retryListeners]]
=== Listeners === Listeners
Often, it is useful to be able to receive additional callbacks for Often, it is useful to be able to receive additional callbacks for cross cutting concerns
cross cutting concerns across a number of different retries. For this across a number of different retries. For this purpose, Spring Batch provides the
purpose, Spring Batch provides the `RetryListener` `RetryListener` interface. The `RetryTemplate` lets users register `RetryListeners`, and
interface. The `RetryTemplate` lets users they are given callbacks with `RetryContext` and `Throwable` where available during the
register `RetryListeners`, and they are given iteration.
callbacks with `RetryContext` and
`Throwable` where available during the
iteration.
The following code shows the interface definition for `RetryListener`: The following code shows the interface definition for `RetryListener`:
[source, java] [source, java]
---- ----
public interface RetryListener { public interface RetryListener {
@@ -376,39 +297,28 @@ public interface RetryListener {
} }
---- ----
The `open` and The `open` and `close` callbacks come before and after the entire retry in the simplest
`close` callbacks come before and after the entire case, and `onError` applies to the individual `RetryCallback` calls. The `close` method
retry in the simplest case, and `onError` applies to might also receive a `Throwable`. If there has been an error, it is the last one thrown by
the individual `RetryCallback` calls. The the `RetryCallback`.
`close` method might also receive a
`Throwable`. If there has been an error, it is the
last one thrown by the `RetryCallback`.
Note that, when there is more than one listener, they are in a list, Note that, when there is more than one listener, they are in a list, so there is an order.
so there is an order. In this case, `open` is In this case, `open` is called in the same order while `onError` and `close` are called in
called in the same order while `onError` and reverse order.
`close` are called in reverse order.
[[declarativeRetry]] [[declarativeRetry]]
=== Declarative Retry === Declarative Retry
Sometimes, there is some business processing that you know you want Sometimes, there is some business processing that you know you want to retry every time it
to retry every time it happens. The classic example of this is the remote happens. The classic example of this is the remote service call. Spring Batch provides an
service call. Spring Batch provides an AOP interceptor that wraps a method AOP interceptor that wraps a method call in a `RetryOperations` implementation for just
call in a `RetryOperations` implementation for just this purpose. this purpose. The `RetryOperationsInterceptor` executes the intercepted method and retries
The `RetryOperationsInterceptor` executes the on failure according to the `RetryPolicy` in the provided `RepeatTemplate`.
intercepted method and retries on failure according to the
`RetryPolicy` in the provided
`RetryTemplate`.
[role="xmlContent"] [role="xmlContent"]
The following example shows a declarative retry that uses the Spring AOP The following example shows a declarative retry that uses the Spring AOP namespace to
namespace to retry a service call to a method called retry a service call to a method called `remoteCall` (for more detail on how to configure
`remoteCall` (for more detail on how to configure AOP interceptors, see the Spring User Guide):
AOP interceptors, see the Spring User Guide):
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -424,11 +334,9 @@ The following example shows a declarative retry that uses the Spring AOP
---- ----
[role="javaContent"] [role="javaContent"]
The following example shows a declarative retry that uses java configuration The following example shows a declarative retry that uses java configuration to retry a
to retry a service call to a method called service call to a method called `remoteCall` (for more detail on how to configure AOP
`remoteCall` (for more detail on how to configure interceptors, see the Spring User Guide):
AOP interceptors, see the Spring User Guide):
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -450,7 +358,5 @@ public MyService myService() {
} }
---- ----
The preceding example uses a default The preceding example uses a default `RetryTemplate` inside the interceptor. To change the
`RetryTemplate` inside the interceptor. To change the policies or listeners, you can inject an instance of `RetryTemplate` into the interceptor.
policies or listeners, you can inject an instance of
`RetryTemplate` into the interceptor.

View File

@@ -41,8 +41,7 @@ The simplest way to start parallel processing is to add a `TaskExecutor` to your
configuration. configuration.
[role="xmlContent"] [role="xmlContent"]
For example, you might add an attribute of the `tasklet`, as shown in the For example, you might add an attribute of the `tasklet`, as follows:
following example:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -52,7 +51,7 @@ following example:
---- ----
[role="javaContent"] [role="javaContent"]
When using java configuration, a `TaskExecutor` can be added to the step When using java configuration, a `TaskExecutor` can be added to the step,
as shown in the following example: as shown in the following example:
.Java Configuration .Java Configuration
@@ -101,7 +100,8 @@ For example you might increase the throttle-limit, as shown in the following exa
---- ----
[role="javaContent"] [role="javaContent"]
When using java configuration, the builders provide access to the throttle limit: When using Java configuration, the builders provide access to the throttle limit, as shown
in the following example:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -175,7 +175,7 @@ as shown in the following example:
---- ----
[role="javaContent"] [role="javaContent"]
When using java configuration, executing steps `(step1,step2)` in parallel with `step3` When using Java configuration, executing steps `(step1,step2)` in parallel with `step3`
is straightforward, as shown in the following example: is straightforward, as shown in the following example:
.Java Configuration .Java Configuration
@@ -295,7 +295,8 @@ many objects and or processes playing this role, and the `PartitionStep` is show
the execution. the execution.
[role="xmlContent"] [role="xmlContent"]
The following example shows the `PartitionStep` configuration: The following example shows the `PartitionStep` configuration when using XML
configuration:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -307,7 +308,8 @@ The following example shows the `PartitionStep` configuration:
---- ----
[role="javaContent"] [role="javaContent"]
The following example shows the `PartitionStep` configuration using java configuration: The following example shows the `PartitionStep` configuration when using Java
configuration:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -469,8 +471,10 @@ the `Partitioner` output might resemble the content of the following table:
|filecopy:partition2|fileName=/home/data/three |filecopy:partition2|fileName=/home/data/three
|=============== |===============
Then the file name can be bound to a step using late binding to the execution context, as Then the file name can be bound to a step using late binding to the execution context.
shown in the following example:
[role="xmlContent"]
The following example shows how to define late binding in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -481,6 +485,9 @@ shown in the following example:
</bean> </bean>
---- ----
[role="xmlContent"]
The following example shows how to define late binding in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----

View File

@@ -11,7 +11,6 @@ include::toggle.adoc[]
endif::onlyonetoggle[] endif::onlyonetoggle[]
[[spring-batch-integration-introduction]] [[spring-batch-integration-introduction]]
=== Spring Batch Integration Introduction === Spring Batch Integration Introduction
Many users of Spring Batch may encounter requirements that are Many users of Spring Batch may encounter requirements that are
@@ -39,29 +38,19 @@ also be embedded in a job (for example reading or writing items for
processing via channels). Remote partitioning and remote chunking processing via channels). Remote partitioning and remote chunking
provide methods to distribute workloads over a number of workers. provide methods to distribute workloads over a number of workers.
This section covers the following key concepts: This section covers the following key concepts:
[role="xmlContent"] [role="xmlContent"]
* <<spring-batch-integration.adoc#namespace-support,Namespace Support>> * <<spring-batch-integration.adoc#namespace-support,Namespace Support>>
[[continue-section-list]] [[continue-section-list]]
* <<spring-batch-integration.adoc#launching-batch-jobs-through-messages,Launching Batch Jobs through Messages>> * <<spring-batch-integration.adoc#launching-batch-jobs-through-messages,Launching Batch Jobs through Messages>>
* <<spring-batch-integration.adoc#providing-feedback-with-informational-messages,Providing Feedback with Informational Messages>> * <<spring-batch-integration.adoc#providing-feedback-with-informational-messages,Providing Feedback with Informational Messages>>
* <<spring-batch-integration.adoc#asynchronous-processors,Asynchronous Processors>> * <<spring-batch-integration.adoc#asynchronous-processors,Asynchronous Processors>>
* <<spring-batch-integration.adoc#externalizing-batch-process-execution,Externalizing * <<spring-batch-integration.adoc#externalizing-batch-process-execution,Externalizing
Batch Process Execution>> Batch Process Execution>>
[[namespace-support]] [[namespace-support]]
[role="xmlContent"] [role="xmlContent"]
==== Namespace Support ==== Namespace Support
@@ -261,13 +250,13 @@ Batch reference documentation on
===== Spring Batch Integration Configuration ===== Spring Batch Integration Configuration
The following configuration creates a file Consider a case where someone needs to create a file `inbound-channel-adapter` to listen
`inbound-channel-adapter` to listen for CSV for CSV files in the provided directory, hand them off to a transformer
files in the provided directory, hand them off to our (`FileMessageToJobRequest`), launch the job through the _Job Launching Gateway_, and then
transformer (`FileMessageToJobRequest`), log the output of the `JobExecution` with the `logging-channel-adapter`.
launch the job via the __Job Launching Gateway__, and then log the output of the
`JobExecution` with the [role="xmlContent"]
`logging-channel-adapter`. The following example shows how that common case can be configured in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -297,6 +286,9 @@ launch the job via the __Job Launching Gateway__, and then log the output of the
<int:logging-channel-adapter channel="jobLaunchReplyChannel"/> <int:logging-channel-adapter channel="jobLaunchReplyChannel"/>
---- ----
[role="javaContent"]
The following example shows how that common case can be configured in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -332,13 +324,14 @@ public IntegrationFlow integrationFlow(JobLaunchingGateway jobLaunchingGateway)
[[example-itemreader-configuration]] [[example-itemreader-configuration]]
===== Example ItemReader Configuration ===== Example ItemReader Configuration
Now that we are polling for files and launching jobs, we need to Now that we are polling for files and launching jobs, we need to configure our Spring
configure our Spring Batch Batch `ItemReader` (for example) to use the files found at the location defined by the job
`ItemReader` (for example) to use the files found at the location defined parameter called "input.file.name", as shown in the following bean configuration:
by the job parameter called "input.file.name", as shown in the following bean configuration:
[role="xmlContent"]
The following XML example shows the necessary bean configuration:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -350,6 +343,9 @@ by the job parameter called "input.file.name", as shown in the following bean co
</bean> </bean>
---- ----
[role="javaContent"]
The following Java example shows the necessary bean configuration:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -414,7 +410,10 @@ to a `SubscribableChannel`.
When this `Gateway` is receiving messages from a When this `Gateway` is receiving messages from a
`PollableChannel`, you must either provide `PollableChannel`, you must either provide
a global default `Poller` or provide a `Poller` sub-element to the a global default `Poller` or provide a `Poller` sub-element to the
`Job Launching Gateway`, as shown in the following example: `Job Launching Gateway`.
[role="xmlContent"]
The following example shows how to provide a poller in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -425,6 +424,9 @@ a global default `Poller` or provide a `Poller` sub-element to the
</batch-int:job-launching-gateway> </batch-int:job-launching-gateway>
---- ----
[role="javaContent"]
The following example shows how to provide a poller in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -438,60 +440,49 @@ public JobLaunchingGateway sampleJobLaunchingGateway() {
---- ----
[[providing-feedback-with-informational-messages]] [[providing-feedback-with-informational-messages]]
==== Providing Feedback with Informational Messages ==== Providing Feedback with Informational Messages
As Spring Batch jobs can run for long times, providing progress As Spring Batch jobs can run for long times, providing progress
information is often critical. For example, stake-holders may want information is often critical. For example, stake-holders may want
to be notified if some or all parts of a batch job have failed. to be notified if some or all parts of a batch job have failed.
Spring Batch provides support for this information being gathered Spring Batch provides support for this information being gathered
through: through:
* Active polling * Active polling
* Event-driven listeners * Event-driven listeners
When starting a Spring Batch job asynchronously (for example, by using the `Job Launching
Gateway`), a `JobExecution` instance is returned. Thus, `JobExecution.getJobId()` can be
used to continuously poll for status updates by retrieving updated instances of the
`JobExecution` from the `JobRepository` by using the `JobExplorer`. However, this is
considered sub-optimal, and an event-driven approach should be preferred.
When starting a Spring Batch job asynchronously (for example, by using the Therefore, Spring Batch provides listeners, including the three most commonly used
`Job Launching Gateway`), a listeners:
`JobExecution` instance is returned. Thus,
`JobExecution.getJobId()` can be used to
continuously poll for status updates by retrieving updated
instances of the `JobExecution` from the
`JobRepository` by using the
`JobExplorer`. However, this is considered
sub-optimal, and an event-driven approach should be preferred.
* `StepListener`
Therefore, Spring Batch provides listeners, including the three most commonly used listeners: * `ChunkListener`
* `JobExecutionListener`
* StepListener
* ChunkListener
* JobExecutionListener
In the example shown in the following image, a Spring Batch job has been configured with a In the example shown in the following image, a Spring Batch job has been configured with a
`StepExecutionListener`. Thus, Spring `StepExecutionListener`. Thus, Spring Integration receives and processes any step before
Integration receives and processes any step before or after or after events. For example, the received `StepExecution` can be inspected by using a
events. For example, the received `Router`. Based on the results of that inspection, various things can occur (such as
`StepExecution` can be inspected by using a routing a message to a Mail Outbound Channel Adapter), so that an Email notification can
`Router`. Based on the results of that be sent out based on some condition.
inspection, various things can occur (such as routing a message
to a Mail Outbound Channel Adapter), so that an Email notification
can be sent out based on some condition.
.Handling Informational Messages .Handling Informational Messages
image::{batch-asciidoc}images/handling-informational-messages.png[Handling Informational Messages, scaledwidth="60%"] image::{batch-asciidoc}images/handling-informational-messages.png[Handling Informational Messages, scaledwidth="60%"]
The following two-part example shows how a listener is configured to send a The following two-part example shows how a listener is configured to send a
message to a `Gateway` for a message to a `Gateway` for a `StepExecution` events and log its output to a
`StepExecution` events and log its output to a
`logging-channel-adapter`. `logging-channel-adapter`.
First, create the notification integration beans: First, create the notification integration beans.
[role="xmlContent"]
The following example shows the how to create the notification integration beans in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -505,6 +496,9 @@ First, create the notification integration beans:
<int:logging-channel-adapter channel="stepExecutionsChannel"/> <int:logging-channel-adapter channel="stepExecutionsChannel"/>
---- ----
[role="javaContent"]
The following example shows the how to create the notification integration beans in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -522,11 +516,14 @@ public interface NotificationExecutionListener extends StepExecutionListener {}
---- ----
[role="javaContent"] [role="javaContent"]
NOTE: You will need to add the `@IntegrationComponentScan` annotation to your configuration. NOTE: You need to add the `@IntegrationComponentScan` annotation to your configuration.
[[message-gateway-entry-list]] [[message-gateway-entry-list]]
Second, modify your job to add a step-level listener: Second, modify your job to add a step-level listener.
[role="xmlContent"]
The following example shows the how to add a step-level listener in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -544,6 +541,9 @@ Second, modify your job to add a step-level listener:
</job> </job>
---- ----
[role="javaContent"]
The following example shows the how to add a step-level listener in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -561,25 +561,17 @@ public Job importPaymentsJob() {
==== Asynchronous Processors ==== Asynchronous Processors
Asynchronous Processors help you to scale the processing of Asynchronous Processors help you to scale the processing of items. In the asynchronous
items. In the asynchronous processor use case, an processor use case, an `AsyncItemProcessor` serves as a dispatcher, executing the logic of
`AsyncItemProcessor` serves as a dispatcher, the `ItemProcessor` for an item on a new thread. Once the item completes, the `Future` is
executing the logic of the `ItemProcessor` for an passed to the `AsynchItemWriter` to be written.
item on a new thread. Once the item completes, the `Future` is passed to
the `AsynchItemWriter` to be written.
Therefore, you can increase performance by using asynchronous item processing, basically
letting you implement _fork-join_ scenarios. The `AsyncItemWriter` gathers the results and
Therefore, you can increase performance by using asynchronous item
processing, basically allowing you to implement
__fork-join__ scenarios. The
`AsyncItemWriter` gathers the results and
writes back the chunk as soon as all the results become available. writes back the chunk as soon as all the results become available.
[role="xmlContent"]
The following example shows how to configuration the `AsyncItemProcessor` in XML:
The following example shows how to configuration the `AsyncItemProcessor`:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -595,6 +587,9 @@ The following example shows how to configuration the `AsyncItemProcessor`:
</bean> </bean>
---- ----
[role="xmlContent"]
The following example shows how to configuration the `AsyncItemProcessor` in XML:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -607,12 +602,11 @@ public AsyncItemProcessor processor(ItemProcessor itemProcessor, TaskExecutor ta
} }
---- ----
The `delegate` property refers The `delegate` property refers to your `ItemProcessor` bean, and the `taskExecutor`
to your `ItemProcessor` bean, and property refers to the `TaskExecutor` of your choice.
the `taskExecutor` property
refers to the `TaskExecutor` of your choice.
The following example shows how to configure the `AsyncItemWriter`: [role="xmlContent"]
The following example shows how to configure the `AsyncItemWriter` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -625,6 +619,9 @@ The following example shows how to configure the `AsyncItemWriter`:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to configure the `AsyncItemWriter` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -687,8 +684,9 @@ Spring Integration's rich collection of Channel Adapters (such as
JMS and AMQP), you can distribute chunks of a Batch job to JMS and AMQP), you can distribute chunks of a Batch job to
external systems for processing. external systems for processing.
A simple job with a step to be remotely chunked might have a [role="xmlContent"]
configuration similar to the following: A job with a step to be remotely chunked might have a configuration similar to the
following in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -703,6 +701,10 @@ configuration similar to the following:
</job> </job>
---- ----
[role="javaContent"]
A job with a step to be remotely chunked might have a configuration similar to the
following in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -717,15 +719,15 @@ public Job chunkJob() {
} }
---- ----
The `ItemReader` reference points to the bean you want The `ItemReader` reference points to the bean you want to use for reading data on the
to use for reading data on the manager. The `ItemWriter` reference master. The `ItemWriter` reference points to a special `ItemWriter` (called
points to a special `ItemWriter` `ChunkMessageChannelItemWriter`), as described above. The processor (if any) is left off
(called `ChunkMessageChannelItemWriter`), the master configuration, as it is configured on the slave. You should check any
as described above. The processor (if any) is left off the additional component properties, such as throttle limits and so on, when implementing
manager configuration, as it is configured on the worker. The your use case.
following configuration provides a basic manager setup. You
should check any additional component properties, such as [role="xmlContent"]
throttle limits and so on, when implementing your use case. The following XML configuration provides a basic master setup:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -758,6 +760,9 @@ throttle limits and so on, when implementing your use case.
channel="replies"/> channel="replies"/>
---- ----
[role="javaContent"]
The following Java configuration provides a basic master setup:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -824,8 +829,10 @@ referenced by our job step, uses the
`ChunkMessageChannelItemWriter` for writing chunks over the `ChunkMessageChannelItemWriter` for writing chunks over the
configured middleware. configured middleware.
Now we can move on to the worker configuration, as shown in the following example: Now we can move on to the slave configuration.
[role="xmlContent"]
The following example shows the slave configuration in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -867,6 +874,9 @@ Now we can move on to the worker configuration, as shown in the following exampl
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows the slave configuration in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1064,14 +1074,13 @@ remoting fabric or grid environment
Similar to remote chunking, JMS can be used as the "remoting Similar to remote chunking, JMS can be used as the "`remoting fabric`". In that case, use
fabric". In that case, use a `MessageChannelPartitionHandler` instance as the `PartitionHandler` implementation, a `MessageChannelPartitionHandler` instance as the `PartitionHandler` implementation,
as described above. as described earlier.
The following example
assumes an existing partitioned job and focuses on
the `MessageChannelPartitionHandler` and JMS
configuration:
[role="xmlContent"]
The following example assumes an existing partitioned job and focuses on the
`MessageChannelPartitionHandler` and JMS configuration in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1125,6 +1134,10 @@ configuration:
class="org.springframework.batch.integration.partition.BeanFactoryStepLocator" /> class="org.springframework.batch.integration.partition.BeanFactoryStepLocator" />
---- ----
[role="javaContent"]
The following example assumes an existing partitioned job and focuses on the
`MessageChannelPartitionHandler` and JMS configuration in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1233,7 +1246,12 @@ public IntegrationFlow outboundJmsStaging() {
} }
---- ----
You must also ensure that the partition `handler` attribute maps to the `partitionHandler` bean, as shown in the following example: You must also ensure that the partition `handler` attribute maps to the `partitionHandler`
bean.
[role="xmlContent"]
The following example maps the partition `handler` attribute to the `partitionHandler` in
XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1246,6 +1264,10 @@ You must also ensure that the partition `handler` attribute maps to the `partiti
</job> </job>
---- ----
[role="javaContent"]
The following example maps the partition `handler` attribute to the `partitionHandler` in
Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----

View File

@@ -56,8 +56,8 @@ Despite the relatively short list of required dependencies for a `Step`, it is a
extremely complex class that can potentially contain many collaborators. extremely complex class that can potentially contain many collaborators.
[role="xmlContent"] [role="xmlContent"]
In order to ease configuration, the Spring Batch namespace can be used, as shown in the In order to ease configuration, the Spring Batch XML namespace can be used, as shown in
following example: the following example:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -72,7 +72,7 @@ following example:
---- ----
[role="javaContent"] [role="javaContent"]
When using java configuration, the Spring Batch builders can be used, as shown in the When using Java configuration, the Spring Batch builders can be used, as shown in the
following example: following example:
.Java Configuration .Java Configuration
@@ -121,31 +121,32 @@ transactions during processing.
transactions during processing. transactions during processing.
[role="xmlContent"] [role="xmlContent"]
* `job-repository`: The `JobRepository` that periodically stores the `StepExecution` and * `job-repository`: The XML-specific name of the `JobRepository` that periodically stores
`ExecutionContext` during processing (just before committing). For an in-line <step/> the `StepExecution` and `ExecutionContext` during processing (just before committing). For
(one defined within a <job/>), it is an attribute on the <job/> element. For a standalone an in-line `<step/>` (one defined within a `<job/>`), it is an attribute on the `<job/>`
step, it is defined as an attribute of the <tasklet/>. element. For a standalone `<step/>`, it is defined as an attribute of the <tasklet/>.
[role="javaContent"] [role="javaContent"]
* `repository`: The `JobRepository` that periodically stores the `StepExecution` and * `repository`: The The Java-specific name of the `JobRepository` that periodically stores
`ExecutionContext` during processing (just before committing). the `StepExecution` and `ExecutionContext` during processing (just before committing).
[role="xmlContent"] [role="xmlContent"]
* `commit-interval`: The number of items to be processed before the transaction is * `commit-interval`: The XML-specific name of the the number of items to be processed
committed. before the transaction is committed.
[role="javaContent"] [role="javaContent"]
* `chunk`: Indicates that this is an item based step and the number of items to be * `chunk`: The XML-specific name of the dependency that indicates that this is an
processed before the transaction is committed. item-based step and the number of items to be processed before the transaction is
committed.
[role="xmlContent"] [role="xmlContent"]
It should be noted that `job-repository` defaults to `jobRepository` and It should be noted that `job-repository` defaults to `jobRepository` and
`transaction-manager` defaults to `transactionManger`. Also, the `ItemProcessor` is `transaction-manager` defaults to `transactionManager`. Also, the `ItemProcessor` is
optional, since the item could be directly passed from the reader to the writer. optional, since the item could be directly passed from the reader to the writer.
[role="javaContent"] [role="javaContent"]
It should be noted that `repository` defaults to `jobRepository` and `transactionManager` It should be noted that `repository` defaults to `jobRepository` and `transactionManager`
defaults to `transactionManger` (all provided through the infrastructure from defaults to `transactionManager` (all provided through the infrastructure from
`@EnableBatchProcessing`). Also, the `ItemProcessor` is optional, since the item could be `@EnableBatchProcessing`). Also, the `ItemProcessor` is optional, since the item could be
directly passed from the reader to the writer. directly passed from the reader to the writer.
endif::backend-html5[] endif::backend-html5[]
@@ -289,8 +290,11 @@ since beginning and committing a transaction is expensive. Ideally, it is prefer
process as many items as possible in each transaction, which is completely dependent upon 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. 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 For this reason, the number of items that are processed within a commit can be
configured. The following example shows a `step` whose `tasklet` has a `commit-interval` configured.
value of 10.
[role="xmlContent"]
The following example shows a `step` whose `tasklet` has a `commit-interval`
value of 10 as it would be defined in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -304,6 +308,10 @@ value of 10.
</job> </job>
---- ----
[role="javaContent"]
The following example shows a `step` whose `tasklet` has a `commit-interval`
value of 10 as it would be defined in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -345,8 +353,10 @@ be started. For example, a particular `Step` might need to be configured so that
runs once because it invalidates some resource that must be fixed manually before it can runs once because it invalidates some resource that must be fixed manually before it can
be run again. This is configurable on the step level, since different steps may have be run again. This is configurable on the step level, since different steps may have
different requirements. A `Step` that may only be executed once can exist as part of the different requirements. A `Step` that may only be executed once can exist as part of the
same `Job` as a `Step` that can be run infinitely. The following code fragment shows an same `Job` as a `Step` that can be run infinitely.
example of a start limit configuration:
[role="xmlContent"]
The following code fragment shows an example of a start limit configuration in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -358,6 +368,9 @@ example of a start limit configuration:
</step> </step>
---- ----
[role="javaContent"]
The following code fragment shows an example of a start limit configuration in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -372,8 +385,8 @@ public Step step1() {
} }
---- ----
The step above can be run only once. Attempting to run it again causes a The step shown in the preceding example can be run only once. Attempting to run it again
`StartLimitExceededException` to be thrown. Note that the default value for the causes a `StartLimitExceededException` to be thrown. Note that the default value for the
start-limit is `Integer.MAX_VALUE`. start-limit is `Integer.MAX_VALUE`.
[[allowStartIfComplete]] [[allowStartIfComplete]]
@@ -384,7 +397,10 @@ run, regardless of whether or not they were successful the first time. An exampl
be a validation step or a `Step` that cleans up resources before processing. During 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 normal processing of a restarted job, any step with a status of 'COMPLETED', meaning it
has already been completed successfully, is skipped. Setting `allow-start-if-complete` to has already been completed successfully, is skipped. Setting `allow-start-if-complete` to
"true" overrides this so that the step always runs, as shown in the following example: "true" overrides this so that the step always runs.
[role="xmlContent"]
The following code fragment shows how to define a restartable job in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -396,6 +412,9 @@ has already been completed successfully, is skipped. Setting `allow-start-if-com
</step> </step>
---- ----
[role="javaContent"]
The following code fragment shows how to define a restartable job in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -413,7 +432,9 @@ public Step step1() {
[[stepRestartExample]] [[stepRestartExample]]
===== `Step` Restart Configuration Example ===== `Step` Restart Configuration Example
The following example shows how to configure a job to have steps that can be restarted: [role="xmlContent"]
The following XML example shows how to configure a job to have steps that can be
restarted:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -440,6 +461,10 @@ The following example shows how to configure a job to have steps that can be res
</job> </job>
---- ----
[role="javaContent"]
The following Java example shows how to configure a job to have steps that can be
restarted:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -552,7 +577,8 @@ allow for skips. If a vendor is not loaded because it was formatted incorrectly
missing necessary information, then there probably are not issues. Usually, these bad missing necessary information, then there probably are not issues. Usually, these bad
records are logged as well, which is covered later when discussing listeners. records are logged as well, which is covered later when discussing listeners.
The following example shows an example of using a skip limit: [role="xmlContent"]
The following XML example shows an example of using a skip limit:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -569,6 +595,9 @@ The following example shows an example of using a skip limit:
</step> </step>
---- ----
[role="javaContent"]
The following Java example shows an example of using a skip limit:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -598,8 +627,10 @@ skip triggers the exception, not the tenth.
One problem with the preceding example is that any other exception besides a One problem with the preceding example is that any other exception besides a
`FlatFileParseException` causes the `Job` to fail. In certain scenarios, this may be the `FlatFileParseException` causes the `Job` to fail. In certain scenarios, this may be the
correct behavior. However, in other scenarios, it may be easier to identify which correct behavior. However, in other scenarios, it may be easier to identify which
exceptions should cause failure and skip everything else, as shown in the following exceptions should cause failure and skip everything else.
example:
[role="xmlContent"]
The following XML example shows an example excluding a particular exception:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -617,6 +648,9 @@ example:
</step> </step>
---- ----
[role="javaContent"]
The following Java example shows an example excluding a particular exception:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -648,11 +682,11 @@ ifdef::backend-html5[]
The order of the `<include/>` and `<exclude/>` elements does not matter. The order of the `<include/>` and `<exclude/>` elements does not matter.
[role="javaContent"] [role="javaContent"]
The order of the `skip` and `noSkip` calls does not matter. The order of the `skip` and `noSkip` method calls does not matter.
endif::backend-html5[] endif::backend-html5[]
ifdef::backend-pdf[] ifdef::backend-pdf[]
The order of specifying include vs exclude (by using either the XML tags or `skip` and The order of specifying include versus exclude (by using either the XML tags or `skip` and
`noSkip` method calls) does not matter. `noSkip` method calls) does not matter.
endif::backend-pdf[] endif::backend-pdf[]
@@ -664,8 +698,10 @@ not all exceptions are deterministic. If a `FlatFileParseException` is encounter
reading, it is always thrown for that record. Resetting the `ItemReader` does not help. reading, it is always thrown for that record. Resetting the `ItemReader` does not help.
However, for other exceptions, such as a `DeadlockLoserDataAccessException`, which However, for other exceptions, such as a `DeadlockLoserDataAccessException`, which
indicates that the current process has attempted to update a record that another process 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 holds a lock on. Waiting and trying again might result in success.
should be configured as follows:
[role="xmlContent"]
In XML, retry should be configured as follows:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -681,6 +717,9 @@ should be configured as follows:
</step> </step>
---- ----
[role="javaContent"]
In Java, retry should be configured as follows:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@Bean @Bean
@@ -709,7 +748,10 @@ described earlier, exceptions thrown from the `ItemReader` do not cause a rollba
However, there are many scenarios in which exceptions thrown from the `ItemWriter` should 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. 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 For this reason, the `Step` can be configured with a list of exceptions that should not
cause rollback, as shown in the following example: cause rollback.
[role="xmlContent"]
In XML, you can control rollback as follows:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -724,6 +766,9 @@ cause rollback, as shown in the following example:
</step> </step>
---- ----
[role="javaContent"]
In Java, you can control rollback as follows:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -748,7 +793,10 @@ from the reader. However, there are certain scenarios in which the reader is bui
top of a transactional resource, such as a JMS queue. In this case, since the queue is top of a transactional resource, such as a JMS queue. In this case, since the queue is
tied to the transaction that is rolled back, the messages that have been pulled from the tied to the transaction that is rolled back, the messages that have been pulled from the
queue are put back on. For this reason, the step can be configured to not buffer the queue are put back on. For this reason, the step can be configured to not buffer the
items, as shown in the following example: items.
[role="xmlContent"]
The following example shows how to create reader that does not buffer items in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -761,6 +809,9 @@ items, as shown in the following example:
</step> </step>
---- ----
[role="javaContent"]
The following example shows how to create reader that does not buffer items in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -782,8 +833,11 @@ Transaction attributes can be used to control the `isolation`, `propagation`, an
`timeout` settings. More information on setting transaction attributes can be found in `timeout` settings. More information on setting transaction attributes can be found in
the the
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction[Spring https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction[Spring
core documentation]. The following example sets the `isolation`, `propagation`, and core documentation].
`timeout` transaction attributes:
[role="xmlContent"]
The following example sets the `isolation`, `propagation`, and `timeout` transaction
attributes in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -798,6 +852,10 @@ core documentation]. The following example sets the `isolation`, `propagation`,
</step> </step>
---- ----
[role="javaContent"]
The following example sets the `isolation`, `propagation`, and `timeout` transaction
attributes in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -830,7 +888,10 @@ If the `ItemReader`, `ItemProcessor`, or `ItemWriter` itself implements the `Ite
interface, then these are registered automatically. Any other streams need to be interface, then these are registered automatically. Any other streams need to be
registered separately. This is often the case where indirect dependencies, such as registered separately. This is often the case where indirect dependencies, such as
delegates, are injected into the reader and writer. A stream can be registered on the delegates, are injected into the reader and writer. A stream can be registered on the
`Step` through the 'streams' element, as illustrated in the following example: `step` through the 'stream' element.
[role="xmlContent"]
The following example shows how to register a `stream` on a `step` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -846,6 +907,9 @@ delegates, are injected into the reader and writer. A stream can be registered o
</tasklet> </tasklet>
</step> </step>
[role="javaContent"]
The following example shows how to register a `stream` on a `step` in Java:
<beans:bean id="compositeWriter" <beans:bean id="compositeWriter"
class="org.springframework.batch.item.support.CompositeItemWriter"> class="org.springframework.batch.item.support.CompositeItemWriter">
<beans:property name="delegates"> <beans:property name="delegates">
@@ -910,8 +974,10 @@ itself since it is empty) can be applied to a step through the `listeners` eleme
The `listeners` element is valid inside a step, tasklet, or chunk declaration. It is The `listeners` element is valid inside a step, tasklet, or chunk declaration. It is
recommended that you declare the listeners at the level at which its function applies, recommended that you declare the listeners at the level at which its function applies,
or, if it is multi-featured (such as `StepExecutionListener` and `ItemReadListener`), or, if it is multi-featured (such as `StepExecutionListener` and `ItemReadListener`),
then declare it at the most granular level where it applies. The following example shows then declare it at the most granular level where it applies.
a listener applied at the chunk level:
[role="xmlContent"]
The following example shows a listener applied at the chunk level in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -926,6 +992,9 @@ a listener applied at the chunk level:
</step> </step>
---- ----
[role="xmlContent"]
The following example shows a listener applied at the chunk level in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1165,10 +1234,9 @@ statement.
ifdef::backend-html5[] ifdef::backend-html5[]
[role="xmlContent"] [role="xmlContent"]
To create a `TaskletStep`, the 'ref' attribute of the To create a `TaskletStep` in XML, the 'ref' attribute of the `<tasklet/>` element should
<tasklet/> element should reference a bean that defines a reference a bean that defines a `Tasklet` object. No `<chunk/>` element should be used
`Tasklet` object. No <chunk/> element should be within the `<tasklet/>`. The following example shows a simple tasklet:
used within the <tasklet/>. The following example shows a simple tasklet:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1178,9 +1246,9 @@ To create a `TaskletStep`, the 'ref' attribute of the
---- ----
[role="javaContent"] [role="javaContent"]
To create a `TaskletStep`, the bean passed to the `tasklet` method of the builder should To create a `TaskletStep` in Java, the bean passed to the `tasklet` method of the builder
implement the `Tasklet` interface. No call to `chunk` should be called when building a should implement the `Tasklet` interface. No call to `chunk` should be called when
`TaskletStep`. The following example shows a simple tasklet: building a `TaskletStep`. The following example shows a simple tasklet:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1233,8 +1301,10 @@ As with other adapters for the `ItemReader` and `ItemWriter` interfaces, the `Ta
interface contains an implementation that allows for adapting itself to any pre-existing 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 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 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, as shown in the class without having to write an adapter for the `Tasklet` interface.
following example:
[role="xmlContent"]
The following example shows how to define a `TaskletAdapter` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1247,6 +1317,9 @@ following example:
</bean> </bean>
---- ----
[role="JavaContent"]
The following example shows how to define a `TaskletAdapter` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1304,9 +1377,12 @@ public class FileDeletingTasklet implements Tasklet, InitializingBean {
} }
---- ----
The preceding `Tasklet` implementation deletes all files within a given directory. It The preceding `tasklet` implementation deletes all files within a given directory. It
should be noted that the `execute` method is called only once. All that is left is to should be noted that the `execute` method is called only once. All that is left is to
reference the `Tasklet` from the `Step`: reference the `tasklet` from the `step`.
[role="xmlContent"]
The following example shows how to reference the `tasklet` from the `step` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1328,6 +1404,9 @@ reference the `Tasklet` from the `Step`:
</beans:bean> </beans:bean>
---- ----
[role="javaContent"]
The following example shows how to reference the `tasklet` from the `step` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1373,8 +1452,10 @@ in the following image:
.Sequential Flow .Sequential Flow
image::{batch-asciidoc}images/sequential-flow.png[Sequential Flow, scaledwidth="60%"] image::{batch-asciidoc}images/sequential-flow.png[Sequential Flow, scaledwidth="60%"]
This can be achieved by using the 'next' attribute of the step element, as shown in the This can be achieved by using the 'next' in a `step`.
following example:
[role="xmlContent"]
The following example shows how to use the `next` attribute in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1386,6 +1467,9 @@ following example:
</job> </job>
---- ----
[role="javaContent"]
The following example shows how to use the `next()` method in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1406,11 +1490,9 @@ then the entire `Job` fails and 'step B' does not execute.
[role="xmlContent"] [role="xmlContent"]
[NOTE] [NOTE]
==== ====
With the Spring Batch namespace, the first step listed in the With the Spring Batch XML namespace, the first step listed in the configuration is
configuration is __always__ the first step _always_ the first step run by the `Job`. The order of the other step elements does not
run by the `Job`. The order of the other matter, but the first step must always appear first in the xml.
step elements does not matter, but the first step must always appear
first in the xml.
==== ====
[[conditionalFlow]] [[conditionalFlow]]
@@ -1418,18 +1500,20 @@ With the Spring Batch namespace, the first step listed in the
In the example above, there are only two possibilities: In the example above, there are only two possibilities:
. The `Step` is successful and the next `Step` should be executed. . The `step` is successful and the next `step` should be executed.
. The `Step` failed and, thus, the `Job` should fail. . The `step` failed and, thus, the `job` should fail.
In many cases, this may be sufficient. However, what about a scenario in which the 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? The failure of a `step` should trigger a different `step`, rather than causing failure? The
following image shows such a flow: following image shows such a flow:
.Conditional Flow .Conditional Flow
image::{batch-asciidoc}images/conditional-flow.png[Conditional Flow, scaledwidth="60%"] image::{batch-asciidoc}images/conditional-flow.png[Conditional Flow, scaledwidth="60%"]
[[nextElement]] [[nextElement]]
In order to handle more complex scenarios, the Spring Batch namespace allows transition [role="xmlContent"]
In order to handle more complex scenarios, the Spring Batch XML namespace allows transitions
elements to be defined within the step element. One such transition is the `next` elements to be defined within the step element. One such transition is the `next`
element. Like the `next` attribute, the `next` element tells the `Job` which `Step` to element. Like the `next` attribute, the `next` element tells the `Job` which `Step` to
execute next. However, unlike the attribute, any number of `next` elements are allowed on execute next. However, unlike the attribute, any number of `next` elements are allowed on
@@ -1438,6 +1522,7 @@ transition elements are used, then all of the behavior for the `Step` transition
defined explicitly. Note also that a single step cannot have both a `next` attribute and defined explicitly. Note also that a single step cannot have both a `next` attribute and
a `transition` element. a `transition` element.
[role="xmlContent"]
The `next` element specifies a pattern to match and the step to execute next, as shown in The `next` element specifies a pattern to match and the step to execute next, as shown in
the following example: the following example:
@@ -1454,6 +1539,12 @@ the following example:
</job> </job>
---- ----
[role="javaContent"]
The Java API offers a fluent set of methods that let you specify the flow and what to do
when a step fails. The following example shows how to specify one step (`stepA`) and then
proceed to either of two different steps (`stepB` and `stepC`), depending on whether
`stepA` succeeds:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1474,7 +1565,7 @@ pattern-matching scheme to match the `ExitStatus` that results from the executio
`Step`. `Step`.
[role="javaContent"] [role="javaContent"]
When using java configuration the `on` method uses a simple pattern-matching scheme to When using java configuration, the `on()` method uses a simple pattern-matching scheme to
match the `ExitStatus` that results from the execution of the `Step`. match the `ExitStatus` that results from the execution of the `Step`.
Only two special characters are allowed in the pattern: Only two special characters are allowed in the pattern:
@@ -1532,7 +1623,7 @@ More specifically, when using XML configuration, the 'next' element shown in the
preceding XML configuration example references the exit code of `ExitStatus`. preceding XML configuration example references the exit code of `ExitStatus`.
[role="xmlContent"] [role="xmlContent"]
When using Java configuration, the 'on' method shown in the preceding When using Java configuration, the 'on()' method shown in the preceding
Java configuration example references the exit code of `ExitStatus`. Java configuration example references the exit code of `ExitStatus`.
In English, it says: "go to stepB if the exit code is `FAILED` ". By default, the exit In English, it says: "go to stepB if the exit code is `FAILED` ". By default, the exit
@@ -1540,6 +1631,9 @@ code is always the same as the `BatchStatus` for the `Step`, which is why the en
works. However, what if the exit code needs to be different? A good example comes from works. However, what if the exit code needs to be different? A good example comes from
the skip sample job within the samples project: the skip sample job within the samples project:
[role="xmlContent"]
The following example shows how to work with a different exit code in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1550,6 +1644,9 @@ the skip sample job within the samples project:
</step> </step>
---- ----
[role="javaContent"]
The following example shows how to work with a different exit code in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1571,7 +1668,7 @@ public Job job() {
. The `Step` completed successfully but with an exit code of 'COMPLETED WITH SKIPS'. In . 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. this case, a different step should be run to handle the errors.
The above configuration works. However, something needs to change the exit code based on The preceding configuration works. However, something needs to change the exit code based on
the condition of the execution having skipped records, as shown in the following example: the condition of the execution having skipped records, as shown in the following example:
[source, java] [source, java]
@@ -1604,14 +1701,19 @@ While these statuses are determined for the `Step` by the code that is executed,
statuses for the `Job` are determined based on the configuration. statuses for the `Job` are determined based on the configuration.
So far, all of the job configurations discussed have had at least one final `Step` with So far, all of the job configurations discussed have had at least one final `Step` with
no transitions. For example, after the following step executes, the `Job` ends, as shown no transitions.
in the following example:
[role="xmlContent"]
In the following XML example, after the `step` executes, the `Job` ends:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
<step id="stepC" parent="s3"/> <step id="stepC" parent="s3"/>
---- ----
[role="javaContent"]
In the following Java example, after the `step` executes, the `Job` ends:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@Bean @Bean
@@ -1659,10 +1761,13 @@ also allows for an optional 'exitStatus' parameter that can be used to customize
`ExitStatus` of the `Job`. If no 'exitStatus' value is provided, then the `ExitStatus` is `ExitStatus` of the `Job`. If no 'exitStatus' value is provided, then the `ExitStatus` is
`COMPLETED` by default, to match the `BatchStatus`. `COMPLETED` by default, to match the `BatchStatus`.
In the following scenario, if `step2` fails, then the `Job` stops with a `BatchStatus` of Consider the following scenario: if `step2` fails, then the `Job` stops with a
`COMPLETED` and an `ExitStatus` of `COMPLETED` and `step3` does not run. Otherwise, `BatchStatus` of `COMPLETED` and an `ExitStatus` of `COMPLETED` and `step3` does not run.
execution moves to `step3`. Note that if `step2` fails, the `Job` is not restartable Otherwise, execution moves to `step3`. Note that if `step2` fails, the `Job` is not
(because the status is `COMPLETED`). restartable (because the status is `COMPLETED`).
[role="xmlContent"]
The following example shows the scenario in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1676,6 +1781,9 @@ execution moves to `step3`. Note that if `step2` fails, the `Job` is not restart
<step id="step3" parent="s3"> <step id="step3" parent="s3">
---- ----
[role="javaContent"]
The following example shows the scenario in Java:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@Bean @Bean
@@ -1703,10 +1811,13 @@ attribute that can be used to customize the `ExitStatus` of the `Job`. If no 'ex
attribute is given, then the `ExitStatus` is `FAILED` by default, to match the attribute is given, then the `ExitStatus` is `FAILED` by default, to match the
`BatchStatus`. `BatchStatus`.
In the following scenario, if `step2` fails, then the `Job` stops with a `BatchStatus` of Consider the following scenario if `step2` fails, then the `Job` stops with a
`FAILED` and an `ExitStatus` of `EARLY TERMINATION` and `step3` does not execute. `BatchStatus` of `FAILED` and an `ExitStatus` of `EARLY TERMINATION` and `step3` does not
Otherwise, execution moves to `step3`. Additionally, if `step2` fails and the `Job` is execute. Otherwise, execution moves to `step3`. Additionally, if `step2` fails and the
restarted, then execution begins again on `step2`. `Job` is restarted, then execution begins again on `step2`.
[role="xmlContent"]
The following example shows the scenario in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1721,6 +1832,9 @@ restarted, then execution begins again on `step2`.
<step id="step3" parent="s3"> <step id="step3" parent="s3">
---- ----
[role="javaContent"]
The following example shows the scenario in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -1743,15 +1857,18 @@ Configuring a job to stop at a particular step instructs a `Job` to stop with a
so that the operator can take some action before restarting the `Job`. so that the operator can take some action before restarting the `Job`.
[role="xmlContent"] [role="xmlContent"]
When using XML configuration 'stop' element requires a 'restart' attribute that specifies When using XML configuration, a 'stop' element requires a 'restart' attribute that specifies
the step where execution should pick up when the "Job is restarted". the step where execution should pick up when the Job is restarted.
[role="javaContent"] [role="javaContent"]
When using java configuration, the `stopAndRestart` method requires a 'restart' attribute When using Java configuration, the `stopAndRestart` method requires a 'restart' attribute
that specifies the step where execution should pick up when the "Job is restarted". that specifies the step where execution should pick up when the Job is restarted.
In the following scenario, if `step1` finishes with `COMPLETE`, then the job stops. Consider the following scenario: if `step1` finishes with `COMPLETE`, then the job then
Once it is restarted, execution begins on `step2`. stops. Once it is restarted, execution begins on `step2`.
[role="xmlContent"]
The following listing shows the scenario in XML:
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
---- ----
@@ -1762,6 +1879,9 @@ Once it is restarted, execution begins on `step2`.
<step id="step2" parent="s2"/> <step id="step2" parent="s2"/>
---- ----
[role="javaContent"]
The following example shows the scenario in Java:
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@Bean @Bean
@@ -1904,7 +2024,11 @@ public Job job(Flow flow1, Flow flow2) {
Part of the flow in a job can be externalized as a separate bean definition and then Part of the flow in a job can be externalized as a separate bean definition and then
re-used. There are two ways to do so. The first is to simply declare the flow as a re-used. There are two ways to do so. The first is to simply declare the flow as a
reference to one defined elsewhere, as shown in the following example: reference to one defined elsewhere.
[role="xmlContent"]
The following example shows how to declare a flow as a reference to a flow defined
elsewhere in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1920,7 +2044,11 @@ reference to one defined elsewhere, as shown in the following example:
</flow> </flow>
---- ----
.Java Configuration [role="javaContent"]
The following example shows how to declare a flow as a reference to a flow defined
elsewhere in Java:
.Java Confguration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@Bean @Bean
@@ -1952,7 +2080,7 @@ The other form of an externalized flow is to use a `JobStep`. A `JobStep` is sim
the flow specified. the flow specified.
[role="xmlContent"] [role="xmlContent"]
The following XML snippet shows an example of a `JobStep`: The following example hows an example of a `JobStep` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -1972,7 +2100,7 @@ The following XML snippet shows an example of a `JobStep`:
---- ----
[role="javaContent"] [role="javaContent"]
The following Java snippet shows an example of a `JobStep`: The following example shows an example of a `JobStep` in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -2023,7 +2151,10 @@ smaller modules and control the flow of jobs.
Both the XML and flat file examples shown earlier use the Spring `Resource` abstraction Both the XML and flat file examples shown earlier use the Spring `Resource` abstraction
to obtain a file. This works because `Resource` has a `getFile` method, which returns a 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 `java.io.File`. Both XML and flat file resources can be configured using standard Spring
constructs, as shown in the following example: constructs:
[role="xmlContent"]
The following example shows late binding in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2035,6 +2166,9 @@ constructs, as shown in the following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows late binding in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2055,7 +2189,7 @@ determined at runtime as a parameter to the job. This can be solved using '-D' p
to read a system property. to read a system property.
[role="xmlContent"] [role="xmlContent"]
The following XML snippet shows how to read a file name from a property: The following example shows how to read a file name from a property in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2067,7 +2201,7 @@ The following XML snippet shows how to read a file name from a property:
---- ----
[role="javaContent"] [role="javaContent"]
The following Java snippet shows how to read a file name from a property: The following shows how to read a file name from a property in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -2091,7 +2225,10 @@ already filters and does placeholder replacement on system properties.
Often, in a batch setting, it is preferable to parametrize the file name in the Often, in a batch setting, it is preferable to parametrize the file name in the
`JobParameters` of the job, instead of through system properties, and access them that `JobParameters` of the job, instead of through system properties, and access them that
way. To accomplish this, Spring Batch allows for the late binding of various `Job` and way. To accomplish this, Spring Batch allows for the late binding of various `Job` and
`Step` attributes, as shown in the following snippet: `Step` attributes.
[role="xmlContent"]
The following example shows how to parameterize a file name in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2102,6 +2239,9 @@ way. To accomplish this, Spring Batch allows for the late binding of various `Jo
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows how to parameterize a file name in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2116,7 +2256,10 @@ public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters['input.file
---- ----
Both the `JobExecution` and `StepExecution` level `ExecutionContext` can be accessed in Both the `JobExecution` and `StepExecution` level `ExecutionContext` can be accessed in
the same way, as shown in the following examples: the same way.
[role="xmlContent"]
The following example shows how to access the `ExecutionContext` in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2136,6 +2279,9 @@ the same way, as shown in the following examples:
</bean> </bean>
---- ----
[role="xmlContent"]
The following example shows how to access the `ExecutionContext` in XML:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2168,11 +2314,29 @@ Any bean that uses late-binding must be declared with scope="step". See
<<step.adoc#step-scope,Step Scope>> for more information. <<step.adoc#step-scope,Step Scope>> for more information.
==== ====
[NOTE]
====
If you are using Spring 3.0 (or above), the expressions in step-scoped beans are in the
Spring Expression Language, a powerful general purpose language with many interesting
features. To provide backward compatibility, if Spring Batch detects the presence of
older versions of Spring, it uses a native expression language that is less powerful and
that has slightly different parsing rules. The main difference is that the map keys in
the example above do not need to be quoted with Spring 2.5, but the quotes are mandatory
in Spring 3.0.
====
// TODO Where is that older language described? It'd be good to have a link to it here.
// Also, given that we're up to version 5 of Spring, should we still be talking about
// things from before version 3? (In other words, we should provide a link or drop the
// whole thing.)
[[step-scope]] [[step-scope]]
==== Step Scope ==== Step Scope
All of the late binding examples from above have a scope of "step" declared on the bean All of the late binding examples shown earlier have a scope of "`step`" declared on the
definition, as shown in the following example: bean definition.
[role="xmlContent"]
The following example shows an example of binding to step scope in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2183,6 +2347,9 @@ definition, as shown in the following example:
</bean> </bean>
---- ----
[role="javaContent"]
The following example shows an example of binding to step scope in Java:
.Java Configuration .Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -2228,8 +2395,10 @@ The following example includes the bean definition explicitly:
but is a Scope for the `Job` context, so that there is only one instance of such a bean but is a Scope for the `Job` context, so that there is only one instance of such a bean
per running job. Additionally, support is provided for late binding of references per running job. Additionally, support is provided for late binding of references
accessible from the `JobContext` using `#{..}` placeholders. Using this feature, bean accessible from the `JobContext` using `#{..}` placeholders. Using this feature, bean
properties can be pulled from the job or job execution context and the job parameters, as properties can be pulled from the job or job execution context and the job parameters.
shown in the following examples:
[role="xmlContent"]
The following example shows an example of binding to job scope in XML:
.XML Configuration .XML Configuration
[source, xml, role="xmlContent"] [source, xml, role="xmlContent"]
@@ -2247,7 +2416,10 @@ shown in the following examples:
</bean> </bean>
---- ----
.Java Configuration [role="javaContent"]
The following example shows an example of binding to job scope in Java:
.Java Configurtation
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@JobScope @JobScope

View File

@@ -10,36 +10,28 @@ ifndef::onlyonetoggle[]
include::toggle.adoc[] include::toggle.adoc[]
endif::onlyonetoggle[] endif::onlyonetoggle[]
As with other application styles, it is extremely important to As with other application styles, it is extremely important to unit test any code written
unit test any code written as part of a batch job. The Spring core as part of a batch job. The Spring core documentation covers how to unit and integration
documentation covers how to unit and integration test with Spring in great test with Spring in great detail, so it is not be repeated here. It is important, however,
detail, so it is not be repeated here. It is important, however, to think to think about how to 'end to end' test a batch job, which is what this chapter covers.
about how to 'end to end' test a batch job, which is what this chapter The spring-batch-test project includes classes that facilitate this end-to-end test
covers. The spring-batch-test project includes classes that approach.
facilitate this end-to-end test approach.
[[creatingUnitTestClass]] [[creatingUnitTestClass]]
=== Creating a Unit Test Class === Creating a Unit Test Class
In order for the unit test to run a batch job, the framework must In order for the unit test to run a batch job, the framework must load the job's
load the job's `ApplicationContext`. Two annotations are used to trigger ApplicationContext. Two annotations are used to trigger this behavior:
this behavior:
* `@RunWith(SpringJUnit4ClassRunner.class)`: Indicates that the class should use Spring's
JUnit facilities
* `@ContextConfiguration(...)`: Indicates which resources to configure the
`ApplicationContext` with.
* `@RunWith(SpringRunner.class)`: [role="javaContent"]
Indicates that the class should use Spring's JUnit facilities The following Java example shows the two annotations in use:
* `@ContextConfiguration(...)`:
Indicates which resources to configure the `ApplicationContext` with.
Starting from v4.1, it is also possible to inject Spring Batch test utilities
like the `JobLauncherTestUtils` and `JobRepositoryTestUtils` in the test context
using the `@SpringBatchTest` annotation.
The following example shows the annotations in use:
.Using Java Configuration .Using Java Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
@@ -50,6 +42,9 @@ The following example shows the annotations in use:
public class SkipSampleFunctionalTests { ... } public class SkipSampleFunctionalTests { ... }
---- ----
[role="xmlContent"]
The following XML example shows the two annotations in use:
.Using XML Configuration .Using XML Configuration
[source, java, role="xmlContent"] [source, java, role="xmlContent"]
---- ----
@@ -65,24 +60,22 @@ public class SkipSampleFunctionalTests { ... }
=== End-To-End Testing of Batch Jobs === End-To-End Testing of Batch Jobs
'End To End' testing can be defined as testing the complete run of a 'End To End' testing can be defined as testing the complete run of a batch job from
batch job from beginning to end. This allows for a test that sets up a beginning to end. This allows for a test that sets up a test condition, executes the job,
test condition, executes the job, and verifies the end result. and verifies the end result.
In the following example, the batch job reads from the database and Consider an example of a batch job that reads from the database and writes to a flat file.
writes to a flat file. The test method begins by setting up the database The test method begins by setting up the database with test data. It clears the CUSTOMER
with test data. It clears the CUSTOMER table and then inserts 10 new table and then inserts 10 new records. The test then launches the `Job` by using the
records. The test then launches the `Job` by using the `launchJob()` method. The `launchJob()` method is provided by the `JobLauncherTestUtils`
`launchJob()` method. The class. The `JobLauncherTestUtils` class also provides the `launchJob(JobParameters)`
`launchJob()` method is provided by the method, which allows the test to give particular parameters. The `launchJob()` method
`JobLauncherTestUtils` class. The `JobLauncherTestUtils` class also provides the returns the `JobExecution` object, which is useful for asserting particular information
`launchJob(JobParameters)` method, which about the `Job` run. In the following case, the test verifies that the `Job` ended with
allows the test to give particular parameters. The status "COMPLETED".
`launchJob()` method returns the
`JobExecution` object, which is useful for asserting [role="xmlContent"]
particular information about the `Job` run. In the The following listing shows the example in XML:
following case, the test verifies that the `Job` ended
with status "COMPLETED":
.XML Based Configuration .XML Based Configuration
[source, java, role="xmlContent"] [source, java, role="xmlContent"]
@@ -119,6 +112,9 @@ public class SkipSampleFunctionalTests {
} }
---- ----
[role="javaContent"]
The following listing shows the example in Java:
.Java Based Configuration .Java Based Configuration
[source, java, role="javaContent"] [source, java, role="javaContent"]
---- ----
@@ -158,16 +154,13 @@ public class SkipSampleFunctionalTests {
=== Testing Individual Steps === Testing Individual Steps
For complex batch jobs, test cases in the end-to-end testing For complex batch jobs, test cases in the end-to-end testing approach may become
approach may become unmanageable. In these cases, it may be more useful to unmanageable. It these cases, it may be more useful to have test cases to test individual
have test cases to test individual steps on their own. The steps on their own. The `AbstractJobTests` class contains a method called `launchStep`,
`JobLauncherTestUtils` class contains a method called which takes a step name and runs just that particular `Step`. This approach allows for
`launchStep`, which takes a step name and runs just more targeted tests letting the test set up data for only that step and to validate its
that particular `Step`. This approach allows for more results directly. The following example shows how to use the `launchStep` method to load a
targeted tests letting the test set up data for only that step and `Step` by name:
to validate its results directly. The following example shows how to use the
`launchStep` method to load a `Step` by name:
[source, java] [source, java]
---- ----
@@ -178,17 +171,14 @@ JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");
=== Testing Step-Scoped Components === Testing Step-Scoped Components
Often, the components that are configured for your steps at runtime Often, the components that are configured for your steps at runtime use step scope and
use step scope and late binding to inject context from the step or job late binding to inject context from the step or job execution. These are tricky to test as
execution. These are tricky to test as standalone components, unless you standalone components, unless you have a way to set the context as if they were in a step
have a way to set the context as if they were in a step execution. That is execution. That is the goal of two components in Spring Batch:
the goal of two components in Spring Batch: `StepScopeTestExecutionListener` and `StepScopeTestUtils`.
`StepScopeTestExecutionListener` and
`StepScopeTestUtils`.
The listener is declared at the class level, and its job is to
create a step execution context for each test method, as shown in the following example:
The listener is declared at the class level, and its job is to create a step execution
context for each test method, as shown in the following example:
[source, java] [source, java]
---- ----
@@ -218,56 +208,18 @@ public class StepScopeTestExecutionListenerIntegrationTests {
} }
---- ----
There are two `TestExecutionListeners`. One There are two `TestExecutionListeners`. One is the regular Spring Test framework, which
is the regular Spring Test framework, which handles dependency injection handles dependency injection from the configured application context to inject the reader.
from the configured application context to inject the reader. The The other is the Spring Batch `StepScopeTestExecutionListener`. It works by looking for a
other is the Spring Batch factory method in the test case for a `StepExecution`, using that as the context for the
`StepScopeTestExecutionListener`. It works by looking test method, as if that execution were active in a `Step` at runtime. The factory method
for a factory method in the test case for a is detected by its signature (it must return a `StepExecution`). If a factory method is
`StepExecution`, using that as the context for not provided, then a default `StepExecution` is created.
the test method, as if that execution were active in a `Step` at runtime. The
factory method is detected by its signature (it must return a
`StepExecution`). If a factory method is not provided,
then a default `StepExecution` is created.
Starting from v4.1, the `StepScopeTestExecutionListener` and
`JobScopeTestExecutionListener` are imported as test execution listeners
if the test class is annotated with `@SpringBatchTest`. The preceding test
example can be configured as follows:
[source, java]
----
@SpringBatchTest
@RunWith(SpringRunner.class)
@ContextConfiguration
public class StepScopeTestExecutionListenerIntegrationTests {
// This component is defined step-scoped, so it cannot be injected unless
// a step is active...
@Autowired
private ItemReader<String> reader;
public StepExecution getStepExecution() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
execution.getExecutionContext().putString("input.data", "foo,bar,spam");
return execution;
}
@Test
public void testReader() {
// The reader is initialized and bound to the input data
assertNotNull(reader.read());
}
}
----
The listener approach is convenient if you want the duration of the
step scope to be the execution of the test method. For a more flexible
but more invasive approach, you can use the
`StepScopeTestUtils`. The following example counts the
number of items available in the reader shown in the previous example:
The listener approach is convenient if you want the duration of the step scope to be the
execution of the test method. For a more flexible but more invasive approach, you can use
the `StepScopeTestUtils`. The following example counts the number of items available in
the reader shown in the previous example:
[source, java] [source, java]
---- ----
@@ -287,21 +239,15 @@ int count = StepScopeTestUtils.doInStepScope(stepExecution,
[[validatingOutputFiles]] [[validatingOutputFiles]]
=== Validating Output Files === Validating Output Files
When a batch job writes to the database, it is easy to query the When a batch job writes to the database, it is easy to query the database to verify that
database to verify that the output is as expected. However, if the batch the output is as expected. However, if the batch job writes to a file, it is equally
job writes to a file, it is equally important that the output be verified. important that the output be verified. Spring Batch provides a class called `AssertFile`
Spring Batch provides a class called `AssertFile` to to facilitate the verification of output files. The method called `assertFileEquals` takes
facilitate the verification of output files. The method called two `File` objects (or two `Resource` objects) and asserts, line by line, that the two
`assertFileEquals` takes two files have the same content. Therefore, it is possible to create a file with the expected
`File` objects (or two output and to compare it to the actual result, as shown in the following example:
`Resource` objects) and asserts, line by line, that
the two files have the same content. Therefore, it is possible to create a
file with the expected output and to compare it to the actual
result, as shown in the following example:
[source, java] [source, java]
---- ----
@@ -317,11 +263,9 @@ AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
=== Mocking Domain Objects === Mocking Domain Objects
Another common issue encountered while writing unit and integration Another common issue encountered while writing unit and integration tests for Spring Batch
tests for Spring Batch components is how to mock domain objects. A good components is how to mock domain objects. A good example is a `StepExecutionListener`, as
example is a `StepExecutionListener`, as illustrated illustrated in the following code snippet:
in the following code snippet:
[source, java] [source, java]
---- ----
@@ -336,13 +280,11 @@ public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSuppo
} }
---- ----
The preceding listener example is provided by the framework and checks a The preceding listener example is provided by the framework and checks a `StepExecution`
`StepExecution` for an empty read count, thus for an empty read count, thus signifying that no work was done. While this example is
signifying that no work was done. While this example is fairly simple, it fairly simple, it serves to illustrate the types of problems that may be encountered when
serves to illustrate the types of problems that may be encountered when attempting to unit test classes that implement interfaces requiring Spring Batch domain
attempting to unit test classes that implement interfaces requiring Spring objects. Consider the following unit test for the listener's in the preceding example:
Batch domain objects. Consider the following unit test for the listener's in the preceding example:
[source, java] [source, java]
---- ----
@@ -362,18 +304,13 @@ public void noWork() {
} }
---- ----
Because the Spring Batch domain model follows good object-oriented Because the Spring Batch domain model follows good object-oriented principles, the
principles, the `StepExecution` requires a `StepExecution` requires a `JobExecution`, which requires a `JobInstance` and
`JobExecution`, which requires a `JobParameters`, to create a valid `StepExecution`. While this is good in a solid domain
`JobInstance` and model, it does make creating stub objects for unit testing verbose. To address this issue,
`JobParameters`, to create a valid the Spring Batch test module includes a factory for creating domain objects:
`StepExecution`. While this is good in a solid domain `MetaDataInstanceFactory`. Given this factory, the unit test can be updated to be more
model, it does make creating stub objects for unit testing verbose. To concise, as shown in the following example:
address this issue, the Spring Batch test module includes a factory for
creating domain objects: `MetaDataInstanceFactory`.
Given this factory, the unit test can be updated to be more
concise, as shown in the following example:
[source, java] [source, java]
---- ----
@@ -391,7 +328,6 @@ public void testAfterStep() {
} }
---- ----
The preceding method for creating a simple The preceding method for creating a simple `StepExecution` is just one convenience method
`StepExecution` is just one convenience method available within the factory. A full method listing can be found in its
available within the factory. A full method listing can be found in its link:$$http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/MetaDataInstanceFactory.html$$[Javadoc].
link:$$https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/test/MetaDataInstanceFactory.html$$[Javadoc].

View File

@@ -8,7 +8,7 @@ ifdef::backend-html5[]
<div class="docToggle-button"> <div class="docToggle-button">
<input id="xmlButton" type="radio" name="docToggle" value="XML"><label for="xmlButton">XML</label> <input id="xmlButton" type="radio" name="docToggle" value="XML"><label for="xmlButton">XML</label>
<input id="javaButton" type="radio" name="docToggle" value="Java" checked><label for="javaButton">Java</label> <input id="javaButton" type="radio" name="docToggle" value="Java" checked><label for="javaButton">Java</label>
<!-- <input id="bothButton" type="radio" name="docToggle" value="Both" checked><label for="bothButton">Both</label> --> <input id="bothButton" type="radio" name="docToggle" value="Both" checked><label for="bothButton">Both</label>
</div> </div>
</div> </div>
+++ +++