BATCH-2224 Ensure Syntax Highlighting for Program Listings
* Polish Docbook
This commit is contained in:
committed by
Michael Minella
parent
f6b83167ac
commit
dff3f0b80b
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="patterns">
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="patterns"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Common Batch Patterns</title>
|
||||
|
||||
<para>Some batch jobs can be assembled purely from off-the-shelf components
|
||||
@@ -36,9 +36,9 @@
|
||||
write. The below code snippets illustrate a listener that logs both read
|
||||
and write failures:</para>
|
||||
|
||||
<programlisting>public class ItemFailureLoggerListener extends ItemListenerSupport {
|
||||
<programlisting language="java">public class ItemFailureLoggerListener extends ItemListenerSupport {
|
||||
|
||||
private static Log logger = LogFactory.getLog("item.error");
|
||||
private static Log logger = LogFactory.getLog("item.error");
|
||||
|
||||
public void onReadError(Exception ex) {
|
||||
logger.error("Encountered error on read", e);
|
||||
@@ -53,7 +53,7 @@
|
||||
<para>Having implemented this listener it must be registered with the
|
||||
step:</para>
|
||||
|
||||
<programlisting><step id="simpleStep">
|
||||
<programlisting language="xml"><step id="simpleStep">
|
||||
...
|
||||
<listeners>
|
||||
<listener>
|
||||
@@ -85,8 +85,8 @@
|
||||
indefinitely or skipped). For example, a custom exception type could be
|
||||
used, as in the example below:</para>
|
||||
|
||||
<programlisting>public class PoisonPillItemWriter implements ItemWriter<T> {
|
||||
|
||||
<programlisting language="java">public class PoisonPillItemWriter implements ItemWriter<T> {
|
||||
|
||||
public void write(T item) throws Exception {
|
||||
if (isPoisonPill(item)) {
|
||||
throw new PoisonPillException("Posion pill detected: " + item);
|
||||
@@ -98,12 +98,12 @@
|
||||
<para>Another simple way to stop a step from executing is to simply return
|
||||
<code>null</code> from the <classname>ItemReader</classname>:</para>
|
||||
|
||||
<programlisting>public class EarlyCompletionItemReader implements ItemReader<T> {
|
||||
<programlisting language="java">public class EarlyCompletionItemReader implements ItemReader<T> {
|
||||
|
||||
private ItemReader<T> delegate;
|
||||
|
||||
public void setDelegate(ItemReader<T> delegate) { ... }
|
||||
|
||||
|
||||
public T read() throws Exception {
|
||||
T item = delegate.read();
|
||||
if (isEndItem(item)) {
|
||||
@@ -121,9 +121,9 @@
|
||||
injected into the <classname>Step</classname> through the
|
||||
<classname>SimpleStepFactoryBean</classname>:</para>
|
||||
|
||||
<programlisting><step id="simpleStep">
|
||||
<programlisting language="xml"><step id="simpleStep">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" commit-interval="10"
|
||||
<chunk reader="reader" writer="writer" commit-interval="10"
|
||||
<emphasis role="bold">chunk-completion-policy="completionPolicy"</emphasis>/>
|
||||
</tasklet>
|
||||
</step>
|
||||
@@ -139,9 +139,9 @@
|
||||
the <classname>Step</classname>. Here is an example of a listener that
|
||||
sets the flag:</para>
|
||||
|
||||
<programlisting>public class CustomItemWriter extends ItemListenerSupport implements StepListener {
|
||||
<programlisting language="java">public class CustomItemWriter extends ItemListenerSupport implements StepListener {
|
||||
|
||||
private StepExecution stepExecution;
|
||||
private StepExecution stepExecution;
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
this.stepExecution = stepExecution;
|
||||
@@ -173,7 +173,7 @@
|
||||
<classname>FlatFileHeaderCallback</classname>) are optional properties of
|
||||
the <classname>FlatFileItemWriter</classname>:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.spr...FlatFileItemWriter">
|
||||
<programlisting language="java"><bean id="itemWriter" class="org.spr...FlatFileItemWriter">
|
||||
<property name="resource" ref="outputResource" />
|
||||
<property name="lineAggregator" ref="lineAggregator"/>
|
||||
<emphasis role="bold"><property name="headerCallback" ref="headerCallback" /></emphasis>
|
||||
@@ -183,7 +183,7 @@
|
||||
<para>The footer callback interface is very simple. It has just one method
|
||||
that is called when the footer must be written:</para>
|
||||
|
||||
<programlisting>public interface FlatFileFooterCallback {
|
||||
<programlisting language="java">public interface FlatFileFooterCallback {
|
||||
|
||||
void writeFooter(Writer writer) throws IOException;
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
<classname>Trade</classname>s is placed in a footer, then the following
|
||||
<classname>ItemWriter</classname> implementation can be used:</para>
|
||||
|
||||
<programlisting>public class TradeItemWriter implements ItemWriter<Trade>,
|
||||
<programlisting language="java">public class TradeItemWriter implements ItemWriter<Trade>,
|
||||
FlatFileFooterCallback {
|
||||
|
||||
private ItemWriter<Trade> delegate;
|
||||
@@ -250,7 +250,7 @@
|
||||
<classname>FlatFileItemWriter</classname> as the
|
||||
<code>footerCallback</code>:</para>
|
||||
|
||||
<programlisting><bean id="tradeItemWriter" class="..TradeItemWriter">
|
||||
<programlisting language="xml"><bean id="tradeItemWriter" class="..TradeItemWriter">
|
||||
<property name="delegate" ref="flatFileItemWriter" />
|
||||
</bean>
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
with the methods <methodname>open</methodname> and
|
||||
<methodname>update</methodname>:</para>
|
||||
|
||||
<programlisting>public void open(ExecutionContext executionContext) {
|
||||
<programlisting language="java">public void open(ExecutionContext executionContext) {
|
||||
if (executionContext.containsKey("total.amount") {
|
||||
totalAmount = (BigDecimal) executionContext.get("total.amount");
|
||||
}
|
||||
@@ -378,7 +378,7 @@ FOT;2;2;267.34</programlisting>
|
||||
<classname>ItemReader</classname> should be implemented as a wrapper for
|
||||
the <classname>FlatFileItemReader</classname>.</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.spr...MultiLineTradeItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...MultiLineTradeItemReader">
|
||||
<property name="delegate">
|
||||
<bean class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="data/iosample/input/multiLine.txt" />
|
||||
@@ -403,7 +403,7 @@ FOT;2;2;267.34</programlisting>
|
||||
deliver a <classname>FieldSet</classname> for each line back to the
|
||||
wrapping <classname>ItemReader</classname>.</para>
|
||||
|
||||
<programlisting><bean id="orderFileTokenizer" class="org.spr...PatternMatchingCompositeLineTokenizer">
|
||||
<programlisting language="xml"><bean id="orderFileTokenizer" class="org.spr...PatternMatchingCompositeLineTokenizer">
|
||||
<property name="tokenizers">
|
||||
<map>
|
||||
<entry key="HEA*" value-ref="headerRecordTokenizer" />
|
||||
@@ -422,7 +422,7 @@ FOT;2;2;267.34</programlisting>
|
||||
<classname>ItemProcessor</classname> and
|
||||
<classname>ItemWriter</classname>.</para>
|
||||
|
||||
<programlisting>private FlatFileItemReader<FieldSet> delegate;
|
||||
<programlisting language="java">private FlatFileItemReader<FieldSet> delegate;
|
||||
|
||||
public Trade read() throws Exception {
|
||||
Trade t = null;
|
||||
@@ -466,7 +466,7 @@ public Trade read() throws Exception {
|
||||
<classname>Tasklet</classname> implementation for calling system
|
||||
commands:</para>
|
||||
|
||||
<programlisting><bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet">
|
||||
<programlisting language="xml"><bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet">
|
||||
<property name="command" value="echo hello" />
|
||||
<!-- 5 second timeout for the command to complete -->
|
||||
<property name="timeout" value="5000" />
|
||||
@@ -490,7 +490,7 @@ public Trade read() throws Exception {
|
||||
a common use case, a listener is provided with just this
|
||||
functionality:</para>
|
||||
|
||||
<programlisting>public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
|
||||
<programlisting language="java">public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
if (stepExecution.getReadCount() == 0) {
|
||||
@@ -536,7 +536,7 @@ public Trade read() throws Exception {
|
||||
during <classname>Step</classname> execution and if the
|
||||
<classname>Step</classname> fails, that data will be lost.</para>
|
||||
|
||||
<programlisting>public class SavingItemWriter implements ItemWriter<Object> {
|
||||
<programlisting language="java">public class SavingItemWriter implements ItemWriter<Object> {
|
||||
private StepExecution stepExecution;
|
||||
|
||||
public void write(List<? extends Object> items) throws Exception {
|
||||
@@ -564,7 +564,7 @@ public Trade read() throws Exception {
|
||||
listeners, it must be registered on the
|
||||
<classname>Step</classname>.</para>
|
||||
|
||||
<programlisting><job id="job1">
|
||||
<programlisting language="xml"><job id="job1">
|
||||
<step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="savingWriter" commit-interval="10"/>
|
||||
@@ -586,7 +586,7 @@ public Trade read() throws Exception {
|
||||
<para>Finally, the saved values must be retrieved from the
|
||||
<classname>Job</classname> <classname>ExeuctionContext</classname>:</para>
|
||||
|
||||
<programlisting>public class RetrievingItemWriter implements ItemWriter<Object> {
|
||||
<programlisting language="java">public class RetrievingItemWriter implements ItemWriter<Object> {
|
||||
private Object someObject;
|
||||
|
||||
public void write(List<? extends Object> items) throws Exception {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="domain" xreflabel="Batch Domain Language">
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="domain"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xreflabel="Batch Domain Language">
|
||||
<title>The Domain Language of Batch</title>
|
||||
|
||||
<para>To any experienced batch architect, the overall concepts of batch
|
||||
@@ -114,7 +113,7 @@
|
||||
namespace abstracts away the need to instantiate it directly. Instead, the
|
||||
<code><job></code> tag can be used:</para>
|
||||
|
||||
<programlisting><job id="footballJob">
|
||||
<programlisting language="xml"><job id="footballJob">
|
||||
<step id="playerload" next="gameLoad"/>
|
||||
<step id="gameLoad" next="playerSummarization"/>
|
||||
<step id="playerSummarization"/>
|
||||
@@ -748,7 +747,7 @@
|
||||
is to put the current number of lines read into the context, and the
|
||||
framework will do the rest:</para>
|
||||
|
||||
<programlisting>executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());</programlisting>
|
||||
<programlisting language="java">executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());</programlisting>
|
||||
|
||||
<para>Using the EndOfDay example from the Job Stereotypes section as an
|
||||
example, assume there's one step: 'loadData', that loads a file into the
|
||||
@@ -895,7 +894,7 @@
|
||||
<classname>ItemReader</classname> is opened, it can check to see if it has
|
||||
any stored state in the context, and initialize itself from there:</para>
|
||||
|
||||
<programlisting>if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
|
||||
<programlisting language="java">if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
|
||||
log.debug("Initializing for restart. Restart data is: " + executionContext);
|
||||
|
||||
long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));
|
||||
@@ -946,7 +945,7 @@
|
||||
<classname>StepExecution</classname>. For example, consider the following
|
||||
code snippet:</para>
|
||||
|
||||
<programlisting>ExecutionContext ecStep = stepExecution.getExecutionContext();
|
||||
<programlisting language="java">ExecutionContext ecStep = stepExecution.getExecutionContext();
|
||||
ExecutionContext ecJob = jobExecution.getExecutionContext();
|
||||
//ecStep does not equal ecJob</programlisting>
|
||||
|
||||
@@ -971,7 +970,7 @@ ExecutionContext ecJob = jobExecution.getExecutionContext();
|
||||
<classname>JobExecution</classname> implementations are persisted by
|
||||
passing them to the repository:</para>
|
||||
|
||||
<programlisting><job-repository id="jobRepository"/></programlisting>
|
||||
<programlisting language="xml"><job-repository id="jobRepository"/></programlisting>
|
||||
</section>
|
||||
|
||||
<section id="domainJobLauncher">
|
||||
@@ -981,7 +980,7 @@ ExecutionContext ecJob = jobExecution.getExecutionContext();
|
||||
launching a <classname>Job</classname> with a given set of
|
||||
<classname>JobParameters</classname>:</para>
|
||||
|
||||
<programlisting>public interface JobLauncher {
|
||||
<programlisting language="java">public interface JobLauncher {
|
||||
|
||||
public JobExecution run(Job job, JobParameters jobParameters)
|
||||
throws JobExecutionAlreadyRunningException, JobRestartException;
|
||||
@@ -1041,7 +1040,7 @@ ExecutionContext ecJob = jobExecution.getExecutionContext();
|
||||
bean definition, a namespace has been provided for ease of
|
||||
configuration:</para>
|
||||
|
||||
<programlisting><beans:beans xmlns="<emphasis role="bold">http://www.springframework.org/schema/batch</emphasis>"
|
||||
<programlisting language="xml"><beans:beans xmlns="<emphasis role="bold">http://www.springframework.org/schema/batch</emphasis>"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<book xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="spring-batch-reference"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<bookinfo>
|
||||
<title>Spring Batch - Reference Documentation</title>
|
||||
<productname>Spring Batch</productname>
|
||||
<releaseinfo>Spring Batch 3.0.0.M3</releaseinfo>
|
||||
<releaseinfo>${version}</releaseinfo>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
@@ -46,8 +46,23 @@
|
||||
<firstname>Chris</firstname>
|
||||
<surname>Schaefer</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<author>
|
||||
<firstname>Gunnar</firstname>
|
||||
<surname>Hillert</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright>
|
||||
<year>2009</year>
|
||||
<year>2010</year>
|
||||
<year>2011</year>
|
||||
<year>2012</year>
|
||||
<year>2013</year>
|
||||
<year>2014</year>
|
||||
<holder>
|
||||
GoPivotal, Inc. All Rights Reserved.
|
||||
</holder>
|
||||
</copyright>
|
||||
<legalnotice>
|
||||
<para>Copies of this document may be made for your own use and for
|
||||
distribution to others, provided that you do not charge any fee for such
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
required dependencies: a name, <classname>JobRepository</classname> , and
|
||||
a list of <classname>Step</classname>s.</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="footballJob">
|
||||
<programlisting language="xml"><![CDATA[<job id="footballJob">
|
||||
<step id="playerload" parent="s1" next="gameLoad"/>
|
||||
<step id="gameLoad" parent="s2" next="playerSummarization"/>
|
||||
<step id="playerSummarization" parent="s3"/>
|
||||
@@ -50,7 +50,7 @@
|
||||
defaults to referencing a repository with an id of 'jobRepository', which
|
||||
is a sensible default. However, this can be overridden explicitly:</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="footballJob" ]]><emphasis role="bold">job-repository="specialRepository"</emphasis><![CDATA[>
|
||||
<programlisting language="xml"><![CDATA[<job id="footballJob" ]]><emphasis role="bold">job-repository="specialRepository"</emphasis><![CDATA[>
|
||||
<step id="playerload" parent="s1" next="gameLoad"/>
|
||||
<step id="gameLoad" parent="s3" next="playerSummarization"/>
|
||||
<step id="playerSummarization" parent="s3"/>
|
||||
@@ -78,7 +78,7 @@
|
||||
be run as part of a new <classname>JobInstance</classname>, then the
|
||||
restartable property may be set to 'false':</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="footballJob" ]]><emphasis role="bold">restartable="false"</emphasis><![CDATA[>
|
||||
<programlisting language="xml"><![CDATA[<job id="footballJob" ]]><emphasis role="bold">restartable="false"</emphasis><![CDATA[>
|
||||
...
|
||||
</job>]]></programlisting>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
restartable will cause a <classname>JobRestartException</classname> to
|
||||
be thrown:</para>
|
||||
|
||||
<programlisting><![CDATA[Job job = new SimpleJob();
|
||||
<programlisting language="java"><![CDATA[Job job = new SimpleJob();
|
||||
job.setRestartable(false);
|
||||
|
||||
JobParameters jobParameters = new JobParameters();
|
||||
@@ -118,7 +118,7 @@ catch (JobRestartException e) {
|
||||
<classname>SimpleJob</classname> allows for this by calling a
|
||||
<classname>JobListener</classname> at the appropriate time:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface JobExecutionListener {
|
||||
<programlisting language="java"><![CDATA[public interface JobExecutionListener {
|
||||
|
||||
void beforeJob(JobExecution jobExecution);
|
||||
|
||||
@@ -130,7 +130,7 @@ catch (JobRestartException e) {
|
||||
<classname>SimpleJob</classname> via the listeners element on the
|
||||
job:</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="footballJob">
|
||||
<programlisting language="xml"><![CDATA[<job id="footballJob">
|
||||
<step id="playerload" parent="s1" next="gameLoad"/>
|
||||
<step id="gameLoad" parent="s2" next="playerSummarization"/>
|
||||
<step id="playerSummarization" parent="s3"/>
|
||||
@@ -144,7 +144,7 @@ catch (JobRestartException e) {
|
||||
<classname>Job</classname>. If success or failure needs to be determined
|
||||
it can be obtained from the <classname>JobExecution</classname>:</para>
|
||||
|
||||
<programlisting><![CDATA[public void afterJob(JobExecution jobExecution){
|
||||
<programlisting language="java"><![CDATA[public void afterJob(JobExecution jobExecution){
|
||||
if( jobExecution.getStatus() == BatchStatus.COMPLETED ){
|
||||
//job success
|
||||
}
|
||||
@@ -184,7 +184,7 @@ catch (JobRestartException e) {
|
||||
<classname>Job</classname> with two listeners and one
|
||||
<classname>Step</classname>, "step1".</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="baseJob" abstract="true">
|
||||
<programlisting language="java"><![CDATA[<job id="baseJob" abstract="true">
|
||||
<listeners>
|
||||
<listener ref="listenerOne"/>
|
||||
<listeners>
|
||||
@@ -216,7 +216,7 @@ catch (JobRestartException e) {
|
||||
of a validator is supported through the XML namespace through a child
|
||||
element of the job, e.g:</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="job1" parent="baseJob3">
|
||||
<programlisting language="xml"><![CDATA[<job id="job1" parent="baseJob3">
|
||||
<step id="step1" parent="standaloneStep"/>
|
||||
<validator ref="paremetersValidator"/>
|
||||
</job>]]></programlisting>
|
||||
@@ -279,7 +279,7 @@ catch (JobRestartException e) {
|
||||
to configure a job. Below is an example of a two step job configured via the
|
||||
<classname>JobBuilderFactory</classname> and the <classname>StepBuilderFactory</classname>.</para>
|
||||
|
||||
<programlisting>@Configuration
|
||||
<programlisting language="java">@Configuration
|
||||
@EnableBatchProcessing
|
||||
@Import(DataSourceCnfiguration.class)
|
||||
public class AppConfig {
|
||||
@@ -335,17 +335,12 @@ public class AppConfig {
|
||||
collaborators. However, there are still a few configuration options
|
||||
available:</para>
|
||||
|
||||
|
||||
|
||||
<programlisting><![CDATA[<job-repository id="jobRepository"
|
||||
<programlisting language="xml"><![CDATA[<job-repository id="jobRepository"
|
||||
data-source="dataSource"
|
||||
transaction-manager="transactionManager"
|
||||
isolation-level-for-create="SERIALIZABLE"
|
||||
table-prefix="BATCH_"
|
||||
max-varchar-length="1000"
|
||||
/>]]></programlisting>
|
||||
|
||||
|
||||
max-varchar-length="1000"/>]]></programlisting>
|
||||
|
||||
<para>None of the configuration options listed above are required except
|
||||
the id. If they are not set, the defaults shown above will be used. They
|
||||
@@ -376,7 +371,7 @@ public class AppConfig {
|
||||
platform supports it. However, this can be overridden:</para>
|
||||
|
||||
<para>
|
||||
<programlisting><![CDATA[<job-repository id="jobRepository"
|
||||
<programlisting language="xml"><![CDATA[<job-repository id="jobRepository"
|
||||
]]><emphasis role="bold">isolation-level-for-create="REPEATABLE_READ"</emphasis><![CDATA[ />]]></programlisting>
|
||||
</para>
|
||||
|
||||
@@ -385,7 +380,7 @@ public class AppConfig {
|
||||
using AOP:</para>
|
||||
|
||||
<para>
|
||||
<programlisting><![CDATA[<aop:config>
|
||||
<programlisting language="xml"><![CDATA[<aop:config>
|
||||
<aop:advisor
|
||||
pointcut="execution(* org.springframework.batch.core..*Repository+.*(..))"/>
|
||||
<advice-ref="txAdvice" />
|
||||
@@ -418,7 +413,7 @@ public class AppConfig {
|
||||
meta data tables is needed within the same schema, then the table prefix
|
||||
will need to be changed:</para>
|
||||
|
||||
<programlisting><![CDATA[<job-repository id="jobRepository"
|
||||
<programlisting language="xml"><![CDATA[<job-repository id="jobRepository"
|
||||
]]><emphasis role="bold">table-prefix="SYSTEM.TEST_"</emphasis><![CDATA[ />]]></programlisting>
|
||||
|
||||
<para>Given the above changes, every query to the meta data tables will
|
||||
@@ -443,7 +438,7 @@ public class AppConfig {
|
||||
this reason, Spring batch provides an in-memory Map version of the job
|
||||
repository:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobRepository"
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobRepository"
|
||||
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
|
||||
<property name="transactionManager" ref="transactionManager"/>
|
||||
</bean>]]></programlisting>
|
||||
@@ -474,7 +469,7 @@ public class AppConfig {
|
||||
shortcut and use it to set the database type to the closest
|
||||
match:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
|
||||
<property name="databaseType" value="db2"/>
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>]]></programlisting>
|
||||
@@ -505,7 +500,7 @@ public class AppConfig {
|
||||
a <classname>JobRepository</classname>, in order to obtain an
|
||||
execution:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobLauncher"
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobLauncher"
|
||||
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>]]></programlisting>
|
||||
@@ -552,7 +547,7 @@ public class AppConfig {
|
||||
configured to allow for this scenario by configuring a
|
||||
<classname>TaskExecutor</classname>:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobLauncher"
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobLauncher"
|
||||
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
<property name="taskExecutor">
|
||||
@@ -668,7 +663,7 @@ public class AppConfig {
|
||||
will be converted into <classname>JobParameters</classname>. An
|
||||
example of the XML configuration is below:</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="endOfDay">
|
||||
<programlisting language="xml"><![CDATA[<job id="endOfDay">
|
||||
<step id="step1" parent="simpleStep" />
|
||||
</job>
|
||||
|
||||
@@ -712,7 +707,7 @@ public class AppConfig {
|
||||
to a number using the <classname>ExitCodeMapper</classname>
|
||||
interface:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface ExitCodeMapper {
|
||||
<programlisting language="java"><![CDATA[public interface ExitCodeMapper {
|
||||
|
||||
public int intValue(String exitCode);
|
||||
|
||||
@@ -775,7 +770,7 @@ public class AppConfig {
|
||||
is required when handling an <classname>HttpRequest</classname>. An
|
||||
example is below:</para>
|
||||
|
||||
<programlisting><![CDATA[@Controller
|
||||
<programlisting language="java"><![CDATA[@Controller
|
||||
public class JobLauncherController {
|
||||
|
||||
@Autowired
|
||||
@@ -845,7 +840,7 @@ public class JobLauncherController {
|
||||
query the repository for existing executions. This functionality is
|
||||
provided by the <classname>JobExplorer</classname> interface:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface JobExplorer {
|
||||
<programlisting language="java"><![CDATA[public interface JobExplorer {
|
||||
|
||||
List<JobInstance> getJobInstances(String jobName, int start, int count);
|
||||
|
||||
@@ -866,7 +861,7 @@ public class JobLauncherController {
|
||||
<classname>JobRepository</classname>, it can be easily configured via a
|
||||
factory bean:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
|
||||
p:dataSource-ref="dataSource" />]]></programlisting>
|
||||
|
||||
<para><link linkend="repositoryTablePrefix">Earlier in this
|
||||
@@ -876,7 +871,7 @@ public class JobLauncherController {
|
||||
<classname>JobExplorer</classname> is working with the same tables, it
|
||||
too needs the ability to set a prefix:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
|
||||
p:dataSource-ref="dataSource" ]]><emphasis role="bold">p:tablePrefix="BATCH_" </emphasis><![CDATA[/>]]></programlisting>
|
||||
</section>
|
||||
|
||||
@@ -893,7 +888,7 @@ public class JobLauncherController {
|
||||
the framework and this is based on a simple map from job name to job
|
||||
instance. It is configured simply like this:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobRegistry" class="org.spr...MapJobRegistry" />]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobRegistry" class="org.spr...MapJobRegistry" />]]></programlisting>
|
||||
|
||||
<para>There are two ways to populate a JobRegistry automatically: using
|
||||
a bean post processor and using a registrar lifecycle component. These
|
||||
@@ -905,7 +900,7 @@ public class JobLauncherController {
|
||||
<para>This is a bean post-processor that can register all jobs as they
|
||||
are created:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
|
||||
<property name="jobRegistry" ref="jobRegistry"/>
|
||||
</bean>]]></programlisting>
|
||||
|
||||
@@ -932,7 +927,7 @@ public class JobLauncherController {
|
||||
integrate jobs contributed from separate modules of an
|
||||
application.</para>
|
||||
|
||||
<programlisting><![CDATA[<bean class="org.spr...AutomaticJobRegistrar">
|
||||
<programlisting language="xml"><![CDATA[<bean class="org.spr...AutomaticJobRegistrar">
|
||||
<property name="applicationContextFactories">
|
||||
<bean class="org.spr...ClasspathXmlApplicationContextsFactoryBean">
|
||||
<property name="resources" value="classpath*:/config/job*.xml" />
|
||||
@@ -984,7 +979,7 @@ public class JobLauncherController {
|
||||
provides for these types of operations via the
|
||||
<classname>JobOperator</classname> interface:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface JobOperator {
|
||||
<programlisting language="java"><![CDATA[public interface JobOperator {
|
||||
|
||||
List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException;
|
||||
|
||||
@@ -1026,7 +1021,7 @@ public class JobLauncherController {
|
||||
implementation of <classname>JobOperator</classname>,
|
||||
<classname>SimpleJobOperator</classname>, has many dependencies:</para>
|
||||
|
||||
<programlisting><![CDATA[<bean id="jobOperator" class="org.spr...SimpleJobOperator">
|
||||
<programlisting language="xml"><![CDATA[<bean id="jobOperator" class="org.spr...SimpleJobOperator">
|
||||
<property name="jobExplorer">
|
||||
<bean class="org.spr...JobExplorerFactoryBean">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
@@ -1064,7 +1059,7 @@ public class JobLauncherController {
|
||||
<classname>Job</classname> to force the <classname>Job</classname> to a
|
||||
new instance:</para>
|
||||
|
||||
<programlisting><![CDATA[public interface JobParametersIncrementer {
|
||||
<programlisting language="java"><![CDATA[public interface JobParametersIncrementer {
|
||||
|
||||
JobParameters getNext(JobParameters parameters);
|
||||
|
||||
@@ -1084,7 +1079,7 @@ public class JobLauncherController {
|
||||
numerical values that help to identify the <classname>Job</classname>,
|
||||
as shown below:</para>
|
||||
|
||||
<programlisting><![CDATA[public class SampleIncrementer implements JobParametersIncrementer {
|
||||
<programlisting language="java"><![CDATA[public class SampleIncrementer implements JobParametersIncrementer {
|
||||
|
||||
public JobParameters getNext(JobParameters parameters) {
|
||||
if (parameters==null || parameters.isEmpty()) {
|
||||
@@ -1104,7 +1099,7 @@ public class JobLauncherController {
|
||||
be associated with <classname>Job</classname> via the 'incrementer'
|
||||
attribute in the namespace:</para>
|
||||
|
||||
<programlisting><![CDATA[<job id="footballJob" ]]><emphasis role="bold">incrementer="sampleIncrementer"</emphasis><![CDATA[>
|
||||
<programlisting language="xml"><![CDATA[<job id="footballJob" ]]><emphasis role="bold">incrementer="sampleIncrementer"</emphasis><![CDATA[>
|
||||
...
|
||||
</job>]]></programlisting>
|
||||
</section>
|
||||
@@ -1116,8 +1111,8 @@ public class JobLauncherController {
|
||||
<classname>JobOperator</classname> is gracefully stopping a
|
||||
<classname>Job:</classname></para>
|
||||
|
||||
<programlisting><![CDATA[Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
|
||||
jobOperator.stop(executions.iterator().next()); ]]></programlisting>
|
||||
<programlisting language="java"><![CDATA[Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
|
||||
jobOperator.stop(executions.iterator().next());]]></programlisting>
|
||||
|
||||
<para>The shutdown is not immediate, since there is no way to force
|
||||
immediate shutdown, especially if the execution is currently in
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<para>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.</para>
|
||||
<para><programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
@@ -73,7 +73,7 @@
|
||||
</step>
|
||||
</job>
|
||||
</beans>
|
||||
</programlisting></para>
|
||||
</programlisting>
|
||||
|
||||
<para>The assembly of Spring contexts (imports, etc) works with JSR-352 jobs just as it would with any other
|
||||
Spring based application. The only difference with a JSR-352 based job is that the entry point for the
|
||||
@@ -82,13 +82,13 @@
|
||||
<para>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.</para>
|
||||
<para><programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
|
||||
<step id="step1" >
|
||||
<batchlet ref="io.spring.FooBatchlet" />
|
||||
</step>
|
||||
</job>
|
||||
</programlisting></para>
|
||||
</programlisting>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -101,12 +101,11 @@
|
||||
<para>JSR-352 allows for properties to be defined at the Job, Step and batch artifact level by way of
|
||||
configuration in the JSL. Batch properties are configured at each level in the following way:</para>
|
||||
|
||||
<para>
|
||||
<programlisting><properties>
|
||||
<programlisting language="xml"><properties>
|
||||
<property name="propertyName1" value="propertyValue1"/>
|
||||
<property name="propertyName2" value="propertyValue2"/>
|
||||
</properties></programlisting>
|
||||
|
||||
<para>
|
||||
Properties may be configured on any batch artifact.</para>
|
||||
</section>
|
||||
<section id="jsrBatchPropertyAnnotation">
|
||||
@@ -118,15 +117,15 @@
|
||||
conversion is up to the implementing developer to perform.</para>
|
||||
|
||||
<para>An <classname>javax.batch.api.chunk.ItemReader</classname> artifact could be configured with a
|
||||
properties block such as the one described above and accessed as such:
|
||||
<programlisting>public class MyItemReader extends AbstractItemReader {
|
||||
properties block such as the one described above and accessed as such:</para>
|
||||
<programlisting language="java">public class MyItemReader extends AbstractItemReader {
|
||||
@Inject
|
||||
@BatchProperty
|
||||
private String propertyName1;
|
||||
|
||||
...
|
||||
}</programlisting>
|
||||
|
||||
<para>
|
||||
The value of the field "propertyName1" will be "propertyValue1"</para>
|
||||
</section>
|
||||
<section id="jsrPropertySubstitution">
|
||||
@@ -153,8 +152,8 @@
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<programlisting>#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}</programlisting>
|
||||
<para>
|
||||
<programlisting>#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}</programlisting>
|
||||
The left hand side of the assignment is the expected value, the right hand side is the default value. In
|
||||
this example, the result will resolve to a value of the system property file.separator as
|
||||
#{jobParameters['unresolving.prop']} is assumed to not be resolvable. If neither expressions can be
|
||||
@@ -190,7 +189,8 @@
|
||||
<classname>ItemReader</classname>. To configure a step this way, specify the
|
||||
<classname>item-count</classname> (which defaults to 10) and optionally configure the
|
||||
<classname>checkpoint-policy</classname> as item (this is the default).
|
||||
<programlisting>...
|
||||
</para>
|
||||
<programlisting language="xml">...
|
||||
<step id="step1">
|
||||
<chunk checkpoint-policy="item" item-count="3">
|
||||
<reader ref="fooReader"/>
|
||||
@@ -199,6 +199,7 @@
|
||||
</chunk>
|
||||
</step>
|
||||
...</programlisting>
|
||||
<para>
|
||||
If item based checkpointing is chosen, an additional attribute <classname>time-limit</classname> is
|
||||
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 then
|
||||
@@ -216,7 +217,8 @@
|
||||
implementation of <classname>CheckpointAlgorithm</classname>, configure your step with the custom
|
||||
<classname>checkpoint-policy</classname> as shown below where fooCheckpointer refers to an
|
||||
implementation of <classname>CheckpointAlgorithm</classname>.
|
||||
<programlisting>...
|
||||
</para>
|
||||
<programlisting language="xml">...
|
||||
<step id="step1">
|
||||
<chunk checkpoint-policy="custom">
|
||||
<checkpoint-algorithm ref="fooCheckpointer"/>
|
||||
@@ -226,7 +228,6 @@
|
||||
</chunk>
|
||||
</step>
|
||||
...</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -239,10 +240,10 @@
|
||||
implementation is loaded via the <classname>javax.batch.runtime.BatchRuntime</classname>. Launching a
|
||||
JSR-352 based batch job is implemented as follows:</para>
|
||||
|
||||
<para><programlisting>
|
||||
<programlisting language="java">
|
||||
JobOperator jobOperator = BatchRuntime.getJobOperator();
|
||||
long jobExecutionId = jobOperator.start("fooJob", new Properties());
|
||||
</programlisting></para>
|
||||
</programlisting>
|
||||
|
||||
<para>The above code does the following:</para>
|
||||
|
||||
@@ -291,9 +292,9 @@ long jobExecutionId = jobOperator.start("fooJob", new Properties());
|
||||
<para>To obtain a reference to the <classname>JobContext</classname> or <classname>StepContext</classname>
|
||||
within the current scope, simply use the <classname>@Inject</classname> annotation:</para>
|
||||
|
||||
<para><programlisting>@Inject
|
||||
<programlisting language="java">@Inject
|
||||
JobContext jobContext;
|
||||
</programlisting></para>
|
||||
</programlisting>
|
||||
|
||||
<note>
|
||||
<title>@Autowire for JSR-352 contexts</title>
|
||||
@@ -371,7 +372,7 @@ JobContext jobContext;
|
||||
<section id="jsrPartitioning">
|
||||
<title>Partitioning</title>
|
||||
<para>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
|
||||
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:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<para><classname>ItemReader</classname> is a basic interface for generic
|
||||
input operations:</para>
|
||||
|
||||
<programlisting>public interface ItemReader<T> {
|
||||
<programlisting language="java">public interface ItemReader<T> {
|
||||
|
||||
T read() throws Exception, UnexpectedInputException, ParseException;
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<para>As with <classname>ItemReader</classname>,
|
||||
<classname>ItemWriter</classname> is a fairly generic interface:</para>
|
||||
|
||||
<programlisting>public interface ItemWriter<T> {
|
||||
<programlisting language="java">public interface ItemWriter<T> {
|
||||
|
||||
void write(List<? extends T> items) throws Exception;
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
that contains another <classname>ItemReader</classname>. For
|
||||
example:</para>
|
||||
|
||||
<programlisting>public class CompositeItemWriter<T> implements ItemWriter<T> {
|
||||
<programlisting language="java">public class CompositeItemWriter<T> implements ItemWriter<T> {
|
||||
|
||||
ItemWriter<T> itemWriter;
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
For this scenario, Spring Batch provides the
|
||||
<classname>ItemProcessor</classname> interface:</para>
|
||||
|
||||
<programlisting>public interface ItemProcessor<I, O> {
|
||||
<programlisting language="java">public interface ItemProcessor<I, O> {
|
||||
|
||||
O process(I item) throws Exception;
|
||||
}</programlisting>
|
||||
@@ -160,7 +160,7 @@
|
||||
written out. An <classname>ItemProcessor</classname> can be written that
|
||||
performs the conversion:</para>
|
||||
|
||||
<programlisting>public class Foo {}
|
||||
<programlisting language="java">public class Foo {}
|
||||
|
||||
public class Bar {
|
||||
public Bar(Foo foo) {}
|
||||
@@ -191,7 +191,7 @@ public class BarWriter implements ItemWriter<Bar>{
|
||||
provided. The <classname>FooProcessor</classname> can then be injected
|
||||
into a <classname>Step</classname>:</para>
|
||||
|
||||
<programlisting><job id="ioSampleJob">
|
||||
<programlisting language="xml"><job id="ioSampleJob">
|
||||
<step name="step1">
|
||||
<tasklet>
|
||||
<chunk reader="fooReader" processor="fooProcessor" writer="barWriter"
|
||||
@@ -211,7 +211,7 @@ public class BarWriter implements ItemWriter<Bar>{
|
||||
transformed to <classname>Bar</classname>, which will be transformed to
|
||||
<classname>Foobar</classname> and written out:</para>
|
||||
|
||||
<programlisting>public class Foo {}
|
||||
<programlisting language="java">public class Foo {}
|
||||
|
||||
public class Bar {
|
||||
public Bar(Foo foo) {}
|
||||
@@ -244,7 +244,7 @@ public class FoobarWriter implements ItemWriter<FooBar>{
|
||||
<classname>BarProcessor</classname> can be 'chained' together to give
|
||||
the resultant <classname>Foobar</classname>:</para>
|
||||
|
||||
<programlisting>CompositeItemProcessor<Foo,Foobar> compositeProcessor =
|
||||
<programlisting language="java">CompositeItemProcessor<Foo,Foobar> compositeProcessor =
|
||||
new CompositeItemProcessor<Foo,Foobar>();
|
||||
List itemProcessors = new ArrayList();
|
||||
itemProcessors.add(new FooTransformer());
|
||||
@@ -254,7 +254,7 @@ compositeProcessor.setDelegates(itemProcessors);</programlisting>
|
||||
<para>Just as with the previous example, the composite processor can be
|
||||
configured into the <classname>Step</classname>:</para>
|
||||
|
||||
<programlisting><job id="ioSampleJob">
|
||||
<programlisting language="xml"><job id="ioSampleJob">
|
||||
<step name="step1">
|
||||
<tasklet>
|
||||
<chunk reader="fooReader" processor="compositeProcessor" writer="foobarWriter"
|
||||
@@ -323,7 +323,7 @@ compositeProcessor.setDelegates(itemProcessors);</programlisting>
|
||||
writers need to be opened, closed, and require a mechanism for persisting
|
||||
state:</para>
|
||||
|
||||
<programlisting>public interface ItemStream {
|
||||
<programlisting language="java">public interface ItemStream {
|
||||
|
||||
void open(ExecutionContext executionContext) throws ItemStreamException;
|
||||
|
||||
@@ -378,7 +378,7 @@ compositeProcessor.setDelegates(itemProcessors);</programlisting>
|
||||
are not known to the <classname>Step</classname>, they need to be injected
|
||||
as listeners or streams (or both if appropriate):</para>
|
||||
|
||||
<programlisting><job id="ioSampleJob">
|
||||
<programlisting language="xml"><job id="ioSampleJob">
|
||||
<step name="step1">
|
||||
<tasklet>
|
||||
<chunk reader="fooReader" processor="fooProcessor" writer="compositeItemWriter"
|
||||
@@ -427,7 +427,7 @@ compositeProcessor.setDelegates(itemProcessors);</programlisting>
|
||||
fields so that the fields may be accessed either by index or name as
|
||||
patterned after <classname>ResultSet</classname>:</para>
|
||||
|
||||
<programlisting>String[] tokens = new String[]{"foo", "1", "true"};
|
||||
<programlisting language="java">String[] tokens = new String[]{"foo", "1", "true"};
|
||||
FieldSet fs = new DefaultFieldSet(tokens);
|
||||
String name = fs.readString(0);
|
||||
int value = fs.readInt(1);
|
||||
@@ -461,7 +461,9 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
Framework, Chapter 5.Resources</citetitle></ulink>. Therefore, this
|
||||
guide will not go into the details of creating
|
||||
<classname>Resource</classname> objects. However, a simple example of a
|
||||
file system resource can be found below: <programlisting>Resource resource = new FileSystemResource("resources/trades.csv");</programlisting></para>
|
||||
file system resource can be found below:
|
||||
</para>
|
||||
<programlisting language="java">Resource resource = new FileSystemResource("resources/trades.csv");</programlisting>
|
||||
|
||||
<para>In complex batch environments the directory structures are often
|
||||
managed by the EAI infrastructure where drop zones for external
|
||||
@@ -580,11 +582,13 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
level construct such as <classname>ResultSet</classname> and returns
|
||||
an <classname>Object</classname>, flat file processing requires the
|
||||
same construct to convert a <classname>String</classname> line into an
|
||||
<classname>Object</classname>:<programlisting>public interface LineMapper<T> {
|
||||
<classname>Object</classname>:
|
||||
</para>
|
||||
<programlisting language="java">public interface LineMapper<T> {
|
||||
|
||||
T mapLine(String line, int lineNumber) throws Exception;
|
||||
|
||||
}</programlisting></para>
|
||||
}</programlisting>
|
||||
|
||||
<para>The basic contract is that, given the current line and the line
|
||||
number with which it is associated, the mapper should return a
|
||||
@@ -610,7 +614,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
<classname>FieldSet</classname>. In Spring Batch, this interface is
|
||||
the <classname>LineTokenizer</classname>:</para>
|
||||
|
||||
<programlisting>public interface LineTokenizer {
|
||||
<programlisting language="java">public interface LineTokenizer {
|
||||
|
||||
FieldSet tokenize(String line);
|
||||
|
||||
@@ -659,7 +663,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
<classname>LineTokenizer</classname> to translate a line of data from
|
||||
a resource into an object of the desired type:</para>
|
||||
|
||||
<programlisting>public interface FieldSetMapper<T> {
|
||||
<programlisting language="java">public interface FieldSetMapper<T> {
|
||||
|
||||
T mapFieldSet(FieldSet fieldSet);
|
||||
|
||||
@@ -706,7 +710,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
<classname>DefaultLineMapper</classname> represents the behavior most
|
||||
users will need:</para>
|
||||
|
||||
<programlisting>public class DefaultLineMapper<T> implements LineMapper<T>, InitializingBean {
|
||||
<programlisting language="java">public class DefaultLineMapper<T> implements LineMapper<T>, InitializingBean {
|
||||
|
||||
private LineTokenizer tokenizer;
|
||||
|
||||
@@ -737,16 +741,20 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
|
||||
<para>The following example will be used to illustrate this using an
|
||||
actual domain scenario. This particular batch job reads in football
|
||||
players from the following file:<programlisting>ID,lastName,firstName,position,birthYear,debutYear
|
||||
players from the following file:
|
||||
</para>
|
||||
<programlisting>ID,lastName,firstName,position,birthYear,debutYear
|
||||
"AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996",
|
||||
"AbduRa00,Abdullah,Rabih,rb,1975,1999",
|
||||
"AberWa00,Abercrombie,Walter,rb,1959,1982",
|
||||
"AbraDa00,Abramowicz,Danny,wr,1945,1967",
|
||||
"AdamBo00,Adams,Bob,te,1946,1969",
|
||||
"AdamCh00,Adams,Charlie,wr,1979,2003" </programlisting></para>
|
||||
"AdamCh00,Adams,Charlie,wr,1979,2003" </programlisting>
|
||||
|
||||
<para>The contents of this file will be mapped to the following
|
||||
<classname>Player</classname> domain object: <programlisting>public class Player implements Serializable {
|
||||
<classname>Player</classname> domain object:
|
||||
</para>
|
||||
<programlisting language="java">public class Player implements Serializable {
|
||||
|
||||
private String ID;
|
||||
private String lastName;
|
||||
@@ -763,15 +771,14 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
}
|
||||
|
||||
// setters and getters...
|
||||
}
|
||||
</programlisting></para>
|
||||
}</programlisting>
|
||||
|
||||
<para>In order to map a <classname>FieldSet</classname> into a
|
||||
<classname>Player</classname> object, a
|
||||
<classname>FieldSetMapper</classname> that returns players needs to be
|
||||
defined:</para>
|
||||
|
||||
<para><programlisting>protected static class PlayerFieldSetMapper implements FieldSetMapper<Player> {
|
||||
<programlisting language="java">protected static class PlayerFieldSetMapper implements FieldSetMapper<Player> {
|
||||
public Player mapFieldSet(FieldSet fieldSet) {
|
||||
Player player = new Player();
|
||||
|
||||
@@ -784,13 +791,13 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
|
||||
|
||||
return player;
|
||||
}
|
||||
} </programlisting></para>
|
||||
}</programlisting>
|
||||
|
||||
<para>The file can then be read by correctly constructing a
|
||||
<classname>FlatFileItemReader</classname> and calling
|
||||
<methodname>read</methodname>:</para>
|
||||
|
||||
<programlisting>FlatFileItemReader<Player> itemReader = new FlatFileItemReader<Player>();
|
||||
<programlisting language="java">FlatFileItemReader<Player> itemReader = new FlatFileItemReader<Player>();
|
||||
itemReader.setResource(new FileSystemResource("resources/players.csv"));
|
||||
//DelimitedLineTokenizer defaults to comma as its delimiter
|
||||
LineMapper<Player> lineMapper = new DefaultLineMapper<Player>();
|
||||
@@ -817,12 +824,12 @@ Player player = itemReader.read();</programlisting>
|
||||
readability of the mapping function. First, the column names of all
|
||||
fields in the flat file are injected into the tokenizer:</para>
|
||||
|
||||
<para><programlisting>tokenizer.setNames(new String[] {"ID", "lastName","firstName","position","birthYear","debutYear"}); </programlisting></para>
|
||||
<programlisting language="java">tokenizer.setNames(new String[] {"ID", "lastName","firstName","position","birthYear","debutYear"}); </programlisting>
|
||||
|
||||
<para>A <classname>FieldSetMapper</classname> can use this information
|
||||
as follows:</para>
|
||||
|
||||
<para><programlisting>public class PlayerMapper implements FieldSetMapper<Player> {
|
||||
<programlisting language="java">public class PlayerMapper implements FieldSetMapper<Player> {
|
||||
public Player mapFieldSet(FieldSet fs) {
|
||||
|
||||
if(fs == null){
|
||||
@@ -839,7 +846,7 @@ Player player = itemReader.read();</programlisting>
|
||||
|
||||
return player;
|
||||
}
|
||||
}</programlisting></para>
|
||||
}</programlisting>
|
||||
</section>
|
||||
|
||||
<section id="beanWrapperFieldSetMapper">
|
||||
@@ -855,7 +862,7 @@ Player player = itemReader.read();</programlisting>
|
||||
<classname>BeanWrapperFieldSetMapper</classname> configuration looks
|
||||
like the following:</para>
|
||||
|
||||
<programlisting><bean id="fieldSetMapper"
|
||||
<programlisting language="xml"><bean id="fieldSetMapper"
|
||||
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
|
||||
<property name="prototypeBeanName" value="player" />
|
||||
</bean>
|
||||
@@ -916,7 +923,7 @@ UK21341EAH4521535.11customer5</programlisting>
|
||||
<classname>FixedLengthLineTokenizer</classname>, each of these lengths
|
||||
must be provided in the form of ranges:</para>
|
||||
|
||||
<programlisting><bean id="fixedLengthLineTokenizer"
|
||||
<programlisting language="xml"><bean id="fixedLengthLineTokenizer"
|
||||
class="org.springframework.batch.io.file.transform.FixedLengthTokenizer">
|
||||
<property name="names" value="ISIN,Quantity,Price,Customer" />
|
||||
<property name="columns" value="1-12, 13-15, 16-20, 21-29" />
|
||||
@@ -969,7 +976,7 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
|
||||
<classname>LineTokenizer</classname>s and patterns to
|
||||
<classname>FieldSetMapper</classname>s to be configured:</para>
|
||||
|
||||
<programlisting><bean id="orderFileLineMapper"
|
||||
<programlisting language="xml"><bean id="orderFileLineMapper"
|
||||
class="org.spr...PatternMatchingCompositeLineMapper">
|
||||
<property name="tokenizers">
|
||||
<map>
|
||||
@@ -1006,7 +1013,7 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
|
||||
("*") can serve as a default by matching any line not matched by any
|
||||
other pattern.</para>
|
||||
|
||||
<programlisting><entry key="*" value-ref="defaultLineTokenizer" /></programlisting>
|
||||
<programlisting language="xml"><entry key="*" value-ref="defaultLineTokenizer" /></programlisting>
|
||||
|
||||
<para>There is also a
|
||||
<classname>PatternMatchingCompositeLineTokenizer</classname> that can
|
||||
@@ -1051,9 +1058,9 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
|
||||
contains the number of tokens encountered, and the number
|
||||
expected:</para>
|
||||
|
||||
<programlisting>tokenizer.setNames(new String[] {"A", "B", "C", "D"});
|
||||
<programlisting language="java">tokenizer.setNames(new String[] {"A", "B", "C", "D"});
|
||||
|
||||
try{
|
||||
try {
|
||||
tokenizer.tokenize("a,b,c");
|
||||
}
|
||||
catch(IncorrectTokenCountException e){
|
||||
@@ -1076,7 +1083,7 @@ catch(IncorrectTokenCountException e){
|
||||
line length doesn't add up to the widest value of this column, an
|
||||
exception is thrown:</para>
|
||||
|
||||
<programlisting>tokenizer.setColumns(new Range[] { new Range(1, 5),
|
||||
<programlisting language="java">tokenizer.setColumns(new Range[] { new Range(1, 5),
|
||||
new Range(6, 10),
|
||||
new Range(11, 15) });
|
||||
try {
|
||||
@@ -1100,7 +1107,7 @@ catch (IncorrectLineLengthException ex) {
|
||||
For this reason, validation of line length can be turned off via the
|
||||
'strict' property:</para>
|
||||
|
||||
<programlisting>tokenizer.setColumns(new Range[] { new Range(1, 5), new Range(6, 10) });
|
||||
<programlisting language="java">tokenizer.setColumns(new Range[] { new Range(1, 5), new Range(6, 10) });
|
||||
<emphasis role="bold">tokenizer.setStrict(false);</emphasis>
|
||||
FieldSet tokens = tokenizer.tokenize("12345");
|
||||
assertEquals("12345", tokens.readString(0));
|
||||
@@ -1134,7 +1141,7 @@ assertEquals("", tokens.readString(1));</programlisting>
|
||||
In Spring Batch this is the
|
||||
<classname>LineAggregator</classname>:</para>
|
||||
|
||||
<programlisting>public interface LineAggregator<T> {
|
||||
<programlisting language="java">public interface LineAggregator<T> {
|
||||
|
||||
public String aggregate(T item);
|
||||
|
||||
@@ -1157,7 +1164,7 @@ assertEquals("", tokens.readString(1));</programlisting>
|
||||
simply assumes that the object is already a string, or that its
|
||||
string representation is acceptable for writing:</para>
|
||||
|
||||
<programlisting>public class PassThroughLineAggregator<T> implements LineAggregator<T> {
|
||||
<programlisting language="java">public class PassThroughLineAggregator<T> implements LineAggregator<T> {
|
||||
|
||||
public String aggregate(T item) {
|
||||
return item.toString();
|
||||
@@ -1196,13 +1203,13 @@ assertEquals("", tokens.readString(1));</programlisting>
|
||||
<classname>FlatFileItemWriter</classname> expresses this in
|
||||
code:</para>
|
||||
|
||||
<programlisting>public void write(T item) throws Exception {
|
||||
<programlisting language="java">public void write(T item) throws Exception {
|
||||
write(lineAggregator.aggregate(item) + LINE_SEPARATOR);
|
||||
}</programlisting>
|
||||
|
||||
<para>A simple configuration would look like the following:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.spr...FlatFileItemWriter">
|
||||
<programlisting language="xml"><bean id="itemWriter" class="org.spr...FlatFileItemWriter">
|
||||
<property name="resource" value="file:target/test-outputs/output.txt" />
|
||||
<property name="lineAggregator">
|
||||
<bean class="org.spr...PassThroughLineAggregator"/>
|
||||
@@ -1257,7 +1264,7 @@ assertEquals("", tokens.readString(1));</programlisting>
|
||||
<classname>FieldExtractor</classname> must be written to accomplish
|
||||
the task of turning the item into an array:</para>
|
||||
|
||||
<programlisting>public interface FieldExtractor<T> {
|
||||
<programlisting language="java">public interface FieldExtractor<T> {
|
||||
|
||||
Object[] extract(T item);
|
||||
|
||||
@@ -1293,7 +1300,7 @@ assertEquals("", tokens.readString(1));</programlisting>
|
||||
<classname>BeanWrapperFieldExtractor</classname> provides just this
|
||||
type of functionality:</para>
|
||||
|
||||
<programlisting>BeanWrapperFieldExtractor<Name> extractor = new BeanWrapperFieldExtractor<Name>();
|
||||
<programlisting language="java">BeanWrapperFieldExtractor<Name> extractor = new BeanWrapperFieldExtractor<Name>();
|
||||
extractor.setNames(new String[] { "first", "last", "born" });
|
||||
|
||||
String first = "Alan";
|
||||
@@ -1328,7 +1335,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
writes out a simple domain object that represents a credit to a
|
||||
customer account:</para>
|
||||
|
||||
<programlisting>public class CustomerCredit {
|
||||
<programlisting language="java">public class CustomerCredit {
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
@@ -1341,7 +1348,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
FieldExtractor interface must be provided, along with the delimiter to
|
||||
use:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
|
||||
<programlisting language="xml"><bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
|
||||
<property name="resource" ref="outputResource" />
|
||||
<property name="lineAggregator">
|
||||
<bean class="org.spr...DelimitedLineAggregator">
|
||||
@@ -1372,7 +1379,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
Using the same <classname>CustomerCredit</classname> domain object
|
||||
described above, it can be configured as follows:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
|
||||
<programlisting language="xml"><bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
|
||||
<property name="resource" ref="outputResource" />
|
||||
<property name="lineAggregator">
|
||||
<bean class="org.spr...FormatterLineAggregator">
|
||||
@@ -1389,7 +1396,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
<para>Most of the above example should look familiar. However, the
|
||||
value of the format property is new:</para>
|
||||
|
||||
<programlisting><property name="format" value="%-9s%-2.0f" /></programlisting>
|
||||
<programlisting language="xml"><property name="format" value="%-9s%-2.0f" /></programlisting>
|
||||
|
||||
<para>The underlying implementation is built using the same
|
||||
<classname>Formatter</classname> added as part of Java 5. The Java
|
||||
@@ -1498,7 +1505,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
stream. First, lets examine a set of XML records that the
|
||||
<classname>StaxEventItemReader</classname> can process.</para>
|
||||
|
||||
<para><programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<records>
|
||||
<trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
|
||||
<isin>XYZ0001</isin>
|
||||
@@ -1518,7 +1525,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
<price>99.99</price>
|
||||
<customer>Customer3</customer>
|
||||
</trade>
|
||||
</records></programlisting></para>
|
||||
</records></programlisting>
|
||||
|
||||
<para>To be able to process the XML records the following is needed:
|
||||
<itemizedlist>
|
||||
@@ -1540,12 +1547,11 @@ assertEquals(born, values[2]);</programlisting>
|
||||
</listitem>
|
||||
</itemizedlist></para>
|
||||
|
||||
<para><programlisting><bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
|
||||
<property name="fragmentRootElementName" value="trade" />
|
||||
<property name="resource" value="data/iosample/input/input.xml" />
|
||||
<property name="unmarshaller" ref="tradeMarshaller" />
|
||||
</bean>
|
||||
</programlisting></para>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>Notice that in this example we have chosen to use an
|
||||
<classname>XStreamMarshaller</classname> which accepts an alias passed
|
||||
@@ -1556,7 +1562,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
the map. In the configuration file we can use a Spring configuration
|
||||
utility to describe the required alias as follows:</para>
|
||||
|
||||
<para><programlisting><bean id="tradeMarshaller"
|
||||
<programlisting language="xml"><bean id="tradeMarshaller"
|
||||
class="org.springframework.oxm.xstream.XStreamMarshaller">
|
||||
<property name="aliases">
|
||||
<emphasis role="bold"> <util:map id="aliases">
|
||||
@@ -1566,7 +1572,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
<entry key="name" value="java.lang.String" />
|
||||
</util:map></emphasis>
|
||||
</property>
|
||||
</bean></programlisting></para>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>On input the reader reads the XML resource until it recognizes
|
||||
that a new fragment is about to start (by matching the tag name by
|
||||
@@ -1580,7 +1586,7 @@ assertEquals(born, values[2]);</programlisting>
|
||||
Java code which uses the injection provided by the Spring
|
||||
configuration:</para>
|
||||
|
||||
<para><programlisting>StaxEventItemReader xmlStaxEventItemReader = new StaxEventItemReader()
|
||||
<programlisting language="java">StaxEventItemReader xmlStaxEventItemReader = new StaxEventItemReader()
|
||||
Resource resource = new ByteArrayResource(xmlResource.getBytes())
|
||||
|
||||
Map aliases = new HashMap();
|
||||
@@ -1606,7 +1612,7 @@ while (hasNext) {
|
||||
else {
|
||||
System.out.println(credit);
|
||||
}
|
||||
}</programlisting></para>
|
||||
}</programlisting>
|
||||
</section>
|
||||
|
||||
<section id="StaxEventItemWriter">
|
||||
@@ -1624,7 +1630,7 @@ while (hasNext) {
|
||||
<classname>MarshallingEventWriterSerializer</classname>. The Spring
|
||||
configuration for this setup looks as follows:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
|
||||
<programlisting language="xml"><bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
|
||||
<property name="resource" ref="outputResource" />
|
||||
<property name="marshaller" ref="customerCreditMarshaller" />
|
||||
<property name="rootTagName" value="customers" />
|
||||
@@ -1637,7 +1643,7 @@ while (hasNext) {
|
||||
should be noted the marshaller used for the writer is the exact same as
|
||||
the one used in the reading example from earlier in the chapter:</para>
|
||||
|
||||
<programlisting><bean id="customerCreditMarshaller"
|
||||
<programlisting language="xml"><bean id="customerCreditMarshaller"
|
||||
class="org.springframework.oxm.xstream.XStreamMarshaller">
|
||||
<property name="aliases">
|
||||
<util:map id="aliases">
|
||||
@@ -1653,7 +1659,7 @@ while (hasNext) {
|
||||
all of the points discussed, demonstrating the programmatic setup of the
|
||||
required properties:</para>
|
||||
|
||||
<programlisting>StaxEventItemWriter staxItemWriter = new StaxEventItemWriter()
|
||||
<programlisting language="java">StaxEventItemWriter staxItemWriter = new StaxEventItemWriter()
|
||||
FileSystemResource resource = new FileSystemResource("data/outputFile.xml")
|
||||
|
||||
Map aliases = new HashMap();
|
||||
@@ -1693,7 +1699,7 @@ staxItemWriter.write(trade);</programlisting>
|
||||
<classname>MuliResourceItemReader</classname> can be used to read in both
|
||||
files by using wildcards:</para>
|
||||
|
||||
<programlisting><bean id="multiResourceReader" class="org.spr...MultiResourceItemReader">
|
||||
<programlisting language="xml"><bean id="multiResourceReader" class="org.spr...MultiResourceItemReader">
|
||||
<property name="resources" value="classpath:data/input/file-*.txt" />
|
||||
<property name="delegate" ref="flatFileItemReader" />
|
||||
</bean></programlisting>
|
||||
@@ -1776,7 +1782,7 @@ staxItemWriter.write(trade);</programlisting>
|
||||
<classname>DataSource</classname>. The following database schema will
|
||||
be used as an example:</para>
|
||||
|
||||
<programlisting>CREATE TABLE CUSTOMER (
|
||||
<programlisting language="sql">CREATE TABLE CUSTOMER (
|
||||
ID BIGINT IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR(45),
|
||||
CREDIT FLOAT
|
||||
@@ -1787,7 +1793,7 @@ staxItemWriter.write(trade);</programlisting>
|
||||
interface to map a <classname>CustomerCredit</classname>
|
||||
object:</para>
|
||||
|
||||
<programlisting>public class CustomerCreditRowMapper implements RowMapper {
|
||||
<programlisting language="java">public class CustomerCreditRowMapper implements RowMapper {
|
||||
|
||||
public static final String ID_COLUMN = "id";
|
||||
public static final String NAME_COLUMN = "name";
|
||||
@@ -1813,7 +1819,7 @@ staxItemWriter.write(trade);</programlisting>
|
||||
CUSTOMER database. The first example will be using
|
||||
<classname>JdbcTemplate</classname>:</para>
|
||||
|
||||
<programlisting>//For simplicity sake, assume a dataSource has already been obtained
|
||||
<programlisting language="java">//For simplicity sake, assume a dataSource has already been obtained
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
List customerCredits = jdbcTemplate.query("SELECT ID, NAME, CREDIT from CUSTOMER",
|
||||
new CustomerCreditRowMapper());</programlisting>
|
||||
@@ -1827,7 +1833,7 @@ List customerCredits = jdbcTemplate.query("SELECT ID, NAME, CREDIT from CUSTOMER
|
||||
contrast this with the approach of the
|
||||
<classname>JdbcCursorItemReader</classname>:</para>
|
||||
|
||||
<programlisting>JdbcCursorItemReader itemReader = new JdbcCursorItemReader();
|
||||
<programlisting language="java">JdbcCursorItemReader itemReader = new JdbcCursorItemReader();
|
||||
itemReader.setDataSource(dataSource);
|
||||
itemReader.setSql("SELECT ID, NAME, CREDIT from CUSTOMER");
|
||||
itemReader.setRowMapper(new CustomerCreditRowMapper());
|
||||
@@ -1855,7 +1861,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
configured for injection into a Spring Batch
|
||||
<classname>Step</classname>:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.spr...JdbcCursorItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...JdbcCursorItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="sql" value="select ID, NAME, CREDIT from CUSTOMER"/>
|
||||
<property name="rowMapper">
|
||||
@@ -2000,7 +2006,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
configuration using the same 'customer credit' example as the JDBC
|
||||
reader:</para>
|
||||
|
||||
<programlisting>HibernateCursorItemReader itemReader = new HibernateCursorItemReader();
|
||||
<programlisting language="java">HibernateCursorItemReader itemReader = new HibernateCursorItemReader();
|
||||
itemReader.setQueryString("from CustomerCredit");
|
||||
//For simplicity sake, assume sessionFactory already obtained.
|
||||
itemReader.setSessionFactory(sessionFactory);
|
||||
@@ -2026,7 +2032,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<classname>JdbcCursorItemReader</classname>, configuration is
|
||||
straightforward:</para>
|
||||
|
||||
<programlisting><bean id="itemReader"
|
||||
<programlisting language="xml"><bean id="itemReader"
|
||||
class="org.springframework.batch.item.database.HibernateCursorItemReader">
|
||||
<property name="sessionFactory" ref="sessionFactory" />
|
||||
<property name="queryString" value="from CustomerCredit" />
|
||||
@@ -2062,7 +2068,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<para>Below is a basic example configuration using the same 'customer
|
||||
credit' example as earlier:</para>
|
||||
|
||||
<programlisting><bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader">
|
||||
<programlisting language="xml"><bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="procedureName" value="sp_customer_credit"/>
|
||||
<property name="rowMapper">
|
||||
@@ -2079,7 +2085,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
returned ref-cursor. Here is an example where the first parameter is
|
||||
the returned ref-cursor:</para>
|
||||
|
||||
<programlisting><bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader">
|
||||
<programlisting language="xml"><bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="procedureName" value="sp_customer_credit"/>
|
||||
<property name="refCursorPosition" value="1"/>
|
||||
@@ -2094,7 +2100,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<literal>true</literal>. It defaults to <literal>false</literal>. Here
|
||||
is what that would look like:</para>
|
||||
|
||||
<programlisting><bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader">
|
||||
<programlisting language="xml"><bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="procedureName" value="sp_customer_credit"/>
|
||||
<property name="function" value="true"/>
|
||||
@@ -2115,7 +2121,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
the out parameter that returns the ref-cursor, the second and third
|
||||
are in parameters that takes a value of type INTEGER:</para>
|
||||
|
||||
<programlisting><bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader">
|
||||
<programlisting language="xml"><bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="procedureName" value="spring.cursor_func"/>
|
||||
<property name="parameters">
|
||||
@@ -2194,7 +2200,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<para>Below is an example configuration using a similar 'customer
|
||||
credit' example as the cursor based ItemReaders above:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.spr...JdbcPagingItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...JdbcPagingItemReader">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="queryProvider">
|
||||
<bean class="org.spr...SqlPagingQueryProviderFactoryBean">
|
||||
@@ -2250,7 +2256,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
is an example configuration using the same 'customer credit' example
|
||||
as the JDBC reader above:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.spr...JpaPagingItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...JpaPagingItemReader">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory"/>
|
||||
<property name="queryString" value="select c from CustomerCredit c"/>
|
||||
<property name="pageSize" value="1000"/>
|
||||
@@ -2280,7 +2286,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<classname>IbatisPagingItemReader</classname> reading CustomerCredits
|
||||
as in the examples above:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.spr...IbatisPagingItemReader">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.spr...IbatisPagingItemReader">
|
||||
<property name="sqlMapClient" ref="sqlMapClient"/>
|
||||
<property name="queryId" value="getPagedCustomerCredits"/>
|
||||
<property name="pageSize" value="1000"/>
|
||||
@@ -2291,7 +2297,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
Here is an example of what that query should look like for
|
||||
MySQL.</para>
|
||||
|
||||
<programlisting><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
<programlisting language="xml"><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
select id, name, credit from customer order by id asc LIMIT #_skiprows#, #_pagesize#
|
||||
</select></programlisting>
|
||||
|
||||
@@ -2303,14 +2309,13 @@ itemReader.close(executionContext);</programlisting>
|
||||
is an example for Oracle (unfortunately we need to use CDATA for some
|
||||
operators since this belongs in an XML document):</para>
|
||||
|
||||
<programlisting><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
<programlisting language="xml"><select id="getPagedCustomerCredits" resultMap="customerCreditResult">
|
||||
select * from (
|
||||
select * from (
|
||||
select t.id, t.name, t.credit, ROWNUM ROWNUM_ from customer t order by id
|
||||
)) where ROWNUM_ <![CDATA[ > ]]> ( #_page# * #_pagesize# )
|
||||
) where ROWNUM <![CDATA[ <= ]]> #_pagesize#
|
||||
</select>
|
||||
</programlisting>
|
||||
</select></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -2407,7 +2412,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
standard Spring method invoking the delegate pattern and are fairly simple
|
||||
to set up. Below is an example of the reader:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter">
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter">
|
||||
<property name="targetObject" ref="fooService" />
|
||||
<property name="targetMethod" value="generateFoo" />
|
||||
</bean>
|
||||
@@ -2423,7 +2428,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
<classname>ItemWriter</classname>. The <classname>ItemWriter</classname>
|
||||
implementation is equally as simple:</para>
|
||||
|
||||
<programlisting><bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
|
||||
<programlisting language="xml"><bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
|
||||
<property name="targetObject" ref="fooService" />
|
||||
<property name="targetMethod" value="processFoo" />
|
||||
</bean>
|
||||
@@ -2452,7 +2457,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
rather provides a very simple interface that can be implemented by any
|
||||
number of frameworks:</para>
|
||||
|
||||
<programlisting>public interface Validator {
|
||||
<programlisting language="java">public interface Validator {
|
||||
|
||||
void validate(Object value) throws ValidationException;
|
||||
|
||||
@@ -2463,7 +2468,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
it is valid. Spring Batch provides an out of the box
|
||||
<classname>ItemProcessor:</classname></para>
|
||||
|
||||
<programlisting><bean class="org.springframework.batch.item.validator.ValidatingItemProcessor">
|
||||
<programlisting language="xml"><bean class="org.springframework.batch.item.validator.ValidatingItemProcessor">
|
||||
<property name="validator" ref="validator" />
|
||||
</bean>
|
||||
|
||||
@@ -2516,7 +2521,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
restart. For this reason, all readers and writers include the 'saveState'
|
||||
property:</para>
|
||||
|
||||
<programlisting><bean id="playerSummarizationSource" class="org.spr...JdbcCursorItemReader">
|
||||
<programlisting language="xml"><bean id="playerSummarizationSource" class="org.spr...JdbcCursorItemReader">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
<property name="rowMapper">
|
||||
<bean class="org.springframework.batch.sample.PlayerSummaryMapper" />
|
||||
@@ -2563,7 +2568,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
basic contract of <classname>ItemReader</classname>,
|
||||
<methodname>read</methodname>:</para>
|
||||
|
||||
<programlisting>public class CustomItemReader<T> implements ItemReader<T>{
|
||||
<programlisting language="java">public class CustomItemReader<T> implements ItemReader<T>{
|
||||
|
||||
List<T> items;
|
||||
|
||||
@@ -2586,7 +2591,7 @@ itemReader.close(executionContext);</programlisting>
|
||||
returns null, thus satisfying the most basic requirements of an
|
||||
<classname>ItemReader</classname>, as illustrated below:</para>
|
||||
|
||||
<programlisting>List<String> items = new ArrayList<String>();
|
||||
<programlisting language="java">List<String> items = new ArrayList<String>();
|
||||
items.add("1");
|
||||
items.add("2");
|
||||
items.add("3");
|
||||
@@ -2616,7 +2621,7 @@ assertNull(itemReader.read());</programlisting>
|
||||
<para>If you do need to store state, then the
|
||||
<classname>ItemStream</classname> interface should be used:</para>
|
||||
|
||||
<programlisting>public class CustomItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
<programlisting language="java">public class CustomItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
List<T> items;
|
||||
int currentIndex = 0;
|
||||
@@ -2663,7 +2668,7 @@ assertNull(itemReader.read());</programlisting>
|
||||
fairly trivial example, but it still meets the general
|
||||
contract:</para>
|
||||
|
||||
<programlisting>ExecutionContext executionContext = new ExecutionContext();
|
||||
<programlisting language="java">ExecutionContext executionContext = new ExecutionContext();
|
||||
((ItemStream)itemReader).open(executionContext);
|
||||
assertEquals("1", itemReader.read());
|
||||
((ItemStream)itemReader).update(executionContext);
|
||||
@@ -2709,7 +2714,7 @@ assertEquals("2", itemReader.read());</programlisting>
|
||||
<classname>List</classname> will be used in order to keep the example as
|
||||
simple as possible:</para>
|
||||
|
||||
<programlisting>public class CustomItemWriter<T> implements ItemWriter<T> {
|
||||
<programlisting language="java">public class CustomItemWriter<T> implements ItemWriter<T> {
|
||||
|
||||
List<T> output = TransactionAwareProxyFactory.createTransactionalList();
|
||||
|
||||
|
||||
@@ -14,18 +14,20 @@
|
||||
The <classname>RepeatOperations</classname> interface looks like
|
||||
this:</para>
|
||||
|
||||
<para><programlisting>public interface RepeatOperations {
|
||||
<programlisting language="java">public interface RepeatOperations {
|
||||
|
||||
RepeatStatus iterate(RepeatCallback callback) throws RepeatException;
|
||||
|
||||
}</programlisting>The callback is a simple interface that allows you to insert
|
||||
}</programlisting>
|
||||
<para>The callback is a simple interface that allows you to insert
|
||||
some business logic to be repeated:</para>
|
||||
|
||||
<para><programlisting>public interface RepeatCallback {
|
||||
<programlisting language="java">public interface RepeatCallback {
|
||||
|
||||
RepeatStatus doInIteration(RepeatContext context) throws Exception;
|
||||
|
||||
}</programlisting>The callback is executed repeatedly until the implementation
|
||||
}</programlisting>
|
||||
<para>The callback is executed repeatedly until the implementation
|
||||
decides that the iteration should end. The return value in these
|
||||
interfaces is an enumeration that can either be
|
||||
<code>RepeatStatus.CONTINUABLE</code> or
|
||||
@@ -42,7 +44,7 @@
|
||||
<classname>RepeatOperations</classname> is
|
||||
<classname>RepeatTemplate</classname>. It could be used like this:</para>
|
||||
|
||||
<programlisting>RepeatTemplate template = new RepeatTemplate();
|
||||
<programlisting language="java">RepeatTemplate template = new RepeatTemplate();
|
||||
|
||||
template.setCompletionPolicy(new FixedChunkSizeCompletionPolicy(2));
|
||||
|
||||
@@ -162,12 +164,13 @@ template.iterate(new RepeatCallback() {
|
||||
<classname>ExceptionHandler</classname> which can decide whether or not to
|
||||
re-throw the exception.</para>
|
||||
|
||||
<para><programlisting>public interface ExceptionHandler {
|
||||
<programlisting language="java">public interface ExceptionHandler {
|
||||
|
||||
void handleException(RepeatContext context, Throwable throwable)
|
||||
throws RuntimeException;
|
||||
|
||||
}</programlisting>A common use case is to count the number of exceptions of a
|
||||
}</programlisting>
|
||||
<para>A common use case is to count the number of exceptions of a
|
||||
given type, and fail when a limit is reached. For this purpose Spring
|
||||
Batch provides the <classname>SimpleLimitExceptionHandler</classname> and
|
||||
slightly more flexible
|
||||
@@ -200,17 +203,14 @@ template.iterate(new RepeatCallback() {
|
||||
|
||||
<para>The interface looks like this:</para>
|
||||
|
||||
<para><programlisting>public interface RepeatListener {
|
||||
<programlisting language="java">public interface RepeatListener {
|
||||
void before(RepeatContext context);
|
||||
|
||||
void after(RepeatContext context, RepeatStatus result);
|
||||
|
||||
void open(RepeatContext context);
|
||||
|
||||
void onError(RepeatContext context, Throwable e);
|
||||
|
||||
void close(RepeatContext context);
|
||||
}</programlisting>The <methodname>open</methodname> and
|
||||
}</programlisting>
|
||||
<para>The <methodname>open</methodname> and
|
||||
<methodname>close</methodname> callbacks come before and after the entire
|
||||
iteration. <methodname>before</methodname>, <methodname>after</methodname>
|
||||
and <methodname>onError</methodname> apply to the individual
|
||||
@@ -258,7 +258,7 @@ template.iterate(new RepeatCallback() {
|
||||
<methodname>processMessage</methodname> (for more detail on how to
|
||||
configure AOP interceptors see the Spring User Guide):</para>
|
||||
|
||||
<programlisting><aop:config>
|
||||
<programlisting language="xml"><aop:config>
|
||||
<aop:pointcut id="transactional"
|
||||
expression="execution(* com..*Service.processMessage(..))" />
|
||||
<aop:advisor pointcut-ref="transactional"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
strategy. The <classname>RetryOperations</classname> interface looks like
|
||||
this:</para>
|
||||
|
||||
<para><programlisting>public interface RetryOperations {
|
||||
<programlisting language="java">public interface RetryOperations {
|
||||
|
||||
<T> T execute(RetryCallback<T> retryCallback) throws Exception;
|
||||
|
||||
@@ -36,14 +36,16 @@
|
||||
<T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
|
||||
RetryState retryState) throws Exception;
|
||||
|
||||
}</programlisting>The basic callback is a simple interface that allows you to
|
||||
}</programlisting>
|
||||
<para>The basic callback is a simple interface that allows you to
|
||||
insert some business logic to be retried:</para>
|
||||
|
||||
<para><programlisting>public interface RetryCallback<T> {
|
||||
<programlisting language="java">public interface RetryCallback<T> {
|
||||
|
||||
T doWithRetry(RetryContext context) throws Throwable;
|
||||
|
||||
}</programlisting>The callback is executed and if it fails (by throwing an
|
||||
}</programlisting>
|
||||
<para>The callback is executed and if it fails (by throwing an
|
||||
<classname>Exception</classname>), it will be retried until either it is
|
||||
successful, or the implementation decides to abort. There are a number of
|
||||
overloaded <methodname>execute</methodname> methods in the
|
||||
@@ -56,7 +58,7 @@
|
||||
<classname>RetryOperations</classname> is
|
||||
<classname>RetryTemplate</classname>. It could be used like this</para>
|
||||
|
||||
<programlisting>RetryTemplate template = new RetryTemplate();
|
||||
<programlisting language="java">RetryTemplate template = new RetryTemplate();
|
||||
|
||||
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
|
||||
policy.setTimeout(30000L);
|
||||
@@ -99,7 +101,7 @@ Foo result = template.execute(new RetryCallback<Foo>() {
|
||||
feature clients just pass in the callbacks together to the same method,
|
||||
for example:</para>
|
||||
|
||||
<para><programlisting>Foo foo = template.execute(new RetryCallback<Foo>() {
|
||||
<programlisting language="java">Foo foo = template.execute(new RetryCallback<Foo>() {
|
||||
public Foo doWithRetry(RetryContext context) {
|
||||
// business logic here
|
||||
},
|
||||
@@ -107,7 +109,8 @@ Foo result = template.execute(new RetryCallback<Foo>() {
|
||||
Foo recover(RetryContext context) throws Exception {
|
||||
// recover logic here
|
||||
}
|
||||
});</programlisting>If the business logic does not succeed before the template
|
||||
});</programlisting>
|
||||
<para>If the business logic does not succeed before the template
|
||||
decides to abort, then the client is given the chance to do some
|
||||
alternate processing through the recovery callback.</para>
|
||||
</section>
|
||||
@@ -235,7 +238,7 @@ Foo result = template.execute(new RetryCallback<Foo>() {
|
||||
this list overrides the retryable list so that it can be used to give
|
||||
finer control over the retry behavior:</para>
|
||||
|
||||
<programlisting>SimpleRetryPolicy policy = new SimpleRetryPolicy();
|
||||
<programlisting language="java">SimpleRetryPolicy policy = new SimpleRetryPolicy();
|
||||
// Set the max retry attempts
|
||||
policy.setMaxAttempts(5);
|
||||
// Retry on all exceptions (this is the default)
|
||||
@@ -277,14 +280,15 @@ template.execute(new RetryCallback<Foo>() {
|
||||
<classname>RetryTemplate</classname> can pause execution according to the
|
||||
<classname>BackoffPolicy</classname> in place.</para>
|
||||
|
||||
<para><programlisting>public interface BackoffPolicy {
|
||||
<programlisting language="java">public interface BackoffPolicy {
|
||||
|
||||
BackOffContext start(RetryContext context);
|
||||
|
||||
void backOff(BackOffContext backOffContext)
|
||||
throws BackOffInterruptedException;
|
||||
|
||||
}</programlisting>A <classname>BackoffPolicy</classname> is free to implement
|
||||
}</programlisting>
|
||||
<para>A <classname>BackoffPolicy</classname> is free to implement
|
||||
the backOff in any way it chooses. The policies provided by Spring Batch
|
||||
out of the box all use <code>Object.wait()</code>. A common use case is to
|
||||
backoff with an exponentially increasing wait period, to avoid two retries
|
||||
@@ -307,14 +311,15 @@ template.execute(new RetryCallback<Foo>() {
|
||||
|
||||
<para>The interface looks like this:</para>
|
||||
|
||||
<para><programlisting>public interface RetryListener {
|
||||
<programlisting language="java">public interface RetryListener {
|
||||
|
||||
void open(RetryContext context, RetryCallback<T> callback);
|
||||
|
||||
void onError(RetryContext context, RetryCallback<T> callback, Throwable e);
|
||||
|
||||
void close(RetryContext context, RetryCallback<T> callback, Throwable e);
|
||||
}</programlisting>The <methodname>open</methodname> and
|
||||
}</programlisting>
|
||||
<para>The <methodname>open</methodname> and
|
||||
<methodname>close</methodname> callbacks come before and after the entire
|
||||
retry in the simplest case and <methodname>onError</methodname> applies to
|
||||
the individual <classname>RetryCallback</classname> calls. The
|
||||
@@ -345,7 +350,7 @@ template.execute(new RetryCallback<Foo>() {
|
||||
<methodname>remoteCall</methodname> (for more detail on how to configure
|
||||
AOP interceptors see the Spring User Guide):</para>
|
||||
|
||||
<programlisting><aop:config>
|
||||
<programlisting language="xml"><aop:config>
|
||||
<aop:pointcut id="transactional"
|
||||
expression="execution(* com..*Service.remoteCall(..))" />
|
||||
<aop:advisor pointcut-ref="transactional"
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<classname>TaskExecutor</classname> to your Step configuration, e.g. as an
|
||||
attribute of the <literal>tasklet</literal>:</para>
|
||||
|
||||
<programlisting><step id="loading">
|
||||
<programlisting language="xml"><step id="loading">
|
||||
<tasklet task-executor="taskExecutor">...</tasklet>
|
||||
</step></programlisting>
|
||||
|
||||
@@ -69,10 +69,10 @@
|
||||
configuration which defaults to 4. You may need to increase this
|
||||
to ensure that a thread pool is fully utilised, e.g.</para>
|
||||
|
||||
<programlisting><step id="loading"> <tasklet
|
||||
<programlisting language="xml"><step id="loading"> <tasklet
|
||||
task-executor="taskExecutor"
|
||||
throttle-limit="20">...</tasklet>
|
||||
</step></programlisting>
|
||||
</step></programlisting>
|
||||
|
||||
<para>Note also that there may be limits placed on concurrency by
|
||||
any pooled resources used in your step, such as
|
||||
@@ -91,7 +91,7 @@
|
||||
indicator (see <xref linkend="process-indicator" xreflabel="" />) to keep
|
||||
track of items that have been processed in a database input table.</para>
|
||||
|
||||
<para>Spring Batch provides some implementations of
|
||||
<para>Spring Batch provides some implementations of
|
||||
<classname>ItemWriter</classname> and
|
||||
<classname>ItemReader</classname>. Usually they say in the
|
||||
Javadocs if they are thread safe or not, or what you have to do to
|
||||
@@ -117,7 +117,7 @@
|
||||
<literal>(step1,step2)</literal> in parallel with
|
||||
<literal>step3</literal>, you could configure a flow like this:</para>
|
||||
|
||||
<para><programlisting><job id="job1">
|
||||
<programlisting language="xml"><job id="job1">
|
||||
<split id="split1" task-executor="taskExecutor" next="step4">
|
||||
<flow>
|
||||
<step id="step1" parent="s1" next="step2"/>
|
||||
@@ -130,7 +130,7 @@
|
||||
<step id="step4" parent="s4"/>
|
||||
</job>
|
||||
|
||||
<beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/></programlisting></para>
|
||||
<beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/></programlisting>
|
||||
|
||||
<para>The configurable "task-executor" attribute is used to specify which
|
||||
TaskExecutor implementation should be used to execute the individual
|
||||
@@ -228,11 +228,11 @@
|
||||
the PartitionStep is shown driving the execution. The PartitionStep
|
||||
configuration looks like this:</para>
|
||||
|
||||
<para><programlisting><step id="step1.master">
|
||||
<programlisting language="xml"><step id="step1.master">
|
||||
<partition step="step1" partitioner="partitioner">
|
||||
<handler grid-size="10" task-executor="taskExecutor"/>
|
||||
</partition>
|
||||
</step></programlisting></para>
|
||||
</step></programlisting>
|
||||
|
||||
<para>Similar to the multi-threaded step's throttle-limit
|
||||
attribute, the grid-size attribute prevents the task executor from
|
||||
@@ -279,7 +279,7 @@
|
||||
default for a step configured with the XML namespace as above. It can
|
||||
also be configured explicitly like this:</para>
|
||||
|
||||
<para><programlisting><step id="step1.master">
|
||||
<programlisting language="xml"><step id="step1.master">
|
||||
<partition step="step1" handler="handler"/>
|
||||
</step>
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
<property name="taskExecutor" ref="taskExecutor"/>
|
||||
<property name="step" ref="step1" />
|
||||
<property name="gridSize" value="10" />
|
||||
</bean></programlisting></para>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>The <literal>gridSize</literal> determines the number of separate
|
||||
step executions to create, so it can be matched to the size of the
|
||||
@@ -309,7 +309,7 @@
|
||||
execution contexts as input parameters for new step executions only (no
|
||||
need to worry about restarts). It has a single method:</para>
|
||||
|
||||
<programlisting>public interface Partitioner {
|
||||
<programlisting language="java">public interface Partitioner {
|
||||
Map<String, ExecutionContext> partition(int gridSize);
|
||||
}</programlisting>
|
||||
|
||||
@@ -400,7 +400,7 @@
|
||||
<para>Then the file name can be bound to a step using late binding to
|
||||
the execution context:</para>
|
||||
|
||||
<programlisting><bean id="itemReader" scope="step"
|
||||
<programlisting language="xml"><bean id="itemReader" scope="step"
|
||||
class="org.spr...MultiResourceItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{stepExecutionContext[fileName]}/*</emphasis>"/>
|
||||
</bean></programlisting>
|
||||
|
||||
@@ -79,14 +79,14 @@
|
||||
requiring it, sequences were used. Each variation of the schema will
|
||||
contain some form of the following:</para>
|
||||
|
||||
<programlisting>CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
|
||||
<programlisting language="sql">CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
|
||||
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ;
|
||||
CREATE SEQUENCE BATCH_JOB_SEQ;</programlisting>
|
||||
|
||||
<para>Many database vendors don't support sequences. In these cases,
|
||||
work-arounds are used, such as the following for MySQL:</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=MYISAM;
|
||||
<programlisting language="sql">CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=MYISAM;
|
||||
INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);
|
||||
CREATE TABLE BATCH_JOB_EXECUTION_SEQ (ID BIGINT NOT NULL) type=MYISAM;
|
||||
INSERT INTO BATCH_JOB_EXECUTION_SEQ values(0);
|
||||
@@ -108,10 +108,10 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
hierarchy. The following generic DDL statement is used to create
|
||||
it:</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_JOB_INSTANCE (
|
||||
JOB_INSTANCE_ID BIGINT PRIMARY KEY ,
|
||||
VERSION BIGINT,
|
||||
JOB_NAME VARCHAR(100) NOT NULL ,
|
||||
<programlisting language="sql">CREATE TABLE BATCH_JOB_INSTANCE (
|
||||
JOB_INSTANCE_ID BIGINT PRIMARY KEY ,
|
||||
VERSION BIGINT,
|
||||
JOB_NAME VARCHAR(100) NOT NULL ,
|
||||
JOB_KEY VARCHAR(2500)
|
||||
);</programlisting>
|
||||
|
||||
@@ -152,12 +152,12 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
<para>The BATCH_JOB_EXECUTION_PARAMS table holds all information relevant to the
|
||||
<classname>JobParameters</classname> object. It contains 0 or more
|
||||
key/value pairs passed to a <classname>Job</classname> and serve as a record of the parameters
|
||||
a job was run with. For each parameter that contributes to the generation of a job's identity,
|
||||
a job was run with. For each parameter that contributes to the generation of a job's identity,
|
||||
the IDENTIFYING flag is set to true. It should be noted that the table has been
|
||||
denormalized. Rather than creating a separate table for each type, there
|
||||
is one table with a column indicating the type:</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
|
||||
<programlisting language="sql">CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
|
||||
JOB_EXECUTION_ID BIGINT NOT NULL ,
|
||||
TYPE_CD VARCHAR(6) NOT NULL ,
|
||||
KEY_NAME VARCHAR(100) NOT NULL ,
|
||||
@@ -225,12 +225,12 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
<classname>Job</classname> is run there will always be a new
|
||||
<classname>JobExecution</classname>, and a new row in this table:</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_JOB_EXECUTION (
|
||||
<programlisting language="sql">CREATE TABLE BATCH_JOB_EXECUTION (
|
||||
JOB_EXECUTION_ID BIGINT PRIMARY KEY ,
|
||||
VERSION BIGINT,
|
||||
VERSION BIGINT,
|
||||
JOB_INSTANCE_ID BIGINT NOT NULL,
|
||||
CREATE_TIME TIMESTAMP NOT NULL,
|
||||
START_TIME TIMESTAMP DEFAULT NULL,
|
||||
START_TIME TIMESTAMP DEFAULT NULL,
|
||||
END_TIME TIMESTAMP DEFAULT NULL,
|
||||
STATUS VARCHAR(10),
|
||||
EXIT_CODE VARCHAR(20),
|
||||
@@ -313,22 +313,22 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
least one entry per <classname>Step</classname> for each
|
||||
<classname>JobExecution</classname> created:</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_STEP_EXECUTION (
|
||||
<programlisting language="sql">CREATE TABLE BATCH_STEP_EXECUTION (
|
||||
STEP_EXECUTION_ID BIGINT PRIMARY KEY ,
|
||||
VERSION BIGINT NOT NULL,
|
||||
VERSION BIGINT NOT NULL,
|
||||
STEP_NAME VARCHAR(100) NOT NULL,
|
||||
JOB_EXECUTION_ID BIGINT NOT NULL,
|
||||
START_TIME TIMESTAMP NOT NULL ,
|
||||
END_TIME TIMESTAMP DEFAULT NULL,
|
||||
START_TIME TIMESTAMP NOT NULL ,
|
||||
END_TIME TIMESTAMP DEFAULT NULL,
|
||||
STATUS VARCHAR(10),
|
||||
COMMIT_COUNT BIGINT ,
|
||||
COMMIT_COUNT BIGINT ,
|
||||
READ_COUNT BIGINT ,
|
||||
FILTER_COUNT BIGINT ,
|
||||
WRITE_COUNT BIGINT ,
|
||||
READ_SKIP_COUNT BIGINT ,
|
||||
WRITE_SKIP_COUNT BIGINT ,
|
||||
PROCESS_SKIP_COUNT BIGINT ,
|
||||
ROLLBACK_COUNT BIGINT ,
|
||||
ROLLBACK_COUNT BIGINT ,
|
||||
EXIT_CODE VARCHAR(20) ,
|
||||
EXIT_MESSAGE VARCHAR(2500) ,
|
||||
LAST_UPDATED TIMESTAMP,
|
||||
@@ -456,7 +456,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
<classname>JobInstance</classname> can 'start from where it left
|
||||
off'.</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
|
||||
<programlisting language="sql">CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
|
||||
JOB_EXECUTION_ID BIGINT PRIMARY KEY,
|
||||
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
|
||||
SERIALIZED_CONTEXT CLOB,
|
||||
@@ -497,7 +497,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
<classname>JobInstance</classname> can 'start from where it left
|
||||
off'.</para>
|
||||
|
||||
<programlisting>CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
|
||||
<programlisting language="sql">CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
|
||||
STEP_EXECUTION_ID BIGINT PRIMARY KEY,
|
||||
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
|
||||
SERIALIZED_CONTEXT CLOB,
|
||||
@@ -562,8 +562,8 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
|
||||
|
||||
<para>If you are using multi-byte character sets (e.g. Chines or Cyrillic)
|
||||
in your business processing, then those characters might need to be
|
||||
persisted in the Spring Batch schema. Many users find that
|
||||
simply changing the schema to double the length of the <literal>VARCHAR</literal>
|
||||
persisted in the Spring Batch schema. Many users find that
|
||||
simply changing the schema to double the length of the <literal>VARCHAR</literal>
|
||||
columns is enough. Others prefer to configure the <link
|
||||
linkend="configuringJobRepository"><classname>JobRepository</classname></link> with <literal>max-varchar-length</literal> half the value of the <literal>VARCHAR</literal> column length is enough. Some users have also reported that
|
||||
they use <literal>NVARCHAR</literal> in place of <literal>VARCHAR</literal>
|
||||
|
||||
@@ -77,8 +77,7 @@
|
||||
namespace declarations to your Spring XML Application Context
|
||||
file:
|
||||
</para>
|
||||
<programlisting>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
<programlisting language="xml"><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
|
||||
xsi:schemaLocation="
|
||||
@@ -87,14 +86,12 @@
|
||||
|
||||
...
|
||||
|
||||
</beans>
|
||||
</programlisting>
|
||||
</beans></programlisting>
|
||||
<para>
|
||||
A fully configured Spring XML Application Context file for Spring
|
||||
Batch Integration may look like the following:
|
||||
</para>
|
||||
<programlisting>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
<programlisting language="xml"><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
@@ -111,8 +108,7 @@
|
||||
|
||||
...
|
||||
|
||||
</beans>
|
||||
</programlisting>
|
||||
</beans></programlisting>
|
||||
<para>
|
||||
Appending version numbers to the referenced XSD file is also
|
||||
allowed but, as a version-less declaration will always use the
|
||||
@@ -194,15 +190,14 @@
|
||||
</imageobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="src/site/docbook/reference/images/launch-batch-job.png"
|
||||
fileref="images/launch-batch-job.png"
|
||||
format="PNG" scale="60"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<sect3 id="transforming-a-file-into-a-joblaunchrequest">
|
||||
<title>Transforming a file into a JobLaunchRequest</title>
|
||||
<programlisting>
|
||||
package io.spring.sbi;
|
||||
<programlisting language="java">package io.spring.sbi;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
@@ -234,8 +229,7 @@ public class FileMessageToJobRequest {
|
||||
|
||||
return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
}</programlisting>
|
||||
</sect3>
|
||||
<sect3 id="the-jobexecution-response">
|
||||
<title>The JobExecution Response</title>
|
||||
@@ -281,8 +275,7 @@ public class FileMessageToJobRequest {
|
||||
</sect3>
|
||||
<sect3 id="spring-batch-integration-configuration">
|
||||
<title>Spring Batch Integration Configuration</title>
|
||||
<programlisting>
|
||||
<int:channel id="inboundFileChannel"/>
|
||||
<programlisting language="xml"><int:channel id="inboundFileChannel"/>
|
||||
<int:channel id="outboundJobRequestChannel"/>
|
||||
<int:channel id="jobLaunchReplyChannel"/>
|
||||
|
||||
@@ -304,8 +297,7 @@ public class FileMessageToJobRequest {
|
||||
<batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
|
||||
reply-channel="jobLaunchReplyChannel"/>
|
||||
|
||||
<int:logging-channel-adapter channel="jobLaunchReplyChannel"/>
|
||||
</programlisting>
|
||||
<int:logging-channel-adapter channel="jobLaunchReplyChannel"/></programlisting>
|
||||
<para>
|
||||
Now that we are polling for files and launching jobs, we need to
|
||||
configure for example our Spring Batch
|
||||
@@ -315,13 +307,11 @@ public class FileMessageToJobRequest {
|
||||
</sect3>
|
||||
<sect3 id="example-itemreader-configuration">
|
||||
<title>Example ItemReader Configuration</title>
|
||||
<programlisting>
|
||||
<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader"
|
||||
<programlisting language="xml"><bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader"
|
||||
scope="step">
|
||||
<property name="resource" value="file://#{jobParameters['input.file.name']}"/>
|
||||
...
|
||||
</bean>
|
||||
</programlisting>
|
||||
</bean></programlisting>
|
||||
<para>
|
||||
The main points of interest here are injecting the value of
|
||||
<classname role="strong">#{jobParameters['input.file.name']}</classname>
|
||||
@@ -439,12 +429,10 @@ public class FileMessageToJobRequest {
|
||||
a global default Poller or provide a Poller sub-element to the
|
||||
<classname>Job Launching Gateway</classname>:
|
||||
</para>
|
||||
<programlisting>
|
||||
<batch-int:job-launching-gateway request-channel="queueChannel"
|
||||
<programlisting language="xml"><batch-int:job-launching-gateway request-channel="queueChannel"
|
||||
reply-channel="replyChannel" job-launcher="jobLauncher">
|
||||
<int:poller fixed-rate="1000"/>
|
||||
</batch-int:job-launching-gateway>
|
||||
</programlisting>
|
||||
</batch-int:job-launching-gateway></programlisting>
|
||||
</sect4>
|
||||
</sect3>
|
||||
</sect2>
|
||||
@@ -519,7 +507,7 @@ public class FileMessageToJobRequest {
|
||||
</imageobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="src/site/docbook/reference/images/handling-informational-messages.png"
|
||||
fileref="images/handling-informational-messages.png"
|
||||
format="PNG" scale="60"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
@@ -532,20 +520,17 @@ public class FileMessageToJobRequest {
|
||||
<para>
|
||||
First create the notifications integration beans:
|
||||
</para>
|
||||
<programlisting>
|
||||
<int:channel id="stepExecutionsChannel"/>
|
||||
<programlisting language="xml"><int:channel id="stepExecutionsChannel"/>
|
||||
|
||||
<int:gateway id="notificationExecutionsListener"
|
||||
service-interface="org.springframework.batch.core.StepExecutionListener"
|
||||
default-request-channel="stepExecutionsChannel"/>
|
||||
|
||||
<int:logging-channel-adapter channel="stepExecutionsChannel"/>
|
||||
</programlisting>
|
||||
<int:logging-channel-adapter channel="stepExecutionsChannel"/></programlisting>
|
||||
<para>
|
||||
Then modify your job to add a step level listener:
|
||||
</para>
|
||||
<programlisting>
|
||||
<job id="importPayments">
|
||||
<programlisting language="xml"><job id="importPayments">
|
||||
<step id="step1">
|
||||
<tasklet ../>
|
||||
<chunk ../>
|
||||
@@ -555,8 +540,7 @@ public class FileMessageToJobRequest {
|
||||
</tasklet>
|
||||
...
|
||||
</step>
|
||||
</job>
|
||||
</programlisting>
|
||||
</job></programlisting>
|
||||
</sect2>
|
||||
<sect2 id="asynchronous-processors">
|
||||
<title>Asynchronous Processors</title>
|
||||
@@ -581,8 +565,7 @@ public class FileMessageToJobRequest {
|
||||
and <classname>AsyncItemWriter</classname> are simple, first the
|
||||
<classname>AsyncItemProcessor</classname>:
|
||||
</para>
|
||||
<programlisting>
|
||||
<bean id="processor"
|
||||
<programlisting language="xml"><bean id="processor"
|
||||
class="org.springframework.batch.integration.async.AsyncItemProcessor">
|
||||
<property name="delegate">
|
||||
<bean class="your.ItemProcessor"/>
|
||||
@@ -590,8 +573,7 @@ public class FileMessageToJobRequest {
|
||||
<property name="taskExecutor">
|
||||
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
|
||||
</property>
|
||||
</bean>
|
||||
</programlisting>
|
||||
</bean></programlisting>
|
||||
<para>
|
||||
The property "<classname>delegate</classname>" is actually
|
||||
a reference to your <classname>ItemProcessor</classname> bean and
|
||||
@@ -601,14 +583,12 @@ public class FileMessageToJobRequest {
|
||||
<para>
|
||||
Then we configure the <classname>AsyncItemWriter</classname>:
|
||||
</para>
|
||||
<programlisting>
|
||||
<bean id="itemWriter"
|
||||
<programlisting language="xml"><bean id="itemWriter"
|
||||
class="org.springframework.batch.integration.async.AsyncItemWriter">
|
||||
<property name="delegate">
|
||||
<bean id="itemWriter" class="your.ItemWriter"/>
|
||||
</property>
|
||||
</bean>
|
||||
</programlisting>
|
||||
</bean></programlisting>
|
||||
<para>
|
||||
Again, the property "<classname>delegate</classname>" is
|
||||
actually a reference to your <classname>ItemWriter</classname> bean.
|
||||
@@ -647,7 +627,7 @@ public class FileMessageToJobRequest {
|
||||
</imageobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="src/site/docbook/reference/images/remote-chunking-sbi.png"
|
||||
fileref="images/remote-chunking-sbi.png"
|
||||
format="PNG" scale="60"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
@@ -674,16 +654,14 @@ public class FileMessageToJobRequest {
|
||||
A simple job with a step to be remotely chunked would have a
|
||||
configuration similar to the following:
|
||||
</para>
|
||||
<programlisting>
|
||||
<job id="personJob">
|
||||
<programlisting language="xml"><job id="personJob">
|
||||
<step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="200"/>
|
||||
</tasklet>
|
||||
...
|
||||
</step>
|
||||
</job>
|
||||
</programlisting>
|
||||
</job></programlisting>
|
||||
<para>
|
||||
The ItemReader reference would point to the bean you would like
|
||||
to use for reading data on the master. The ItemWriter reference
|
||||
@@ -695,8 +673,7 @@ public class FileMessageToJobRequest {
|
||||
advised to check any additional component properties such as
|
||||
throttle limits and so on when implementing your use case.
|
||||
</para>
|
||||
<programlisting>
|
||||
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
|
||||
<programlisting language="xml"><bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
|
||||
<property name="brokerURL" value="tcp://localhost:61616"/>
|
||||
</bean>
|
||||
|
||||
@@ -727,8 +704,7 @@ public class FileMessageToJobRequest {
|
||||
|
||||
<int-jms:message-driven-channel-adapter id="jmsReplies"
|
||||
destination-name="replies"
|
||||
channel="replies"/>
|
||||
</programlisting>
|
||||
channel="replies"/></programlisting>
|
||||
<para>
|
||||
This configuration provides us with a number of beans. We
|
||||
configure our messaging middleware using ActiveMQ and
|
||||
@@ -741,8 +717,7 @@ public class FileMessageToJobRequest {
|
||||
<para>
|
||||
Now lets move on to the slave configuration:
|
||||
</para>
|
||||
<programlisting>
|
||||
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
|
||||
<programlisting language="xml"><bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
|
||||
<property name="brokerURL" value="tcp://localhost:61616"/>
|
||||
</bean>
|
||||
|
||||
@@ -776,8 +751,7 @@ public class FileMessageToJobRequest {
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</programlisting>
|
||||
</bean></programlisting>
|
||||
<para>
|
||||
Most of these configuration items should look familiar from the
|
||||
master configuration. Slaves do not need access to things like
|
||||
@@ -810,7 +784,7 @@ public class FileMessageToJobRequest {
|
||||
</imageobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="src/site/docbook/reference/images/remote-partitioning.png"
|
||||
fileref="images/remote-partitioning.png"
|
||||
format="PNG" scale="60"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
@@ -865,8 +839,7 @@ public class FileMessageToJobRequest {
|
||||
the <classname>MessageChannelPartitionHandler</classname> and JMS
|
||||
configuration:
|
||||
</para>
|
||||
<programlisting>
|
||||
<bean id="partitionHandler"
|
||||
<programlisting language="xml"><bean id="partitionHandler"
|
||||
class="org.springframework.batch.integration.partition.MessageChannelPartitionHandler">
|
||||
<property name="stepName" value="step1"/>
|
||||
<property name="gridSize" value="3"/>
|
||||
@@ -912,20 +885,17 @@ public class FileMessageToJobRequest {
|
||||
</int:channel>
|
||||
|
||||
<bean id="stepLocator"
|
||||
class="org.springframework.batch.integration.partition.BeanFactoryStepLocator" />
|
||||
</programlisting>
|
||||
class="org.springframework.batch.integration.partition.BeanFactoryStepLocator" /></programlisting>
|
||||
<para>
|
||||
Also ensure the partition <classname>handler</classname> attribute
|
||||
maps to the <classname>partitionHandler</classname> bean:
|
||||
</para>
|
||||
<programlisting>
|
||||
<job id="personJob">
|
||||
<programlisting language="xml"><job id="personJob">
|
||||
<step id="step1.master">
|
||||
<partition partitioner="partitioner" handler="partitionHandler"/>
|
||||
...
|
||||
</step>
|
||||
</job>
|
||||
</programlisting>
|
||||
</job></programlisting>
|
||||
</sect3>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<para>Below is a code representation of the same concepts shown
|
||||
above:</para>
|
||||
|
||||
<programlisting>List items = new Arraylist();
|
||||
<programlisting language="java">List items = new Arraylist();
|
||||
for(int i = 0; i < commitInterval; i++){
|
||||
Object item = itemReader.read()
|
||||
Object processedItem = itemProcessor.process(item);
|
||||
@@ -70,7 +70,7 @@ itemWriter.write(items);</programlisting>
|
||||
potentially contain many collaborators. In order to ease configuration,
|
||||
the Spring Batch namespace can be used:</para>
|
||||
|
||||
<programlisting><job id="sampleJob" job-repository="jobRepository">
|
||||
<programlisting language="xml"><job id="sampleJob" job-repository="jobRepository">
|
||||
<step id="step1">
|
||||
<tasklet transaction-manager="transactionManager">
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
|
||||
@@ -138,7 +138,7 @@ itemWriter.write(items);</programlisting>
|
||||
allowStartIfComplete=true. Additionally, the commitInterval will be '5'
|
||||
since it is overridden by the "concreteStep1":</para>
|
||||
|
||||
<programlisting><step id="parentStep">
|
||||
<programlisting language="xml"><step id="parentStep">
|
||||
<tasklet allow-start-if-complete="true">
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
|
||||
</tasklet>
|
||||
@@ -183,7 +183,7 @@ itemWriter.write(items);</programlisting>
|
||||
be abstract. The <classname>Step</classname> "concreteStep2" will have
|
||||
'itemReader', 'itemWriter', and commitInterval=10.</para>
|
||||
|
||||
<programlisting><step id="abstractParentStep" abstract="true">
|
||||
<programlisting language="xml"><step id="abstractParentStep" abstract="true">
|
||||
<tasklet>
|
||||
<chunk commit-interval="10"/>
|
||||
</tasklet>
|
||||
@@ -214,7 +214,7 @@ itemWriter.write(items);</programlisting>
|
||||
<classname>listenerOne</classname> and
|
||||
<classname>listenerTwo</classname>:</para>
|
||||
|
||||
<programlisting><step id="listenersParentStep" abstract="true">
|
||||
<programlisting language="xml"><step id="listenersParentStep" abstract="true">
|
||||
<listeners>
|
||||
<listener ref="listenerOne"/>
|
||||
<listeners>
|
||||
@@ -246,7 +246,7 @@ itemWriter.write(items);</programlisting>
|
||||
number of items that are processed within a commit can be
|
||||
configured.</para>
|
||||
|
||||
<programlisting><job id="sampleJob">
|
||||
<programlisting language="xml"><job id="sampleJob">
|
||||
<step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter" <emphasis
|
||||
@@ -285,8 +285,8 @@ itemWriter.write(items);</programlisting>
|
||||
as a <classname>Step</classname> that can be run infinitely. Below is
|
||||
an example start limit configuration:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<tasklet <emphasis role="bold">start-limit="1"</emphasis>>
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet start-limit="1">
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
|
||||
</tasklet>
|
||||
</step></programlisting>
|
||||
@@ -309,8 +309,8 @@ itemWriter.write(items);</programlisting>
|
||||
successfully, will be skipped. Setting allow-start-if-complete to
|
||||
"true" overrides this so that the step will always run:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<tasklet <emphasis role="bold">allow-start-if-complete="true"</emphasis>>
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet allow-start-if-complete="true">
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
|
||||
</tasklet>
|
||||
</step></programlisting>
|
||||
@@ -319,7 +319,7 @@ itemWriter.write(items);</programlisting>
|
||||
<section id="stepRestartExample">
|
||||
<title>Step Restart Configuration Example</title>
|
||||
|
||||
<programlisting><job id="footballJob" restartable="true">
|
||||
<programlisting language="xml"><job id="footballJob" restartable="true">
|
||||
<step id="playerload" next="gameLoad">
|
||||
<tasklet>
|
||||
<chunk reader="playerFileItemReader" writer="playerWriter"
|
||||
@@ -451,7 +451,9 @@ itemWriter.write(items);</programlisting>
|
||||
loaded because it was formatted incorrectly or was missing necessary
|
||||
information, then there probably won't be issues. Usually these bad
|
||||
records are logged as well, which will be covered later when discussing
|
||||
listeners.<programlisting><step id="step1">
|
||||
listeners.
|
||||
</para>
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="flatFileItemReader" writer="itemWriter"
|
||||
commit-interval="10" <emphasis role="bold">skip-limit="10"</emphasis>>
|
||||
@@ -460,7 +462,7 @@ itemWriter.write(items);</programlisting>
|
||||
</skippable-exception-classes></emphasis>
|
||||
</chunk>
|
||||
</tasklet>
|
||||
</step></programlisting></para>
|
||||
</step></programlisting>
|
||||
|
||||
<para>In this example, a <classname>FlatFileItemReader</classname> is
|
||||
used, and if at any point a
|
||||
@@ -475,7 +477,9 @@ itemWriter.write(items);</programlisting>
|
||||
<classname>Job</classname> to fail. In certain scenarios this may be the
|
||||
correct behavior. However, in other scenarios it may be easier to
|
||||
identify which exceptions should cause failure and skip everything
|
||||
else:<programlisting><step id="step1">
|
||||
else:
|
||||
</para>
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="flatFileItemReader" writer="itemWriter"
|
||||
commit-interval="10" <emphasis role="bold">skip-limit="10"</emphasis>>
|
||||
@@ -485,7 +489,7 @@ itemWriter.write(items);</programlisting>
|
||||
</skippable-exception-classes>
|
||||
</emphasis> </chunk>
|
||||
</tasklet>
|
||||
</step></programlisting></para>
|
||||
</step></programlisting>
|
||||
|
||||
<para>By 'including' <classname>java.lang.Exception</classname> as a
|
||||
skippable exception class, the configuration indicates that all
|
||||
@@ -517,7 +521,7 @@ itemWriter.write(items);</programlisting>
|
||||
process holds a lock on, waiting and trying again might result in
|
||||
success. In this case, retry should be configured:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter"
|
||||
commit-interval="2" <emphasis role="bold">retry-limit="3"</emphasis>>
|
||||
@@ -548,7 +552,7 @@ itemWriter.write(items);</programlisting>
|
||||
the <classname>Step</classname> can be configured with a list of
|
||||
exceptions that should not cause rollback.</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
|
||||
<no-rollback-exception-classes>
|
||||
@@ -570,7 +574,7 @@ itemWriter.write(items);</programlisting>
|
||||
this reason, the step can be configured to not buffer the
|
||||
items:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="2"
|
||||
<emphasis role="bold"> is-reader-transactional-queue="true"</emphasis>/>
|
||||
@@ -587,7 +591,7 @@ itemWriter.write(items);</programlisting>
|
||||
transaction attributes can be found in the spring core
|
||||
documentation.</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
|
||||
<transaction-attributes isolation="DEFAULT"
|
||||
@@ -618,7 +622,7 @@ itemWriter.write(items);</programlisting>
|
||||
can be registered on the <classname>Step</classname> through the
|
||||
'streams' element, as illustrated below:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="itemReader" writer="compositeWriter" commit-interval="2">
|
||||
<emphasis role="bold"><streams>
|
||||
@@ -675,7 +679,7 @@ itemWriter.write(items);</programlisting>
|
||||
the most granular level that it applies (chunk in the example
|
||||
given).</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet>
|
||||
<chunk reader="reader" writer="writer" commit-interval="10"/>
|
||||
<listeners>
|
||||
@@ -714,7 +718,7 @@ itemWriter.write(items);</programlisting>
|
||||
for notification before a <classname>Step</classname> is started and
|
||||
after it has ends, whether it ended normally or failed:</para>
|
||||
|
||||
<programlisting>public interface StepExecutionListener extends StepListener {
|
||||
<programlisting language="java">public interface StepExecutionListener extends StepListener {
|
||||
|
||||
void beforeStep(StepExecution stepExecution);
|
||||
|
||||
@@ -749,10 +753,9 @@ itemWriter.write(items);</programlisting>
|
||||
useful to perform logic before a chunk begins processing or after a
|
||||
chunk has completed successfully:</para>
|
||||
|
||||
<programlisting>public interface ChunkListener extends StepListener {
|
||||
<programlisting language="java">public interface ChunkListener extends StepListener {
|
||||
|
||||
void beforeChunk();
|
||||
|
||||
void afterChunk();
|
||||
|
||||
}</programlisting>
|
||||
@@ -790,15 +793,15 @@ itemWriter.write(items);</programlisting>
|
||||
<para>When discussing skip logic above, it was mentioned that it may
|
||||
be beneficial to log the skipped records, so that they can be deal
|
||||
with later. In the case of read errors, this can be done with an
|
||||
<classname>ItemReaderListener:</classname><programlisting>public interface ItemReadListener<T> extends StepListener {
|
||||
<classname>ItemReaderListener:</classname>
|
||||
</para>
|
||||
<programlisting language="java">public interface ItemReadListener<T> extends StepListener {
|
||||
|
||||
void beforeRead();
|
||||
|
||||
void afterRead(T item);
|
||||
|
||||
void onReadError(Exception ex);
|
||||
|
||||
}</programlisting></para>
|
||||
}</programlisting>
|
||||
|
||||
<para>The <methodname>beforeRead</methodname> method will be called
|
||||
before each call to <methodname>read</methodname> on the
|
||||
@@ -833,12 +836,10 @@ itemWriter.write(items);</programlisting>
|
||||
<para>Just as with the <classname>ItemReadListener</classname>, the
|
||||
processing of an item can be 'listened' to:</para>
|
||||
|
||||
<programlisting>public interface ItemProcessListener<T, S> extends StepListener {
|
||||
<programlisting language="java">public interface ItemProcessListener<T, S> extends StepListener {
|
||||
|
||||
void beforeProcess(T item);
|
||||
|
||||
void afterProcess(T item, S result);
|
||||
|
||||
void onProcessError(T item, Exception e);
|
||||
|
||||
}</programlisting>
|
||||
@@ -876,12 +877,10 @@ itemWriter.write(items);</programlisting>
|
||||
<para>The writing of an item can be 'listened' to with the
|
||||
<classname>ItemWriteListener</classname>:</para>
|
||||
|
||||
<programlisting> public interface ItemWriteListener<S> extends StepListener {
|
||||
<programlisting language="java">public interface ItemWriteListener<S> extends StepListener {
|
||||
|
||||
void beforeWrite(List<? extends S> items);
|
||||
|
||||
void afterWrite(List<? extends S> items);
|
||||
|
||||
void onWriteError(Exception exception, List<? extends S> items);
|
||||
|
||||
}</programlisting>
|
||||
@@ -924,13 +923,10 @@ itemWriter.write(items);</programlisting>
|
||||
this reason, there is a separate interface for tracking skipped
|
||||
items:</para>
|
||||
|
||||
<programlisting>
|
||||
public interface SkipListener<T,S> extends StepListener {
|
||||
<programlisting language="java">public interface SkipListener<T,S> extends StepListener {
|
||||
|
||||
void onSkipInRead(Throwable t);
|
||||
|
||||
void onSkipInProcess(T item, Throwable t);
|
||||
|
||||
void onSkipInWrite(S item, Throwable t);
|
||||
|
||||
}</programlisting>
|
||||
@@ -1011,8 +1007,8 @@ itemWriter.write(items);</programlisting>
|
||||
<classname>Tasklet</classname> object; no <chunk/> element should be
|
||||
used within the <tasklet/>:</para>
|
||||
|
||||
<programlisting><step id="step1">
|
||||
<tasklet <classname>ref="myTasklet"</classname>/>
|
||||
<programlisting language="xml"><step id="step1">
|
||||
<tasklet ref="myTasklet"/>
|
||||
</step></programlisting>
|
||||
|
||||
<note>
|
||||
@@ -1034,7 +1030,7 @@ itemWriter.write(items);</programlisting>
|
||||
this class without having to write an adapter for the
|
||||
<classname>Tasklet</classname> interface:</para>
|
||||
|
||||
<programlisting><bean id="myTasklet" class="org.springframework.batch.core.step.tasklet.MethodInvokingTaskletAdapter">
|
||||
<programlisting language="xml"><bean id="myTasklet" class="o.s.b.core.step.tasklet.MethodInvokingTaskletAdapter">
|
||||
<property name="targetObject">
|
||||
<bean class="org.mycompany.FooDao"/>
|
||||
</property>
|
||||
@@ -1054,7 +1050,7 @@ itemWriter.write(items);</programlisting>
|
||||
project, is a <classname>Tasklet</classname> implementation with just
|
||||
such a responsibility:</para>
|
||||
|
||||
<programlisting>public class FileDeletingTasklet implements Tasklet, InitializingBean {
|
||||
<programlisting language="java">public class FileDeletingTasklet implements Tasklet, InitializingBean {
|
||||
|
||||
private Resource directory;
|
||||
|
||||
@@ -1089,7 +1085,7 @@ itemWriter.write(items);</programlisting>
|
||||
that is left is to reference the <classname>Tasklet</classname> from the
|
||||
<classname>Step</classname>:</para>
|
||||
|
||||
<programlisting><job id="taskletJob">
|
||||
<programlisting language="xml"><job id="taskletJob">
|
||||
<step id="deleteFilesInDir">
|
||||
<tasklet ref="fileDeletingTasklet"/>
|
||||
</step>
|
||||
@@ -1140,11 +1136,13 @@ itemWriter.write(items);</programlisting>
|
||||
<para>This can be achieved using the 'next' attribute of the step
|
||||
element:</para>
|
||||
|
||||
<para><programlisting><job id="job">
|
||||
<programlisting language="xml"><job id="job">
|
||||
<step id="stepA" parent="s1" next="stepB" />
|
||||
<step id="stepB" parent="s2" next="stepC"/>
|
||||
<step id="stepC" parent="s3" />
|
||||
</job></programlisting>In the scenario above, 'step A' will execute
|
||||
</job></programlisting>
|
||||
|
||||
<para>In the scenario above, 'step A' will execute
|
||||
first because it is the first <classname>Step</classname> listed. If
|
||||
'step A' completes normally, then 'step B' will execute, and so on.
|
||||
However, if 'step A' fails, then the entire <classname>Job</classname>
|
||||
@@ -1206,14 +1204,14 @@ itemWriter.write(items);</programlisting>
|
||||
<para>The next element specifies a pattern to match and the step to
|
||||
execute next:</para>
|
||||
|
||||
<para><programlisting><job id="job">
|
||||
<programlisting language="xml"><job id="job">
|
||||
<step id="stepA" parent="s1">
|
||||
<next on="*" to="stepB" />
|
||||
<next on="FAILED" to="stepC" />
|
||||
</step>
|
||||
<step id="stepB" parent="s2" next="stepC" />
|
||||
<step id="stepC" parent="s3" />
|
||||
</job></programlisting></para>
|
||||
</job></programlisting>
|
||||
|
||||
<para>The "on" attribute of a transition element uses a simple
|
||||
pattern-matching scheme to match the <classname>ExitStatus</classname>
|
||||
@@ -1261,7 +1259,7 @@ itemWriter.write(items);</programlisting>
|
||||
it fails, and so on. The example above contains the following 'next'
|
||||
element:</para>
|
||||
|
||||
<programlisting><next on="FAILED" to="stepB" /></programlisting>
|
||||
<programlisting language="xml"><next on="FAILED" to="stepB" /></programlisting>
|
||||
|
||||
<para>At first glance, it would appear that the 'on' attribute
|
||||
references the <classname>BatchStatus</classname> of the
|
||||
@@ -1278,7 +1276,7 @@ itemWriter.write(items);</programlisting>
|
||||
code needs to be different? A good example comes from the skip sample
|
||||
job within the samples project:</para>
|
||||
|
||||
<programlisting><step id="step1" parent="s1">
|
||||
<programlisting language="xml"><step id="step1" parent="s1">
|
||||
<end on="FAILED" />
|
||||
<next on="COMPLETED WITH SKIPS" to="errorPrint1" />
|
||||
<next on="*" to="step2" />
|
||||
@@ -1308,8 +1306,7 @@ itemWriter.write(items);</programlisting>
|
||||
change the exit code based on the condition of the execution having
|
||||
skipped records:</para>
|
||||
|
||||
<programlisting>public class SkipCheckingListener extends StepExecutionListenerSupport {
|
||||
|
||||
<programlisting language="java">public class SkipCheckingListener extends StepExecutionListenerSupport {
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
String exitCode = stepExecution.getExitStatus().getExitCode();
|
||||
if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
|
||||
@@ -1320,7 +1317,6 @@ itemWriter.write(items);</programlisting>
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>The above code is a <classname>StepExecutionListener</classname>
|
||||
@@ -1349,7 +1345,7 @@ itemWriter.write(items);</programlisting>
|
||||
after the following step executes, the <classname>Job</classname> will
|
||||
end:</para>
|
||||
|
||||
<para><programlisting><step id="stepC" parent="s3"/></programlisting></para>
|
||||
<programlisting language="xml"><step id="stepC" parent="s3"/></programlisting>
|
||||
|
||||
<para>If no transitions are defined for a <classname>Step</classname>,
|
||||
then the <classname>Job</classname>'s statuses will be defined as
|
||||
@@ -1408,7 +1404,7 @@ itemWriter.write(items);</programlisting>
|
||||
fails, the <classname>Job</classname> will not be restartable (because
|
||||
the status is COMPLETED).</para>
|
||||
|
||||
<programlisting><step id="step1" parent="s1" next="step2">
|
||||
<programlisting language="xml"><step id="step1" parent="s1" next="step2">
|
||||
|
||||
<step id="step2" parent="s2">
|
||||
<end on="FAILED"/>
|
||||
@@ -1439,7 +1435,7 @@ itemWriter.write(items);</programlisting>
|
||||
Additionally, if step2 fails, and the <classname>Job</classname> is
|
||||
restarted, then execution will begin again on step2.</para>
|
||||
|
||||
<programlisting><step id="step1" parent="s1" next="step2">
|
||||
<programlisting language="xml"><step id="step1" parent="s1" next="step2">
|
||||
|
||||
<step id="step2" parent="s2">
|
||||
<fail on="FAILED" exit-code="EARLY TERMINATION"/>
|
||||
@@ -1464,11 +1460,11 @@ itemWriter.write(items);</programlisting>
|
||||
the job will then stop. Once it is restarted, execution will begin on
|
||||
step2.</para>
|
||||
|
||||
<para><programlisting><step id="step1" parent="s1">
|
||||
<programlisting language="xml"><step id="step1" parent="s1">
|
||||
<stop on="COMPLETED" restart="step2"/>
|
||||
</step>
|
||||
|
||||
<step id="step2" parent="s2"/></programlisting></para>
|
||||
<step id="step2" parent="s2"/></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -1481,7 +1477,7 @@ itemWriter.write(items);</programlisting>
|
||||
<classname>JobExecutionDecider</classname> can be used to assist in the
|
||||
decision.</para>
|
||||
|
||||
<para><programlisting>public class MyDecider implements JobExecutionDecider {
|
||||
<programlisting language="java">public class MyDecider implements JobExecutionDecider {
|
||||
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
|
||||
if (someCondition) {
|
||||
return "FAILED";
|
||||
@@ -1490,12 +1486,12 @@ itemWriter.write(items);</programlisting>
|
||||
return "COMPLETED";
|
||||
}
|
||||
}
|
||||
}</programlisting></para>
|
||||
}</programlisting>
|
||||
|
||||
<para>In the job configuration, a "decision" tag will specify the
|
||||
decider to use as well as all of the transitions.</para>
|
||||
|
||||
<para><programlisting><job id="job">
|
||||
<programlisting language="xml"><job id="job">
|
||||
<step id="step1" parent="s1" next="decision" />
|
||||
|
||||
<decision id="decision" decider="decider">
|
||||
@@ -1507,7 +1503,7 @@ itemWriter.write(items);</programlisting>
|
||||
<step id="step3" parent="s3" />
|
||||
</job>
|
||||
|
||||
<beans:bean id="decider" class="com.MyDecider"/></programlisting></para>
|
||||
<beans:bean id="decider" class="com.MyDecider"/></programlisting>
|
||||
</section>
|
||||
|
||||
<section id="split-flows">
|
||||
@@ -1524,7 +1520,7 @@ itemWriter.write(items);</programlisting>
|
||||
elements such as the 'next' attribute or the 'next', 'end', 'fail', or
|
||||
'pause' elements.</para>
|
||||
|
||||
<programlisting><split id="split1" next="step4">
|
||||
<programlisting language="xml"><split id="split1" next="step4">
|
||||
<flow>
|
||||
<step id="step1" parent="s1" next="step2"/>
|
||||
<step id="step2" parent="s2"/>
|
||||
@@ -1545,7 +1541,7 @@ itemWriter.write(items);</programlisting>
|
||||
first is to simply declare the flow as a reference to one defined
|
||||
elsewhere:</para>
|
||||
|
||||
<programlisting><job id="job">
|
||||
<programlisting language="xml"><job id="job">
|
||||
<flow id="job1.flow1" parent="flow1" next="step3"/>
|
||||
<step id="step3" parent="s3"/>
|
||||
</job>
|
||||
@@ -1568,7 +1564,7 @@ itemWriter.write(items);</programlisting>
|
||||
launches a separate job execution for the steps in the flow specified.
|
||||
Here is an example:</para>
|
||||
|
||||
<para><programlisting><job id="jobStepJob" restartable="true">
|
||||
<programlisting language="xml"><job id="jobStepJob" restartable="true">
|
||||
<step id="jobStepJob.step1">
|
||||
<job ref="<emphasis role="bold">job</emphasis>" job-launcher="jobLauncher"
|
||||
job-parameters-extractor="jobParametersExtractor"/>
|
||||
@@ -1579,7 +1575,7 @@ itemWriter.write(items);</programlisting>
|
||||
|
||||
<bean id="jobParametersExtractor" class="org.spr...DefaultJobParametersExtractor">
|
||||
<property name="keys" value="input.file"/>
|
||||
</bean></programlisting></para>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>The job parameters extractor is a strategy that determines how a
|
||||
the <classname>ExecutionContext</classname> for the
|
||||
@@ -1604,7 +1600,7 @@ itemWriter.write(items);</programlisting>
|
||||
Flat File resources can be configured using standard Spring
|
||||
constructs:</para>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader"
|
||||
<programlisting language="xml"><bean id="flatFileItemReader"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource"
|
||||
value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" />
|
||||
@@ -1618,7 +1614,7 @@ itemWriter.write(items);</programlisting>
|
||||
at runtime as a parameter to the job. This could be solved using '-D'
|
||||
parameters, i.e. a system property:</para>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader"
|
||||
<programlisting language="xml"><bean id="flatFileItemReader"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="${input.file.name}" />
|
||||
</bean></programlisting>
|
||||
@@ -1637,7 +1633,7 @@ itemWriter.write(items);</programlisting>
|
||||
accomplish this, Spring Batch allows for the late binding of various Job
|
||||
and Step attributes:</para>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader" scope="step"
|
||||
<programlisting language="xml"><bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{jobParameters['input.file.name']}</emphasis>" />
|
||||
</bean></programlisting>
|
||||
@@ -1647,12 +1643,12 @@ itemWriter.write(items);</programlisting>
|
||||
<classname>ExecutionContext</classname> can be accessed in the same
|
||||
way:</para>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader" scope="step"
|
||||
<programlisting language="xml"><bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{jobExecutionContext['input.file.name']}</emphasis>" />
|
||||
</bean></programlisting>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader" scope="step"
|
||||
<programlisting language="xml"><bean id="flatFileItemReader" scope="step"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="<emphasis role="bold">#{stepExecutionContext['input.file.name']}</emphasis>" />
|
||||
</bean></programlisting>
|
||||
@@ -1680,7 +1676,7 @@ itemWriter.write(items);</programlisting>
|
||||
<para>All of the late binding examples from above have a scope of "step"
|
||||
declared on the bean definition:</para>
|
||||
|
||||
<programlisting><bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
|
||||
<programlisting language="xml"><bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="#{jobParameters[input.file.name]}" />
|
||||
</bean></programlisting>
|
||||
@@ -1692,7 +1688,7 @@ itemWriter.write(items);</programlisting>
|
||||
scope must be added explicitly, either by using the
|
||||
<literal>batch</literal> namespace:</para>
|
||||
|
||||
<programlisting><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
<programlisting language="xml"><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="...">
|
||||
@@ -1703,7 +1699,7 @@ itemWriter.write(items);</programlisting>
|
||||
<para>or by including a bean definition explicitly for the<classname>
|
||||
StepScope</classname> (but not both):</para>
|
||||
|
||||
<programlisting><bean class="org.springframework.batch.core.scope.StepScope" /></programlisting>
|
||||
<programlisting language="xml"><bean class="org.springframework.batch.core.scope.StepScope" /></programlisting>
|
||||
</section>
|
||||
|
||||
<section id="job-scope">
|
||||
@@ -1715,20 +1711,20 @@ itemWriter.write(items);</programlisting>
|
||||
for late binding of references 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. E.g.
|
||||
|
||||
<programlisting><bean id="..." class="..." <emphasis role="bold">scope="job"</emphasis>>
|
||||
</para>
|
||||
<programlisting language="xml"><bean id="..." class="..." <emphasis role="bold">scope="job"</emphasis>>
|
||||
<property name="name" value="#{jobParameters[input]}" />
|
||||
</bean>
|
||||
</programlisting>
|
||||
<programlisting><bean id="..." class="..." <emphasis role="bold">scope="job"</emphasis>>
|
||||
<programlisting language="xml"><bean id="..." class="..." <emphasis role="bold">scope="job"</emphasis>>
|
||||
<property name="name" value="#{jobExecutionContext['input.name']}.txt" />
|
||||
</bean>
|
||||
</programlisting></para>
|
||||
</programlisting>
|
||||
|
||||
<para>Because it is not part of the Spring container by default, the scope
|
||||
must be added explicitly, either by using the <literal>batch</literal> namespace:</para>
|
||||
|
||||
<programlisting><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
<programlisting language="xml"><beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:batch="http://www.springframework.org/schema/batch"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="...">
|
||||
@@ -1739,7 +1735,7 @@ itemWriter.write(items);</programlisting>
|
||||
|
||||
<para>Or by including a bean definition explicitly for the <classname>JobScope</classname> (but not both):</para>
|
||||
|
||||
<programlisting><bean class="org.springframework.batch.core.scope.JobScope" /></programlisting>
|
||||
<programlisting language="xml"><bean class="org.springframework.batch.core.scope.JobScope" /></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
||||
"/jobs/skipSampleJob.xml" })
|
||||
public class SkipSampleFunctionalTests extends AbstractJobTests { ... }</programlisting>
|
||||
</section>
|
||||
@@ -59,8 +59,8 @@ public class SkipSampleFunctionalTests extends AbstractJobTests { ... }</program
|
||||
case below, the test verifies that the <classname>Job</classname> ended
|
||||
with status "COMPLETED".</para>
|
||||
|
||||
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
||||
<programlisting language="java">@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
||||
"/jobs/skipSampleJob.xml" })
|
||||
public class SkipSampleFunctionalTests {
|
||||
|
||||
@@ -78,7 +78,7 @@ public class SkipSampleFunctionalTests {
|
||||
public void testJob() throws Exception {
|
||||
simpleJdbcTemplate.update("delete from CUSTOMER");
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
|
||||
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
|
||||
i, "customer" + i);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class SkipSampleFunctionalTests {
|
||||
targeted tests by allowing the test to set up data for just that step and
|
||||
to validate its results directly.</para>
|
||||
|
||||
<programlisting>JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");</programlisting>
|
||||
<programlisting language="java">JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");</programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -119,8 +119,8 @@ public class SkipSampleFunctionalTests {
|
||||
<para>The listener is declared at the class level, and its job is to
|
||||
create a step execution context for each test method. For example:</para>
|
||||
|
||||
<programlisting>@ContextConfiguration
|
||||
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
|
||||
<programlisting language="java">@ContextConfiguration
|
||||
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
|
||||
StepScopeTestExecutionListener.class })
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class StepScopeTestExecutionListenerIntegrationTests {
|
||||
@@ -141,7 +141,7 @@ public class StepScopeTestExecutionListenerIntegrationTests {
|
||||
// The reader is initialized and bound to the input data
|
||||
assertNotNull(reader.read());
|
||||
}
|
||||
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>There are two <classname>TestExecutionListeners</classname>, one
|
||||
@@ -162,9 +162,8 @@ public class StepScopeTestExecutionListenerIntegrationTests {
|
||||
<classname>StepScopeTestUtils</classname>. For example, to count the
|
||||
number of items available in the reader above:</para>
|
||||
|
||||
<programlisting>int count = StepScopeTestUtils.doInStepScope(stepExecution,
|
||||
<programlisting language="java">int count = StepScopeTestUtils.doInStepScope(stepExecution,
|
||||
new Callable<Integer>() {
|
||||
|
||||
public Integer call() throws Exception {
|
||||
|
||||
int count = 0;
|
||||
@@ -172,9 +171,7 @@ public class StepScopeTestExecutionListenerIntegrationTests {
|
||||
while (reader.read() != null) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
});</programlisting>
|
||||
</section>
|
||||
@@ -194,10 +191,10 @@ public class StepScopeTestExecutionListenerIntegrationTests {
|
||||
file with the expected output and to compare it to the actual
|
||||
result:</para>
|
||||
|
||||
<programlisting>private static final String EXPECTED_FILE = "src/main/resources/data/input.txt";
|
||||
<programlisting language="java">private static final String EXPECTED_FILE = "src/main/resources/data/input.txt";
|
||||
private static final String OUTPUT_FILE = "target/test-outputs/output.txt";
|
||||
|
||||
AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
|
||||
AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
|
||||
new FileSystemResource(OUTPUT_FILE));</programlisting>
|
||||
</section>
|
||||
|
||||
@@ -209,9 +206,9 @@ AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
|
||||
example is a <classname>StepExecutionListener</classname>, as illustrated
|
||||
below:</para>
|
||||
|
||||
<programlisting>public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
|
||||
<programlisting language="java">public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
if (stepExecution.getReadCount() == 0) {
|
||||
throw new NoWorkFoundException("Step has not processed any items");
|
||||
}
|
||||
@@ -226,12 +223,12 @@ AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
|
||||
attempting to unit test classes that implement interfaces requiring Spring
|
||||
Batch domain objects. Consider the above listener's unit test:</para>
|
||||
|
||||
<programlisting>private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
|
||||
<programlisting language="java">private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
|
||||
|
||||
@Test
|
||||
public void testAfterStep() {
|
||||
<emphasis role="bold">StepExecution stepExecution = new StepExecution("NoProcessingStep",
|
||||
new JobExecution(new JobInstance(1L, new JobParameters(),
|
||||
new JobExecution(new JobInstance(1L, new JobParameters(),
|
||||
"NoProcessingJob")));</emphasis>
|
||||
|
||||
stepExecution.setReadCount(0);
|
||||
@@ -256,7 +253,7 @@ public void testAfterStep() {
|
||||
Given this factory, the unit test can be updated to be more
|
||||
concise:</para>
|
||||
|
||||
<programlisting>private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
|
||||
<programlisting language="java">private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
|
||||
|
||||
@Test
|
||||
public void testAfterStep() {
|
||||
|
||||
Reference in New Issue
Block a user