BATCH-1270: Update documentation for formatting consistency

This commit is contained in:
dhgarrette
2009-06-05 23:45:59 +00:00
parent 9a7bdaa775
commit b716c94eba
12 changed files with 352 additions and 390 deletions

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<appendix>
<title>List of ItemReaders</title>
<appendix id="listOfReadersAndWriters">
<title>List of ItemReaders and ItemWriters</title>
<section>
<section id="itemReadersAppendix">
<title>Item Readers</title>
<table>
@@ -124,7 +124,7 @@
</table>
</section>
<section>
<section id="itemWritersAppendix">
<title>Item Writers</title>
<table>
@@ -194,7 +194,7 @@
<row>
<entry align="left">JdbcBatchItemWriter</entry>
<entry align="left">Uses batching freatures from a
<entry align="left">Uses batching features from a
<classname>PreparedStatement</classname>, if available, and can
take rudimentary steps to locate a failure during a
<methodname>flush</methodname>.</entry>

View File

@@ -24,7 +24,7 @@
<classname>ItemWriter</classname> can implement a listener interface as
well, if appropriate.</para>
<section>
<section id="loggingItemProcessingAndFailures">
<title>Logging Item Processing and Failures</title>
<para>A common use case is the need for special handling of errors in a
@@ -53,13 +53,12 @@
<para>Having implemented this listener it must be registered with the
step:</para>
<programlisting>&lt;bean id="simpleStep"
class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" &gt;
<programlisting>&lt;step id="simpleStep"&gt;
...
&lt;property name="listeners"&gt;
&lt;bean class="org.example...ItemFailureLoggerListener"/&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
&lt;listeners&gt;
&lt;listener class="org.example...ItemFailureLoggerListener"/&gt;
&lt;/listeners&gt;
&lt;/step&gt;</programlisting>
<para>Remember that if your listener does anything in an
<code>onError()</code> method, it will be inside a transaction that is
@@ -70,7 +69,7 @@
REQUIRES_NEW.</para>
</section>
<section>
<section id="stoppingAJobManuallyForBusinessReasons">
<title>Stopping a Job Manually for Business Reasons</title>
<para>Spring Batch provides a <methodname>stop</methodname>() method
@@ -87,11 +86,9 @@
<programlisting>public class PoisonPillItemWriter implements ItemWriter&lt;T&gt; {
public void write(T item) throws Exception {
if (isPoisonPill(item)) {
throw new PoisonPillException("Posion pill detected: "+item);
throw new PoisonPillException("Posion pill detected: " + item);
}
}
}</programlisting>
@@ -106,15 +103,11 @@
public void setDelegate(ItemReader&lt;T&gt; delegate) { ... }
public T read() throws Exception {
T item = delegate.read();
if (isEndItem(item)) {
return null; // end the step here
}
return item;
}
}</programlisting>
@@ -126,13 +119,14 @@
injected into the <classname>Step</classname> through the
<classname>SimpleStepFactoryBean</classname>:</para>
<programlisting>&lt;bean id="simpleStep"
class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" &gt;
...
&lt;property name="chunkCompletionPolicy"&gt;
&lt;bean class="org.example...SpecialCompletionPolicy"/&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
<programlisting>&lt;step id="simpleStep"&gt;
&lt;tasklet&gt;
&lt;chunk reader="reader" writer="writer" commit-interval="10"
<emphasis role="bold">chunk-completion-policy="completionPolicy"</emphasis>/&gt;
&lt;/tasklet&gt;
&lt;/step&gt;
&lt;bean id="completionPolicy" class="org.example...SpecialCompletionPolicy"/&gt;</programlisting>
<para>An alternative is to set a flag in the
<classname>StepExecution</classname>, which is checked by the
@@ -166,7 +160,7 @@
an abnormal ending to a job.</para>
</section>
<section>
<section id="addingAFooterRecord">
<title>Adding a Footer Record</title>
<para>Often when writing to flat files, a "footer" record must be appended
@@ -177,25 +171,23 @@
<classname>FlatFileHeaderCallback</classname>) are optional properties of
the <classname>FlatFileItemWriter</classname>:</para>
<programlisting> &lt;bean id="itemWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
<programlisting>&lt;bean id="itemWriter" class="org.spr...FlatFileItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="lineAggregator" ref="lineAggregator"/&gt;
<emphasis role="bold">&lt;property name="headerCallback" ref="headerCallback" /&gt;</emphasis>
<emphasis role="bold">&lt;property name="footerCallback" ref="footerCallback" /&gt;</emphasis>
&lt;/bean&gt;
</programlisting>
&lt;/bean&gt;</programlisting>
<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>public interface FlatFileFooterCallback {
void writeFooter(Writer writer) throws IOException;
void writeFooter(Writer writer) throws IOException;
}</programlisting>
}</programlisting>
<section>
<section id="writingASummaryFooter">
<title>Writing a Summary Footer</title>
<para>A very common requirement involving footer records is to aggregate
@@ -208,10 +200,11 @@
Trades is placed in a footer, then the following ItemWriter
implementation can be used:</para>
<programlisting> public class TradeItemWriter implements ItemWriter&lt;Trade&gt;,
FlatFileFooterCallback {
<programlisting>public class TradeItemWriter implements ItemWriter&lt;Trade&gt;,
FlatFileFooterCallback {
private ItemWriter&lt;Trade&gt; delegate;
private double totalAmount = 0.0;
public void write(List&lt;? extends Trade&gt; items) {
@@ -226,7 +219,7 @@
}
public void setDelegate(ItemWriter delegate) {...}
}</programlisting>
}</programlisting>
<para>This <classname>TradeItemWriter</classname> stores a
<code>totalAmount</code> value that is increased with the
@@ -238,17 +231,15 @@
the <classname>FlatFileItemWriter</classname> as the
<code>footerCallback</code>:</para>
<programlisting> &lt;bean id="tradeItemWriter" class="..TradeItemWriter"&gt;
&lt;property name="delegate" ref="flatFileItemWriter" /&gt;
&lt;/bean&gt;
<programlisting>&lt;bean id="tradeItemWriter" class="..TradeItemWriter"&gt;
&lt;property name="delegate" ref="flatFileItemWriter" /&gt;
&lt;/bean&gt;
&lt;bean id="flatFileItemWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="lineAggregator" ref="lineAggregator"/&gt;
<emphasis role="bold">&lt;property name="footerCallback" ref="tradeItemWriter" /&gt;</emphasis>
&lt;/bean&gt;
</programlisting>
&lt;bean id="flatFileItemWriter" class="org.spr...FlatFileItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="lineAggregator" ref="lineAggregator"/&gt;
<emphasis role="bold">&lt;property name="footerCallback" ref="tradeItemWriter" /&gt;</emphasis>
&lt;/bean&gt;</programlisting>
<para>The way that the <classname>TradeItemWriter</classname> has been
so far will only function correctly if the <classname>Step</classname>
@@ -290,7 +281,7 @@
</section>
</section>
<section>
<section id="drivingQueryBasedItemReaders">
<title>Driving Query Based ItemReaders</title>
<para>In the chapter on readers and writers, database input using paging
@@ -347,10 +338,10 @@
spanning multiple lines with multiple formats. The following excerpt from
a file illustrates this:</para>
<programlisting> HEA;0013100345;2007-02-15
NCU;Smith;Peter;;T;20014539;F
BAD;;Oak Street 31/A;;Small Town;00235;IL;US
FOT;2;2;267.34</programlisting>
<programlisting>HEA;0013100345;2007-02-15
NCU;Smith;Peter;;T;20014539;F
BAD;;Oak Street 31/A;;Small Town;00235;IL;US
FOT;2;2;267.34</programlisting>
<para>Everything between the line starting with 'HEA' and the line
starting with 'FOT' is considered one record. There are a few
@@ -376,22 +367,21 @@
<classname>ItemReader</classname> should be implemented as a wrapper for
the <classname>FlatFileItemReader</classname>.</para>
<programlisting> &lt;bean id="itemReader"
class="org.springframework.batch.sample.iosample.internal.MultiLineTradeItemReader"&gt;
<programlisting>&lt;bean id="itemReader" class="org.spr...MultiLineTradeItemReader"&gt;
&lt;property name="delegate"&gt;
&lt;bean class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="data/iosample/input/multiLine.txt" /&gt;
&lt;property name="lineMapper"&gt;
&lt;bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"&gt;
&lt;property name="lineTokenizer" ref="orderFileTokenizer"/&gt;
&lt;property name="fieldSetMapper"&gt;
&lt;bean class="org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper" /&gt;
&lt;bean class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="data/iosample/input/multiLine.txt" /&gt;
&lt;property name="lineMapper"&gt;
&lt;bean class="org.spr...DefaultLineMapper"&gt;
&lt;property name="lineTokenizer" ref="orderFileTokenizer"/&gt;
&lt;property name="fieldSetMapper"&gt;
&lt;bean class="org.spr...PassThroughFieldSetMapper" /&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;/bean&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;</programlisting>
<para>To ensure that each line is tokenized properly, which is especially
important for fixed length input, the
@@ -402,17 +392,16 @@
deliver a <classname>FieldSet</classname> for each line back to the
wrapping <classname>ItemReader</classname>.</para>
<programlisting> &lt;bean id="orderFileTokenizer"
class="org.springframework.batch.io.file.transform.PatternMatchingCompositeLineTokenizer"&gt;
<programlisting>&lt;bean id="orderFileTokenizer" class="org.spr...PatternMatchingCompositeLineTokenizer"&gt;
&lt;property name="tokenizers"&gt;
&lt;map&gt;
&lt;entry key="HEA*" value-ref="headerRecordTokenizer" /&gt;
&lt;entry key="FOT*" value-ref="footerRecordTokenizer" /&gt;
&lt;entry key="NCU*" value-ref="customerLineTokenizer" /&gt;
&lt;entry key="BAD*" value-ref="billingAddressLineTokenizer" /&gt;
&lt;/map&gt;
&lt;map&gt;
&lt;entry key="HEA*" value-ref="headerRecordTokenizer" /&gt;
&lt;entry key="FOT*" value-ref="footerRecordTokenizer" /&gt;
&lt;entry key="NCU*" value-ref="customerLineTokenizer" /&gt;
&lt;entry key="BAD*" value-ref="billingAddressLineTokenizer" /&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;</programlisting>
<para>This wrapper will have to be able recognize the end of a record so
that it can continually call <methodname>read()</methodname> on its
@@ -422,38 +411,38 @@
<classname>ItemProcessor</classname> and
<classname>ItemWriter</classname>.</para>
<programlisting> private FlatFileItemReader&lt;FieldSet&gt; delegate;
<programlisting>private FlatFileItemReader&lt;FieldSet&gt; delegate;
public Trade read() throws Exception {
public Trade read() throws Exception {
Trade t = null;
for (FieldSet line = null; (line = this.delegate.read()) != null;) {
String prefix = line.readString(0);
if (prefix.equals("HEA")) {
t = new Trade(); // Record must start with header
}
else if (prefix.equals("NCU")) {
Assert.notNull(t, "No header was found.");
t.setLast(line.readString(1));
t.setFirst(line.readString(2));
...
}
else if (prefix.equals("BAD")) {
Assert.notNull(t, "No header was found.");
t.setCity(line.readString(4));
t.setState(line.readString(6));
...
}
else if (prefix.equals("FOT")) {
return t; // Record must end with footer
}
String prefix = line.readString(0);
if (prefix.equals("HEA")) {
t = new Trade(); // Record must start with header
}
else if (prefix.equals("NCU")) {
Assert.notNull(t, "No header was found.");
t.setLast(line.readString(1));
t.setFirst(line.readString(2));
...
}
else if (prefix.equals("BAD")) {
Assert.notNull(t, "No header was found.");
t.setCity(line.readString(4));
t.setState(line.readString(6));
...
}
else if (prefix.equals("FOT")) {
return t; // Record must end with footer
}
}
Assert.isNull(t, "No 'END' was found.");
return null;
}</programlisting>
}</programlisting>
</section>
<section>
<section id="executingSystemCommands">
<title>Executing System Commands</title>
<para>Many batch jobs may require that an external command be called from
@@ -466,15 +455,15 @@
<classname>Tasklet</classname> implementation for calling system
commands:</para>
<programlisting> &lt;bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet"&gt;
<programlisting>&lt;bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet"&gt;
&lt;property name="command" value="echo hello" /&gt;
&lt;!-- 5 second timeout for the command to complete --&gt;
&lt;property name="timeout" value="5000" /&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;</programlisting>
</section>
<section>
<title>Handling Step completion when no input is found</title>
<section id="handlingStepCompletionWhenNoInputIsFound">
<title>Handling Step Completion When No Input is Found</title>
<para>In many batch scenarios, finding no rows in a database or file to
process is not exceptional. The <classname>Step</classname> is simply
@@ -490,17 +479,16 @@
a common use case, a listener is provided with just this
functionality:</para>
<programlisting>
public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
<programlisting>public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
return ExitStatus.FAILED;
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
return ExitStatus.FAILED;
}
return null;
}
return null;
}
</programlisting>
}</programlisting>
<para>The above <classname>StepExecutionListener</classname> inspects the
readCount property of the <classname>StepExecution</classname> during the
@@ -511,8 +499,8 @@
<classname>Step</classname>.</para>
</section>
<section>
<title>Passing data to future steps</title>
<section id="passingDataToFutureSteps">
<title>Passing Data to Future Steps</title>
<para>It is often useful to pass information from one step to another.
This can be done using the <classname>ExecutionContext</classname>. The
@@ -580,8 +568,7 @@
&lt;/step&gt;
&lt;/job&gt;
<emphasis role="bold">&lt;beans:bean id="promotionListener"
class="org.spr....ExecutionContextPromotionListener"&gt;
<emphasis role="bold">&lt;beans:bean id="promotionListener" class="org.spr....ExecutionContextPromotionListener"&gt;
&lt;beans:property name="keys" value="someKey"/&gt;
&lt;/beans:bean&gt;</emphasis></programlisting>
@@ -597,7 +584,8 @@
@BeforeStep
public void saveStepExecution(StepExecution stepExecution) {
ExecutionContext jobContext = stepExecution.getJobExecution().getExecutionContext();
JobExecution jobExecution = stepExecution.getJobExecution();
ExecutionContext jobContext = jobExecution.getExecutionContext();
this.someObject = jobContext.get("someKey");
}
}</programlisting>

View File

@@ -218,7 +218,7 @@
controlled and persisted:</para>
<table>
<title>JobExecution properties</title>
<title>JobExecution Properties</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="*" />
@@ -592,7 +592,7 @@
<classname>StepExecution</classname>:</para>
<table>
<title>StepExecution properties</title>
<title>StepExecution Properties</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="*" />

View File

@@ -2,7 +2,7 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="configureJob">
<title>Configuring and Running A Job</title>
<title>Configuring and Running a Job</title>
<para>In <xref linkend="domain" />, the overall architecture design was
discussed, using the following diagram as a guide:</para>
@@ -27,7 +27,7 @@
stored during that run. This chapter will explain the various configuration
options and runtime concerns of a <classname>Job</classname>.</para>
<section>
<section id="configuringAJob">
<title>Configuring a Job</title>
<para>There are multiple implementations of the <link
@@ -52,10 +52,10 @@
&lt;step id="playerSummarization" parent="s3"/&gt;
&lt;/job&gt;</programlisting>
<section>
<section id="restartability">
<title>Restartability</title>
<para>One key issue when execution a batch job concerns the behavior of
<para>One key issue when executing a batch job concerns the behavior of
a <classname>Job</classname> when it is restarted? The launching of a
<classname>Job</classname> is considered to be a 'restart' if a
<classname>JobExecution</classname> already exists for the particular
@@ -99,8 +99,8 @@ catch (JobRestartException e) {
attempt will throw a <classname>JobRestartException</classname>.</para>
</section>
<section>
<title>Intercepting Job execution</title>
<section id="interceptingJobExecution">
<title>Intercepting Job Execution</title>
<para>During the course of the execution of a
<classname>Job</classname>, it may be useful to be notified of various
@@ -156,7 +156,7 @@ catch (JobRestartException e) {
</itemizedlist>
</section>
<section>
<section id="inheritingFromAParentJob">
<title>Inheriting from a Parent Job</title>
<para>If a group of <classname>Job</classname>s share similar, but not
@@ -193,7 +193,7 @@ catch (JobRestartException e) {
for more detailed information.</para>
</section>
<section>
<section id="jobFactoryAndStatefulComponentsInSteps">
<title>JobFactory and Stateful Components in Steps</title>
<para>Unlike many traditional Spring applications, many of the
@@ -221,7 +221,7 @@ catch (JobRestartException e) {
</section>
</section>
<section>
<section id="configuringJobRepository">
<title>Configuring a JobRepository</title>
<para>As described in earlier, the <link
@@ -239,7 +239,7 @@ catch (JobRestartException e) {
<programlisting>&lt;job-repository id="jobRepository"
dataSource="dataSource"
transactionManager="transactionManager"
isolation-level-for-create="serializable"
isolation-level-for-create="SERIALIZABLE"
table-prefix="BATCH_"
/&gt;</programlisting>
@@ -247,8 +247,8 @@ catch (JobRestartException e) {
the id. If they are not set, the defaults shown above will be used. They
are shown above for awareness purposes.</para>
<section>
<title>Transaction Configuration For the JobRepository</title>
<section id="txConfigForJobRepository">
<title>Transaction Configuration for the JobRepository</title>
<para>If the namespace is used, transactional advice will be
automatically created around the repository. This is to ensure that the
@@ -292,7 +292,7 @@ catch (JobRestartException e) {
</section>
<section id="repositoryTablePrefix">
<title>Changing the table prefix</title>
<title>Changing the Table Prefix</title>
<para>Another modifiable property of the
<classname>JobRepository</classname> is the table prefix of the
@@ -316,7 +316,7 @@ catch (JobRestartException e) {
</note>
</section>
<section>
<section id="inMemoryRepository">
<title>In-Memory Repository</title>
<para>There are scenarios in which you may not want to persist your
@@ -344,7 +344,7 @@ catch (JobRestartException e) {
<classname>ResourcelessTransactionManager</classname> useful.</para>
</section>
<section>
<section id="nonStandardDatabaseTypesInRepository">
<title>Non-standard Database Types in a Repository</title>
<para>If you are using a database platform that is not in the list of
@@ -447,7 +447,7 @@ catch (JobRestartException e) {
executed.</para>
</section>
<section>
<section id="runningAJob">
<title>Running a Job</title>
<para>At a minimum, launching a batch job requires two things: the
@@ -461,7 +461,7 @@ catch (JobRestartException e) {
<classname>JobLauncher</classname>, configured for asynchronous job
launching, that multiple requests will invoke to launch their jobs.</para>
<section>
<section id="runningJobsFromCommandLine">
<title>Running Jobs from the Command Line</title>
<para>For users that want to run their jobs from an enterprise
@@ -473,7 +473,7 @@ catch (JobRestartException e) {
even 'build tools' such as ant or maven. However, because most people
are familiar with shell scripts, this example will focus on them.</para>
<section>
<section id="commandLineJobRunner">
<title>The CommandLineJobRunner</title>
<para>Because the script launching the job must kick off a Java
@@ -565,7 +565,7 @@ catch (JobRestartException e) {
<classname>JobLauncher</classname></para>
</section>
<section>
<section id="exitCodes">
<title>ExitCodes</title>
<para>When launching a batch job from the command-line, an enterprise
@@ -623,8 +623,8 @@ catch (JobRestartException e) {
</section>
</section>
<section>
<title>Running Jobs from within a web container</title>
<section id="runningJobsFromWebContainer">
<title>Running Jobs from within a Web Container</title>
<para>Historically, offline processing such as batch jobs have been
launched from the command-line, as described above. However, there are
@@ -719,8 +719,8 @@ public class JobLauncherController {
below, add additional functionality for querying and controlling the meta
data.</para>
<section>
<title>Querying the repository</title>
<section id="queryingRepository">
<title>Querying the Repository</title>
<para>The most basic need before any advanced features is the ability to
query the repository for existing executions. This functionality is
@@ -761,7 +761,7 @@ public class JobLauncherController {
p:dataSource-ref="dataSource" <emphasis role="bold">p:tablePrefix="BATCH_" </emphasis>/&gt;</programlisting>
</section>
<section>
<section id="JobOperator">
<title>JobOperator</title>
<para>As previously discussed, the <classname>JobRepository</classname>
@@ -827,7 +827,7 @@ public class JobLauncherController {
&lt;/bean&gt;</programlisting>
</section>
<section>
<section id="JobParametersIncrementer">
<title>JobParametersIncrementer</title>
<para>Most of the methods on <classname>JobOperator</classname> are
@@ -894,7 +894,7 @@ public class JobLauncherController {
&lt;/job&gt;</programlisting>
</section>
<section>
<section id="stoppingAJob">
<title>Stopping a Job</title>
<para>One of the most common use cases of

View File

@@ -11,7 +11,7 @@
<classname>ItemReader</classname>, <classname>ItemProcessor</classname> and
<classname>ItemWriter</classname>.</para>
<section>
<section id="itemReader">
<title id="infrastructure.1">ItemReader</title>
<para>Although a simple concept, an <classname>ItemReader</classname> is
@@ -70,7 +70,7 @@
the first invocation of <methodname>read</methodname>.</para>
</section>
<section>
<section id="itemWriter">
<title id="infrastructure.1.4">ItemWriter</title>
<para><classname>ItemWriter</classname> is similar in functionality to an
@@ -103,7 +103,7 @@
Session before returning.</para>
</section>
<section>
<section id="itemProcessor">
<title>ItemProcessor</title>
<para>The <classname>ItemReader</classname> and
@@ -200,7 +200,7 @@ public class BarWriter implements ItemWriter&lt;Bar&gt;{
&lt;/step&gt;
&lt;/job&gt;</programlisting>
<section>
<section id="chainingItemProcessors">
<title>Chaining ItemProcessors</title>
<para>Performing a single transformation is useful in many scenarios,
@@ -274,7 +274,7 @@ compositeProcessor.setItemProcessors(itemProcessors);</programlisting>
&lt;/bean&gt;</programlisting>
</section>
<section>
<section id="filiteringRecords">
<title>Filtering Records</title>
<para>One typical use for an item processor is to filter out records
@@ -350,7 +350,7 @@ compositeProcessor.setItemProcessors(itemProcessors);</programlisting>
Quartz <classname>JobDataMap</classname>.</para>
</section>
<section>
<section id="delegatePatternAndRegistering">
<title>The Delegate Pattern and Registering with the Step</title>
<para>Note that the <classname>CompositeItemWriter</classname> is an
@@ -387,7 +387,7 @@ compositeProcessor.setItemProcessors(itemProcessors);</programlisting>
&lt;bean id="barWriter" class="...BarWriter" /&gt;</programlisting>
</section>
<section>
<section id="flatFiles">
<title id="infrastructure.1.2">Flat Files</title>
<para>One of the most common mechanisms for interchanging bulk data has
@@ -398,7 +398,7 @@ compositeProcessor.setItemProcessors(itemProcessors);</programlisting>
files are those in which fields are separated by a delimiter, such as a
comma. Fixed Length files have fields that are a set length.</para>
<section>
<section id="fieldSet">
<title>The FieldSet</title>
<para>When working with flat files in Spring Batch, regardless of
@@ -562,7 +562,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
</tgroup>
</table></para>
<section>
<section id="lineMapper">
<title>LineMapper</title>
<para>As with <classname>RowMapper</classname>, which takes a low
@@ -590,7 +590,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
mapped to an object, as described below.</para>
</section>
<section>
<section id="lineTokenizer">
<title>LineTokenizer</title>
<para>An abstraction for turning a line of input into a line into a
@@ -636,7 +636,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
</itemizedlist>
</section>
<section>
<section id="fieldSetMapper">
<title>FieldSetMapper</title>
<para>The <classname>FieldSetMapper</classname> interface defines a
@@ -659,7 +659,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
<classname>JdbcTemplate</classname>.</para>
</section>
<section>
<section id="defaultLineMapper">
<title>DefaultLineMapper</title>
<para>Now that the basic interfaces for reading in flat files have
@@ -721,7 +721,7 @@ boolean booleanValue = fs.readBoolean(2);</programlisting>
to the raw line is needed.</para>
</section>
<section>
<section id="simpleDelimitedFileReadingExample">
<title>Simple Delimited File Reading Example</title>
<para>The following example will be used to illustrate this using an
@@ -794,8 +794,8 @@ Player player = itemReader.read();</programlisting>
reached, null will be returned.</para>
</section>
<section>
<title>Mapping fields by name</title>
<section id="mappingFieldsByName">
<title>Mapping Fields by Name</title>
<para>There is one additional piece of functionality that is allowed
by both <classname>DelimitedLineTokenizer</classname> and
@@ -808,8 +808,8 @@ Player player = itemReader.read();</programlisting>
<para><programlisting>tokenizer.setNames(new String[] {"ID", "lastName","firstName","position","birthYear","debutYear"}); </programlisting></para>
<para>a <classname>FieldSetMapper</classname> can this use this
information as follows:</para>
<para>A <classname>FieldSetMapper</classname> can use this information
as follows:</para>
<para><programlisting>public class PlayerMapper implements FieldSetMapper&lt;Player&gt; {
public Player mapFieldSet(FieldSet fs) {
@@ -831,7 +831,7 @@ Player player = itemReader.read();</programlisting>
}</programlisting></para>
</section>
<section>
<section id="beanWrapperFieldSetMapper">
<title>Automapping FieldSets to Domain Objects</title>
<para>For many, having to write a specific
@@ -863,8 +863,8 @@ Player player = itemReader.read();</programlisting>
required.</para>
</section>
<section>
<title>Fixed Length file formats</title>
<section id="fixedLengthFileFormats">
<title>Fixed Length File Formats</title>
<para>So far only delimited files have been discussed in much detail,
however, they represent only half of the file reading picture. Many
@@ -930,7 +930,7 @@ UK21341EAH4521535.11customer5</programlisting>
</section>
<section id="prefixMatchingLineMapper">
<title>Multiple record types within a single file</title>
<title>Multiple Record Types within a Single File</title>
<para>All of the file reading examples up to this point have all made
a key assumption for simplicity's sake: all of the records in a file
@@ -1003,12 +1003,12 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
<para>It is also common for a flat file to contain records that each
span multiple lines. To handle this situation, a more complex strategy
is required. A demonstration of this common patter can be found in
is required. A demonstration of this common pattern can be found in
<xref linkend="multiLineRecords" />.</para>
</section>
<section>
<title>Exception Handling in flat files</title>
<section id="exceptionHandlingInFlatFiles">
<title>Exception Handling in Flat Files</title>
<para>There are many scenarios when tokenizing a line may cause
exceptions to be thrown. Many flat files are imperfect and contain
@@ -1027,7 +1027,7 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
and indicates a more specific error encountered while
tokenizing.</para>
<section>
<section id="incorrectTokenCountException">
<title>IncorrectTokenCountException</title>
<para>Both <classname>DelimitedLineTokenizer</classname> and
@@ -1056,7 +1056,7 @@ catch(IncorrectTokenCountException e){
thrown.</para>
</section>
<section>
<section id="incorrectLineLengthException">
<title>IncorrectLineLengthException</title>
<para>Files formatted in a fixed length format have additional
@@ -1105,7 +1105,7 @@ assertEquals("", tokens.readString(1));</programlisting>
</section>
</section>
<section>
<section id="flatFileItemWriter">
<title>FlatFileItemWriter</title>
<para>Writing out to flat files has the same problems and issues that
@@ -1113,7 +1113,7 @@ assertEquals("", tokens.readString(1));</programlisting>
in either delimited or fixed length formats in a transactional
manner.</para>
<section>
<section id="lineAggregator">
<title>LineAggregator</title>
<para>Just as the <classname>LineTokenizer</classname> interface is
@@ -1138,7 +1138,7 @@ assertEquals("", tokens.readString(1));</programlisting>
<classname>item</classname> and returns a
<classname>String</classname>.</para>
<section>
<section id="PassThroughLineAggregator">
<title>PassThroughLineAggregator</title>
<para>The most basic implementation of the LineAggregator interface
@@ -1160,7 +1160,7 @@ assertEquals("", tokens.readString(1));</programlisting>
</section>
</section>
<section>
<section id="SimplifiedFileWritingExample">
<title>Simplified File Writing Example</title>
<para>Now that the <classname>LineAggregator</classname> interface and
@@ -1191,17 +1191,15 @@ assertEquals("", tokens.readString(1));</programlisting>
<para>A simple configuration would look like the following:</para>
<programlisting>&lt;bean id="itemWriter"
class="org.springframework.batch.io.file.FlatFileItemWriter"&gt;
&lt;property name="resource"
value="file:target/test-outputs/20070122.testStream.multilineStep.txt" /&gt;
<programlisting>&lt;bean id="itemWriter" class="org.spr...FlatFileItemWriter"&gt;
&lt;property name="resource" value="file:target/test-outputs/output.txt" /&gt;
&lt;property name="lineAggregator"&gt;
&lt;bean class="org.spr...PassThroughLineAggregator"/&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
</section>
<section>
<section id="FieldExtractor">
<title>FieldExtractor</title>
<para>The above example may be useful for the most basic uses of a
@@ -1259,23 +1257,22 @@ assertEquals("", tokens.readString(1));</programlisting>
object, which can then be written out with a delimiter between the
elements, or as part of a field-width line.</para>
<section>
<section id="PassThroughFieldExtractor">
<title>PassThroughFieldExtractor</title>
<para>There are many cases where an array or something that can be
converted to an array, such as a <classname>Collection</classname>,
needs to be written out. For example, a <classname>List</classname>
could be passed through, in which case it only needs to be converted
to an <classname>Object</classname> array to be written out. For
this type of scenario the
<classname>PassThroughFieldExtractor</classname> can be used. It
should be noted, that if the object passed in is not an array, and
not a <classname>Collection</classname>, then an
<classname>Object</classname> array containing solely the item will
be returned.</para>
<para>There are many cases where a collection, such as an array,
<classname>Collection</classname>, or
<classname>FieldSet</classname>, needs to be written out.
"Extracting" an array from a one of these collection types is very
straightforward: simply convert the collection to an array.
Therefore, the <classname>PassThroughFieldExtractor</classname>
should be used in this scenario. It should be noted, that if the
object passed in is not a type of collection, then the
<classname>PassThroughFieldExtractor</classname> will return an
array containing solely the item to be extracted.</para>
</section>
<section>
<section id="BeanWrapperFieldExtractor">
<title>BeanWrapperFieldExtractor</title>
<para>As with the <classname>BeanWrapperFieldSetMapper</classname>
@@ -1311,7 +1308,7 @@ assertEquals(born, values[2]);</programlisting>
</section>
</section>
<section>
<section id="delimitedFileWritingExample">
<title>Delimited File Writing Example</title>
<para>The most basic flat file format is one in which all fields are
@@ -1354,7 +1351,7 @@ assertEquals(born, values[2]);</programlisting>
then written out with commas between each field.</para>
</section>
<section>
<section id="fixedWidthFileWritingExample">
<title>Fixed Width File Writing Example</title>
<para>Delimited is not the only type of flat file format. Many prefer
@@ -1392,8 +1389,8 @@ assertEquals(born, values[2]);</programlisting>
url="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html"><citetitle>Formatter</citetitle></ulink>.</para>
</section>
<section>
<title>Handling file creation</title>
<section id="handlingFileCreation">
<title>Handling File Creation</title>
<para><classname>FlatFileItemReader</classname> has a very simple
relationship with file resources. When the reader is initialized, it
@@ -1416,7 +1413,7 @@ assertEquals(born, values[2]);</programlisting>
</section>
</section>
<section>
<section id="xmlReadingWriting">
<title id="infrastructure.2.3">XML Item Readers and Writers</title>
<para>Spring Batch provides transactional infrastructure for both reading
@@ -1482,7 +1479,7 @@ assertEquals(born, values[2]);</programlisting>
<para>Now with an introduction to OXM and how one can use XML fragments to
represent records, let's take a closer look at readers and writers.</para>
<section>
<section id="StaxEventItemReader">
<title>StaxEventItemReader</title>
<para>The <classname>StaxEventItemReader</classname> configuration
@@ -1618,7 +1615,7 @@ while (hasNext) {
}</programlisting></para>
</section>
<section>
<section id="StaxEventItemWriter">
<title>StaxEventItemWriter</title>
<para>Output works symmetrically to input. The
@@ -1686,7 +1683,7 @@ staxItemWriter.write(trade);</programlisting>
</section>
</section>
<section>
<section id="multiFileInput">
<title>Multi-File Input</title>
<para>It is a common requirement to process multiple files within a single
@@ -1716,7 +1713,7 @@ staxItemWriter.write(trade);</programlisting>
individual directories until completed successfully.</para>
</section>
<section>
<section id="database">
<title id="infrastructure.2.2">Database</title>
<para>Like most enterprise application styles, a database is the central
@@ -1734,7 +1731,7 @@ staxItemWriter.write(trade);</programlisting>
provides two types of solutions for this problem: Cursor and Paging
database ItemReaders.</para>
<section>
<section id="cursorBasedItemReaders">
<title>Cursor Based ItemReaders</title>
<para>Using a database cursor is generally the default approach of most
@@ -1782,7 +1779,7 @@ staxItemWriter.write(trade);</programlisting>
collected (assuming no instance variables are maintaining references to
them).</para>
<section>
<section id="JdbcCursorItemReader">
<title>JdbcCursorItemReader</title>
<para><classname>JdbcCursorItemReader</classname> is the Jdbc
@@ -1879,7 +1876,7 @@ itemReader.close(executionContext);</programlisting>
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
<section>
<section id="JdbcCursorItemReaderProperties">
<title>Additional Properties</title>
<para>Because there are so many varying options for opening a cursor
@@ -1990,7 +1987,7 @@ itemReader.close(executionContext);</programlisting>
</section>
</section>
<section>
<section id="HibernateCursorItemReader">
<title>HibernateCursorItemReader</title>
<para>Just as normal Spring users make important decisions about
@@ -2050,7 +2047,7 @@ itemReader.close(executionContext);</programlisting>
</section>
</section>
<section>
<section id="pagingItemReaders">
<title>Paging ItemReaders</title>
<para>An alternative to using a database cursor is executing multiple
@@ -2059,7 +2056,7 @@ itemReader.close(executionContext);</programlisting>
specify the starting row number and the number of rows that we want
returned for the page.</para>
<section>
<section id="JdbcPagingItemReader">
<title>JdbcPagingItemReader</title>
<para>One implementation of a paging <classname>ItemReader</classname>
@@ -2122,7 +2119,7 @@ itemReader.close(executionContext);</programlisting>
1.</para>
</section>
<section>
<section id="JpaPagingItemReader">
<title>JpaPagingItemReader</title>
<para>Another implementation of a paging
@@ -2159,7 +2156,7 @@ itemReader.close(executionContext);</programlisting>
entities read from the database for each query execution.</para>
</section>
<section>
<section id="IbatisPagingItemReader">
<title>IbatisPagingItemReader</title>
<para>If you use IBATIS for your data access then you can use the
@@ -2207,7 +2204,7 @@ itemReader.close(executionContext);</programlisting>
</section>
</section>
<section>
<section id="databaseItemWriters">
<title>Database ItemWriters</title>
<para>While both Flat Files and XML have specific ItemWriters, there is
@@ -2279,7 +2276,7 @@ itemReader.close(executionContext);</programlisting>
</section>
</section>
<section>
<section id="reusingExistingServices">
<title>Reusing Existing Services</title>
<para>Batch systems are often used in conjunction with other application
@@ -2325,7 +2322,7 @@ itemReader.close(executionContext);</programlisting>
</programlisting>
</section>
<section>
<section id="validatingInput">
<title id="infrastructure.5">Validating Input</title>
<para>During the course of this chapter, multiple approaches to parsing
@@ -2391,7 +2388,7 @@ itemReader.close(executionContext);</programlisting>
</section>
<section id="process-indicator">
<title>Preventing state persistence</title>
<title>Preventing State Persistence</title>
<para>By default, all of the <classname>ItemReader</classname> and
<classname>ItemWriter</classname> implementations store their current
@@ -2432,7 +2429,7 @@ itemReader.close(executionContext);</programlisting>
executions in which it participates.</para>
</section>
<section>
<section id="customReadersWriters">
<title id="infrastructure.1.1">Creating Custom ItemReaders and
ItemWriters</title>
@@ -2447,7 +2444,7 @@ itemReader.close(executionContext);</programlisting>
implement <classname>ItemStream</classname>, in order to illustrate how to
make a reader or writer restartable.</para>
<section>
<section id="customReader">
<title>Custom ItemReader Example</title>
<para>For the purpose of this example, a simple
@@ -2490,9 +2487,9 @@ assertEquals("2", itemReader.read());
assertEquals("3", itemReader.read());
assertNull(itemReader.read());</programlisting>
<section>
<section id="restartableReader">
<title>Making the <classname>ItemReader</classname>
restartable</title>
Restartable</title>
<para>The final challenge now is to make the
<classname>ItemReader</classname> restartable. Currently, if the power
@@ -2591,7 +2588,7 @@ assertEquals("2", itemReader.read());</programlisting>
</section>
</section>
<section>
<section id="customWriter">
<title>Custom ItemWriter Example</title>
<para>Implementing a Custom <classname>ItemWriter</classname> is similar
@@ -2615,9 +2612,9 @@ assertEquals("2", itemReader.read());</programlisting>
}
}</programlisting>
<section>
<section id="restartableWriter">
<title>Making the <classname>ItemWriter</classname>
restartable</title>
Restartable</title>
<para>To make the ItemWriter restartable we would follow the same
process as for the <classname>ItemReader</classname>, adding and

View File

@@ -1,14 +1,14 @@
<?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>
<chapter id="repeat">
<title>Repeat</title>
<section>
<section id="repeatTemplate">
<title>RepeatTemplate</title>
<para>Batch processing is about repetitive actions - either as a simple
optimisation, or as part of a job. To strategize and generalize the
optimization, or as part of a job. To strategize and generalize the
repetition as well as to provide what amounts to an iterator framework,
Spring Batch has the <classname>RepeatOperations</classname> interface.
The <classname>RepeatOperations</classname> interface looks like
@@ -64,7 +64,7 @@ template.iterate(new RepeatCallback() {
completion decision is delegated to an external policy as in the case
above.</para>
<section>
<section id="repeatContext">
<title>RepeatContext</title>
<para>The method parameter for the <classname>RepeatCallback</classname>
@@ -78,11 +78,11 @@ template.iterate(new RepeatCallback() {
if there is a nested iteration in progress. The parent context is
occasionally useful for storing data that need to be shared between
calls to <methodname>iterate</methodname>. This is the case for instance
if you want to count the number of occurrences of an even in the
if you want to count the number of occurrences of an event in the
iteration and remember it across subsequent calls.</para>
</section>
<section>
<section id="repeatStatus">
<title>RepeatStatus</title>
<para><classname>RepeatStatus</classname> is an enumeration used by
@@ -90,7 +90,7 @@ template.iterate(new RepeatCallback() {
possible <classname>RepeatStatus</classname> values:</para>
<table>
<title>ExitStatus properties</title>
<title>ExitStatus Properties</title>
<tgroup cols="2">
<tbody>
@@ -124,13 +124,13 @@ template.iterate(new RepeatCallback() {
</section>
</section>
<section>
<section id="completionPolicies">
<title>Completion Policies</title>
<para>Inside a <classname>RepeatTemplate</classname> the termination of
the loop in the <methodname>iterate</methodname> method is determined by a
<classname>CompletionPolicy</classname> which is also a factory for the
<classname>ReapeatContext</classname>. The
<classname>RepeatContext</classname>. The
<classname>RepeatTemplate</classname> has the responsibility to use the
current policy to create a <classname>RepeatContext</classname> and pass
that in to the <classname>RepeatCallback</classname> at every stage in the
@@ -153,7 +153,7 @@ template.iterate(new RepeatCallback() {
would require a custom policy.</para>
</section>
<section>
<section id="repeatExceptionHandling">
<title>Exception Handling</title>
<para>If there is an exception thrown inside a
@@ -186,7 +186,7 @@ template.iterate(new RepeatCallback() {
iteration (e.g. a set of chunks inside a step).</para>
</section>
<section>
<section id="repeatListeners">
<title>Listeners</title>
<para>Often it is useful to be able to receive additional callbacks for
@@ -210,8 +210,7 @@ template.iterate(new RepeatCallback() {
void onError(RepeatContext context, Throwable e);
void close(RepeatContext context);
}
</programlisting>The <methodname>open</methodname> and
}</programlisting>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
@@ -224,7 +223,7 @@ template.iterate(new RepeatCallback() {
<methodname>close</methodname> are called in reverse order.</para>
</section>
<section>
<section id="repeatParallelProcessing">
<title>Parallel Processing</title>
<para>Implementations of <classname>RepeatOperations</classname> are not
@@ -234,12 +233,12 @@ template.iterate(new RepeatCallback() {
<classname>TaskExecutorRepeatTemplate</classname>, which uses the Spring
<classname>TaskExecutor</classname> strategy to run the
<classname>RepeatCallback</classname>. The default is to use a
<classname>SynchronousTaskExecutor</classname>, which has the effect of executing the whole
iteration in the same thread (the same as a normal
<classname>SynchronousTaskExecutor</classname>, which has the effect of
executing the whole iteration in the same thread (the same as a normal
<classname>RepeatTemplate</classname>).</para>
</section>
<section>
<section id="declarativeIteration">
<title>Declarative Iteration</title>
<para>Sometimes there is some business processing that you know you want
@@ -266,9 +265,7 @@ template.iterate(new RepeatCallback() {
advice-ref="retryAdvice" order="-1"/&gt;
&lt;/aop:config&gt;
&lt;bean id="retryAdvice"
class="org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor"/&gt;
</programlisting>
&lt;bean id="retryAdvice" class="org.spr...RepeatOperationsInterceptor"/&gt;</programlisting>
<para>The example above uses a default
<classname>RepeatTemplate</classname> inside the interceptor. To change

View File

@@ -4,7 +4,7 @@
<chapter id="retry">
<title>Retry</title>
<section>
<section id="retryTemplate">
<title>RetryTemplate</title>
<para>To make processing more robust and less prone to failure, sometimes
@@ -68,7 +68,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
to the user. If that call fails then it is retried until a timeout is
reached.</para>
<section>
<section id="retryContext">
<title>RetryContext</title>
<para>The method parameter for the <classname>RetryCallback</classname>
@@ -82,7 +82,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
between calls to <methodname>execute</methodname>.</para>
</section>
<section>
<section id="recoveryCallback">
<title>RecoveryCallback</title>
<para>When a retry is exhausted the
@@ -104,7 +104,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
alternate processing through the recovery callback.</para>
</section>
<section>
<section id="statelessRetry">
<title>Stateless Retry</title>
<para>In the simplest case, a retry is just a while loop: the
@@ -120,7 +120,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
when it failed.</para>
</section>
<section>
<section id="statefulRetry">
<title>Stateful Retry</title>
<para>Where the failure has caused a transactional resource to become
@@ -183,7 +183,7 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
</section>
</section>
<section>
<section id="retryPolicies">
<title>Retry Policies</title>
<para>Inside a <classname>RetryTemplate</classname> the decision to retry
@@ -245,7 +245,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
<para>There is also a more flexible implementation called
<classname>ExceptionClassifierRetryPolicy</classname>, which allows the
user to configure different retry behavior for an arbitrary set of
excecption types though the <classname>ExceptionClassifier</classname>
exception types though the <classname>ExceptionClassifier</classname>
abstraction. The policy works by calling on the classifier to convert an
exception into a delegate <classname>RetryPolicy</classname>, so for
example, one exception type can be retried more times before failure than
@@ -257,7 +257,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
retryable.</para>
</section>
<section>
<section id="backoffPolicies">
<title>Backoff Policies</title>
<para>When retrying after a transient failure it often helps to wait a bit
@@ -283,7 +283,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
<classname>ExponentialBackoffPolicy</classname>.</para>
</section>
<section>
<section id="retryListeners">
<title>Listeners</title>
<para>Often it is useful to be able to receive additional callbacks for
@@ -304,8 +304,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
void onError(RetryContext context, RetryCallback&lt;T&gt; callback, Throwable e);
void close(RetryContext context, RetryCallback&lt;T&gt; callback, Throwable e);
}
</programlisting>The <methodname>open</methodname> and
}</programlisting>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
@@ -319,7 +318,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
<methodname>close</methodname> will be called in reverse order.</para>
</section>
<section>
<section id="declarativeRetry">
<title>Declarative Retry</title>
<para>Sometimes there is some business processing that you know you want
@@ -344,8 +343,7 @@ template.execute(new RetryCallback&lt;Foo&gt;() {
&lt;/aop:config&gt;
&lt;bean id="retryAdvice"
class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/&gt;
</programlisting>
class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/&gt;</programlisting>
<para>The example above uses a default
<classname>RetryTemplate</classname> inside the interceptor. To change the

View File

@@ -39,7 +39,7 @@
<para>Next we review the single-process options first, and then the
multi-process options.</para>
<section>
<section id="multithreadedStep">
<title>Multi-threaded Step</title>
<para>The simplest way to start parallel processing is to add a
@@ -47,11 +47,11 @@
attribute of the <literal>tasklet</literal>:</para>
<programlisting>&lt;step id="loading"&gt;
&lt;tasklet reader="stagingReader"
processor="stagingProcessor"
writer="tradeWriter"
commit-interval="1"
task-executor="taskExecutor"/&gt;
&lt;tasklet reader="stagingReader"
processor="stagingProcessor"
writer="tradeWriter"
commit-interval="1"
task-executor="taskExecutor"/&gt;
&lt;/step&gt;</programlisting>
<para>In this example the taskExecutor is a reference to another bean
@@ -78,7 +78,7 @@
track of items that have been processed in a database input table.</para>
</section>
<section>
<section id="scalabilityParallelSteps">
<title>Parallel Steps</title>
<para>As long as the application logic that needs to be parallelized can
@@ -89,19 +89,19 @@
<literal>step3</literal>, you could configure a flow like this:</para>
<para><programlisting>&lt;job id="job1"&gt;
&lt;split id="split1" task-executor="taskExecutor" next="step4"&gt;
&lt;flow&gt;
&lt;step id="step1" parent="s1" next="step2"/&gt;
&lt;step id="step2" parent="s2"/&gt;
&lt;/flow&gt;
&lt;flow&gt;
&lt;step id="step3" parent="s3"/&gt;
&lt;/flow&gt;
&lt;/split&gt;
&lt;step id="step4" parent="s4"/&gt;
&lt;split id="split1" task-executor="taskExecutor" next="step4"&gt;
&lt;flow&gt;
&lt;step id="step1" parent="s1" next="step2"/&gt;
&lt;step id="step2" parent="s2"/&gt;
&lt;/flow&gt;
&lt;flow&gt;
&lt;step id="step3" parent="s3"/&gt;
&lt;/flow&gt;
&lt;/split&gt;
&lt;step id="step4" parent="s4"/&gt;
&lt;/job&gt;
&lt;beans:bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/&gt;</programlisting></para>
&lt;beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/&gt;</programlisting></para>
<para>The configurable "task-executor" attribute is used to specify which
TaskExecutor implementation should be used to execute the individual
@@ -217,7 +217,7 @@
<classname>org.springframework.batch.core.partition</classname>
package).</para>
<section>
<section id="partitionHandler">
<title>PartitionHandler</title>
<para>The <classname>PartitionHandler</classname> is the component that
@@ -247,10 +247,10 @@
<classname>TaskExecutorPartitionHandler</classname>, and it can be
configured like this:</para>
<para><programlisting>&lt;bean class="org.sfw..TaskExecutorPartitionHandler"&gt;
&lt;property name="taskExecutor" ref="taskExecutor"/&gt;
&lt;property name="step" ref="step1" /&gt;
&lt;property name="gridSize" value="10" /&gt;
<para><programlisting>&lt;bean class="org.spr...TaskExecutorPartitionHandler"&gt;
&lt;property name="taskExecutor" ref="taskExecutor"/&gt;
&lt;property name="step" ref="step1" /&gt;
&lt;property name="gridSize" value="10" /&gt;
&lt;/bean&gt;</programlisting></para>
<para>The <literal>gridSize</literal> determines the number of separate
@@ -264,7 +264,7 @@
replicating filesystems into content management systems.</para>
</section>
<section>
<section id="stepExecutionSplitter">
<title>StepExecutionSplitter</title>
<para>The <classname>StepExecutionSplitter</classname> is responsible
@@ -274,9 +274,9 @@
principal method for this in the interface is</para>
<programlisting>public interface StepExecutionSplitter {
...
Set&lt;StepExecution&gt; split(StepExecution stepExecution, int gridSize)
throws JobExecutionException;
...
Set&lt;StepExecution&gt; split(StepExecution stepExecution, int gridSize)
throws JobExecutionException;
}</programlisting>
<para>So an execution instance for the Master step is passed in, along
@@ -295,7 +295,7 @@
restarts). It has a single method:</para>
<programlisting>public interface Partitioner {
Map&lt;String, ExecutionContext&gt; partition(int gridSize);
Map&lt;String, ExecutionContext&gt; partition(int gridSize);
}</programlisting>
<para>The return value from this method associates a unique name for
@@ -322,7 +322,7 @@
this convention.</para>
</section>
<section>
<section id="bindingInputDataToSteps">
<title>Binding Input Data to Steps</title>
<para>It is very efficient for the steps that are executed by the
@@ -374,10 +374,10 @@
<para>Then the file name can be bound to a step using late binding to
the execution context:</para>
<programlisting> &lt;bean id="itemReader" scope="step"
class="org.sfw...MultiResourceItemReader"&gt;
<programlisting>&lt;bean id="itemReader" scope="step"
class="org.spr...MultiResourceItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext[fileName]}/*</emphasis>"/&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;</programlisting>
</section>
</section>
</chapter>

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<appendix>
<appendix id="metaDataSchema">
<title>Meta-Data Schema</title>
<section>
<section id="metaDataSchemaOverview">
<title>Overview</title>
<para>The Spring Batch Meta-Data tables very closely match the Domain
@@ -37,7 +37,7 @@
</imageobject>
</mediaobject>
<section>
<section id="exampleDDLScripts">
<title>Example DDL Scripts</title>
<para>The root of the Spring Batch Core JAR file contains example
@@ -49,7 +49,7 @@
short name of the target database platform.</para>
</section>
<section>
<section id="metaDataVersion">
<title>Version</title>
<para>Many of the database tables discussed in this appendix contain a
@@ -64,7 +64,7 @@
different machines, they are all using the same database tables.</para>
</section>
<section>
<section id="metaDataIdentity">
<title>Identity</title>
<para>BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, and BATCH_STEP_EXECUTION
@@ -83,7 +83,7 @@ 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>
work-arounds are used, such as the following for mySQL:</para>
<programlisting>CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);
@@ -99,7 +99,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</section>
</section>
<section>
<section id="metaDataBatchJobInstance">
<title>BATCH_JOB_INSTANCE</title>
<para>The BATCH_JOB_INSTANCE table holds all information relevant to a
@@ -145,7 +145,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="metaDataBatchJobParams">
<title>BATCH_JOB_PARAMS</title>
<para>The BATCH_JOB_PARAMS table holds all information relevant to the
@@ -211,7 +211,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
generated key, without causing any issues to the framework itself.</para>
</section>
<section>
<section id="metaDataBatchJobExecution">
<title>BATCH_JOB_EXECUTION</title>
<para>The BATCH_JOB_EXECUTION table holds all information relevant to the
@@ -298,7 +298,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="metaDataBatchStepExecution">
<title>BATCH_STEP_EXECUTION</title>
<para>The BATCH_STEP_EXECUTION table holds all information relevant to the
@@ -437,7 +437,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="metaDataBatchJobExecutionContext">
<title>BATCH_JOB_EXECUTION_CONTEXT</title>
<para>The BATCH_JOB_EXECUTION_CONTEXT table holds all information relevant
@@ -478,7 +478,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="metaDataBatchStepExecutionContext">
<title>BATCH_STEP_EXECUTION_CONTEXT</title>
<para>The BATCH_STEP_EXECUTION_CONTEXT table holds all information
@@ -519,7 +519,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="metaDataArchiving">
<title>Archiving</title>
<para>Because there are entries in multiple tables every time a batch job
@@ -551,7 +551,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</itemizedlist>
</section>
<section>
<section id="recommendationsForIndexingMetaDataTables">
<title>Recommendations for Indexing Meta Data Tables</title>
<para>Spring Batch provides DDL samples for the meta-data tables in the
@@ -565,8 +565,8 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
projects can make up their own minds about indexing.</para>
<table>
<title>Where clauses in SQL statements (exluding primary keys) and their
approximate frequency of use.</title>
<title>Where clauses in SQL statements (excluding primary keys) and
their approximate frequency of use.</title>
<tgroup cols="3">
<tbody>

View File

@@ -339,7 +339,7 @@ itemWriter.write(items);</programlisting>
</section>
<section id="stepRestartExample">
<title>Step restart configuration example</title>
<title>Step Restart Configuration Example</title>
<programlisting>&lt;job id="footballJob" restartable="true"&gt;
&lt;step id="playerload" next="gameLoad"&gt;

View File

@@ -12,7 +12,7 @@
focus on. The spring-batch-test project includes classes that will help
facilitate this end-to-end test approach.</para>
<section>
<section id="creatingUnitTestClass">
<title>Creating a Unit Test Class</title>
<para>In order for the unit test to run a batch job, the framework must
@@ -31,16 +31,14 @@
</listitem>
</itemizedlist>
<programlisting>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests extends AbstractJobTests { ... }
</programlisting>
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
"/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests extends AbstractJobTests { ... }</programlisting>
</section>
<section>
<title>End To End Testing Batch Jobs</title>
<section id="endToEndTesting">
<title>End-To-End Testing of Batch Jobs</title>
<para>'End To End' testing can be defined as testing the complete run of a
batch job from beginning to end. This allows for a test that sets up a
@@ -61,36 +59,35 @@
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", "/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests extends AbstractJobTests {
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
"/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests extends AbstractJobTests {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcTemplate simpleJdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Autowired
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Transactional
@Test
public void testJob() throws Exception {
simpleJdbcTemplate.update("delete from CUSTOMER");
for (int i = 1; i &lt;= 10; i++) {
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)", i, "customer" + i);
}
@Transactional
@Test
public void testJob() throws Exception {
simpleJdbcTemplate.update("delete from CUSTOMER");
for (int i = 1; i &lt;= 10; i++) {
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
i, "customer" + i);
}
JobExecution jobExecution = <emphasis role="bold">this.launchJob()</emphasis>;
JobExecution jobExecution = <emphasis role="bold">this.launchJob()</emphasis>;
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus());
}
}
</programlisting>
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus());
}
}</programlisting>
</section>
<section>
<section id="testingIndividualSteps">
<title>Testing Individual Steps</title>
<para>For complex batch jobs, test cases in the end-to-end testing
@@ -102,13 +99,10 @@
targeted tests by allowing the test to set up data for just that step and
to validate its results directly.</para>
<programlisting>
JobExecution jobExecution = this.launchStep("loadFileStep");
</programlisting>
<programlisting>JobExecution jobExecution = this.launchStep("loadFileStep");</programlisting>
</section>
<section>
<section id="validatingOutputFiles">
<title>Validating Output Files</title>
<para>When a batch job writes to the database, it is easy to query the
@@ -123,35 +117,30 @@
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/iosample/input/multiLine.txt";
private static final String OUTPUT_FILE = "target/test-outputs/multiLineOutput.txt";
<programlisting>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), new FileSystemResource(OUTPUT_FILE));
</programlisting>
AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
new FileSystemResource(OUTPUT_FILE));</programlisting>
</section>
<section>
<section id="mockingDomainObjects">
<title>Mocking Domain Objects</title>
<para>Another common issue encountered while writing unit and integration
tests for Spring Batch components is how to mock domain objects. A good
example is a <classname>StepExecutionListener</classname>, as illustrated
below: </para>
below:</para>
<programlisting>
public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
<programlisting>public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
throw new NoWorkFoundException("Step has not processed any items");
}
return stepExecution.getExitStatus();
if (stepExecution.getReadCount() == 0) {
throw new NoWorkFoundException("Step has not processed any items");
}
return stepExecution.getExitStatus();
}
}
</programlisting>
}</programlisting>
<para>The above listener is provided by the framework and checks a
<classname>StepExecution</classname> for an empty read count, thus
@@ -160,26 +149,22 @@
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>private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
@Test
public void testAfterStep() {
<emphasis role="bold">StepExecution stepExecution = new StepExecution("NoProcessingStep",
new JobExecution(
new JobInstance(1L, new JobParameters(), "NoProcessingJob")));</emphasis>
@Test
public void testAfterStep() {
<emphasis role="bold">StepExecution stepExecution = new StepExecution("NoProcessingStep",
new JobExecution(new JobInstance(1L, new JobParameters(), "NoProcessingJob")));</emphasis>
stepExecution.setReadCount(0);
stepExecution.setReadCount(0);
try {
tested.afterStep(stepExecution);
fail();
} catch (NoWorkFoundException e) {
assertEquals("Step has not processed any items", e.getMessage());
}
try {
tested.afterStep(stepExecution);
fail();
} catch (NoWorkFoundException e) {
assertEquals("Step has not processed any items", e.getMessage());
}
</programlisting>
}</programlisting>
<para>Because the Spring Batch domain model follows good object orientated
principles, the StepExecution requires a
@@ -190,27 +175,24 @@
model, it does make creating stub objects for unit testing verbose. To
address this issue, the Spring Batch test module includes a factory for
creating domain objects: <classname>MetaDataInstanceFactory</classname>.
Given this factory, the unit test can be updated to be more concise:
</para>
Given this factory, the unit test can be updated to be more
concise:</para>
<programlisting>
private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
<programlisting>private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
@Test
public void testAfterStep() {
<emphasis role="bold">StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();</emphasis>
@Test
public void testAfterStep() {
<emphasis role="bold">StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();</emphasis>
stepExecution.setReadCount(0);
stepExecution.setReadCount(0);
try {
tested.afterStep(stepExecution);
fail();
} catch (NoWorkFoundException e) {
assertEquals("Step has not processed any items", e.getMessage());
}
try {
tested.afterStep(stepExecution);
fail();
} catch (NoWorkFoundException e) {
assertEquals("Step has not processed any items", e.getMessage());
}
</programlisting>
}</programlisting>
<para>The above method for creating a simple
<classname>StepExecution</classname> is just one convenience method

View File

@@ -2,7 +2,7 @@
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="whatsNew">
<title>What's new in Spring Batch 2.0</title>
<title>What's New in Spring Batch 2.0</title>
<para>The Spring Batch 2.0 release has six major themes:</para>