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:
committed by
Mahmoud Ben Hassine
parent
ae6cd59155
commit
efdce562b0
@@ -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"]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"]
|
||||||
----
|
----
|
||||||
|
|||||||
62
spring-batch-docs/asciidoc/jsfiles/DocumentToggle.js
Normal file
62
spring-batch-docs/asciidoc/jsfiles/DocumentToggle.js
Normal 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 });
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
@@ -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<Object> 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<Object> 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.
|
||||||
|
|||||||
@@ -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"]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -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"]
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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].
|
|
||||||
|
|||||||
@@ -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>
|
||||||
+++
|
+++
|
||||||
|
|||||||
Reference in New Issue
Block a user