BATCH-2224 Ensure Syntax Highlighting for Program Listings

* Polish Docbook
This commit is contained in:
Gunnar Hillert
2014-04-26 23:54:15 -04:00
committed by Michael Minella
parent f6b83167ac
commit dff3f0b80b
13 changed files with 395 additions and 412 deletions

View File

@@ -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>&lt;step id="simpleStep"&gt;
<programlisting language="xml">&lt;step id="simpleStep"&gt;
...
&lt;listeners&gt;
&lt;listener&gt;
@@ -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&lt;T&gt; {
<programlisting language="java">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);
@@ -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&lt;T&gt; {
<programlisting language="java">public class EarlyCompletionItemReader implements ItemReader&lt;T&gt; {
private ItemReader&lt;T&gt; delegate;
public void setDelegate(ItemReader&lt;T&gt; 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>&lt;step id="simpleStep"&gt;
<programlisting language="xml">&lt;step id="simpleStep"&gt;
&lt;tasklet&gt;
&lt;chunk reader="reader" writer="writer" commit-interval="10"
&lt;chunk reader="reader" writer="writer" commit-interval="10"
<emphasis role="bold">chunk-completion-policy="completionPolicy"</emphasis>/&gt;
&lt;/tasklet&gt;
&lt;/step&gt;
@@ -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>&lt;bean id="itemWriter" class="org.spr...FlatFileItemWriter"&gt;
<programlisting language="java">&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>
@@ -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&lt;Trade&gt;,
<programlisting language="java">public class TradeItemWriter implements ItemWriter&lt;Trade&gt;,
FlatFileFooterCallback {
private ItemWriter&lt;Trade&gt; delegate;
@@ -250,7 +250,7 @@
<classname>FlatFileItemWriter</classname> as the
<code>footerCallback</code>:</para>
<programlisting>&lt;bean id="tradeItemWriter" class="..TradeItemWriter"&gt;
<programlisting language="xml">&lt;bean id="tradeItemWriter" class="..TradeItemWriter"&gt;
&lt;property name="delegate" ref="flatFileItemWriter" /&gt;
&lt;/bean&gt;
@@ -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>&lt;bean id="itemReader" class="org.spr...MultiLineTradeItemReader"&gt;
<programlisting language="xml">&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;
@@ -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>&lt;bean id="orderFileTokenizer" class="org.spr...PatternMatchingCompositeLineTokenizer"&gt;
<programlisting language="xml">&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;
@@ -422,7 +422,7 @@ FOT;2;2;267.34</programlisting>
<classname>ItemProcessor</classname> and
<classname>ItemWriter</classname>.</para>
<programlisting>private FlatFileItemReader&lt;FieldSet&gt; delegate;
<programlisting language="java">private FlatFileItemReader&lt;FieldSet&gt; 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>&lt;bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet"&gt;
<programlisting language="xml">&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;
@@ -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&lt;Object&gt; {
<programlisting language="java">public class SavingItemWriter implements ItemWriter&lt;Object&gt; {
private StepExecution stepExecution;
public void write(List&lt;? extends Object&gt; items) throws Exception {
@@ -564,7 +564,7 @@ public Trade read() throws Exception {
listeners, it must be registered on the
<classname>Step</classname>.</para>
<programlisting>&lt;job id="job1"&gt;
<programlisting language="xml">&lt;job id="job1"&gt;
&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="reader" writer="savingWriter" commit-interval="10"/&gt;
@@ -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&lt;Object&gt; {
<programlisting language="java">public class RetrievingItemWriter implements ItemWriter&lt;Object&gt; {
private Object someObject;
public void write(List&lt;? extends Object&gt; items) throws Exception {

View File

@@ -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>&lt;job&gt;</code> tag can be used:</para>
<programlisting>&lt;job id="footballJob"&gt;
<programlisting language="xml">&lt;job id="footballJob"&gt;
&lt;step id="playerload" next="gameLoad"/&gt;
&lt;step id="gameLoad" next="playerSummarization"/&gt;
&lt;step id="playerSummarization"/&gt;
@@ -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>&lt;job-repository id="jobRepository"/&gt;</programlisting>
<programlisting language="xml">&lt;job-repository id="jobRepository"/&gt;</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>&lt;beans:beans xmlns="<emphasis role="bold">http://www.springframework.org/schema/batch</emphasis>"
<programlisting language="xml">&lt;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="

View File

@@ -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

View File

@@ -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>&#064;Configuration
<programlisting language="java">&#064;Configuration
&#064;EnableBatchProcessing
&#064;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

View File

@@ -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>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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 @@
&lt;/step&gt;
&lt;/job&gt;
&lt;/beans&gt;
</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>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"&gt;
&lt;step id="step1" &gt;
&lt;batchlet ref="io.spring.FooBatchlet" /&gt;
&lt;/step&gt;
&lt;/job&gt;
</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>&lt;properties&gt;
<programlisting language="xml">&lt;properties&gt;
&lt;property name=&quot;propertyName1&quot; value=&quot;propertyValue1&quot;/&gt;
&lt;property name=&quot;propertyName2&quot; value=&quot;propertyValue2&quot;/&gt;
&lt;/properties&gt;</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">...
&lt;step id="step1"&gt;
&lt;chunk checkpoint-policy="item" item-count="3"&gt;
&lt;reader ref="fooReader"/&gt;
@@ -199,6 +199,7 @@
&lt;/chunk&gt;
&lt;/step&gt;
...</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">...
&lt;step id="step1"&gt;
&lt;chunk checkpoint-policy="custom"&gt;
&lt;checkpoint-algorithm ref="fooCheckpointer"/&gt;
@@ -226,7 +228,6 @@
&lt;/chunk&gt;
&lt;/step&gt;
...</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>

View File

@@ -46,7 +46,7 @@
<para><classname>ItemReader</classname> is a basic interface for generic
input operations:</para>
<programlisting>public interface ItemReader&lt;T&gt; {
<programlisting language="java">public interface ItemReader&lt;T&gt; {
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&lt;T&gt; {
<programlisting language="java">public interface ItemWriter&lt;T&gt; {
void write(List&lt;? extends T&gt; items) throws Exception;
@@ -115,7 +115,7 @@
that contains another <classname>ItemReader</classname>. For
example:</para>
<programlisting>public class CompositeItemWriter&lt;T&gt; implements ItemWriter&lt;T&gt; {
<programlisting language="java">public class CompositeItemWriter&lt;T&gt; implements ItemWriter&lt;T&gt; {
ItemWriter&lt;T&gt; itemWriter;
@@ -145,7 +145,7 @@
For this scenario, Spring Batch provides the
<classname>ItemProcessor</classname> interface:</para>
<programlisting>public interface ItemProcessor&lt;I, O&gt; {
<programlisting language="java">public interface ItemProcessor&lt;I, O&gt; {
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&lt;Bar&gt;{
provided. The <classname>FooProcessor</classname> can then be injected
into a <classname>Step</classname>:</para>
<programlisting>&lt;job id="ioSampleJob"&gt;
<programlisting language="xml">&lt;job id="ioSampleJob"&gt;
&lt;step name="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="fooReader" processor="fooProcessor" writer="barWriter"
@@ -211,7 +211,7 @@ public class BarWriter implements ItemWriter&lt;Bar&gt;{
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&lt;FooBar&gt;{
<classname>BarProcessor</classname> can be 'chained' together to give
the resultant <classname>Foobar</classname>:</para>
<programlisting>CompositeItemProcessor&lt;Foo,Foobar&gt; compositeProcessor =
<programlisting language="java">CompositeItemProcessor&lt;Foo,Foobar&gt; compositeProcessor =
new CompositeItemProcessor&lt;Foo,Foobar&gt;();
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>&lt;job id="ioSampleJob"&gt;
<programlisting language="xml">&lt;job id="ioSampleJob"&gt;
&lt;step name="step1"&gt;
&lt;tasklet&gt;
&lt;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>&lt;job id="ioSampleJob"&gt;
<programlisting language="xml">&lt;job id="ioSampleJob"&gt;
&lt;step name="step1"&gt;
&lt;tasklet&gt;
&lt;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&lt;T&gt; {
<classname>Object</classname>:
</para>
<programlisting language="java">public interface LineMapper&lt;T&gt; {
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&lt;T&gt; {
<programlisting language="java">public interface FieldSetMapper&lt;T&gt; {
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&lt;T&gt; implements LineMapper&lt;T&gt;, InitializingBean {
<programlisting language="java">public class DefaultLineMapper&lt;T&gt; implements LineMapper&lt;T&gt;, 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&lt;Player&gt; {
<programlisting language="java">protected static class PlayerFieldSetMapper implements FieldSetMapper&lt;Player&gt; {
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&lt;Player&gt; itemReader = new FlatFileItemReader&lt;Player&gt;();
<programlisting language="java">FlatFileItemReader&lt;Player&gt; itemReader = new FlatFileItemReader&lt;Player&gt;();
itemReader.setResource(new FileSystemResource("resources/players.csv"));
//DelimitedLineTokenizer defaults to comma as its delimiter
LineMapper&lt;Player&gt; lineMapper = new DefaultLineMapper&lt;Player&gt;();
@@ -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&lt;Player&gt; {
<programlisting language="java">public class PlayerMapper implements FieldSetMapper&lt;Player&gt; {
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>&lt;bean id="fieldSetMapper"
<programlisting language="xml">&lt;bean id="fieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"&gt;
&lt;property name="prototypeBeanName" value="player" /&gt;
&lt;/bean&gt;
@@ -916,7 +923,7 @@ UK21341EAH4521535.11customer5</programlisting>
<classname>FixedLengthLineTokenizer</classname>, each of these lengths
must be provided in the form of ranges:</para>
<programlisting>&lt;bean id="fixedLengthLineTokenizer"
<programlisting language="xml">&lt;bean id="fixedLengthLineTokenizer"
class="org.springframework.batch.io.file.transform.FixedLengthTokenizer"&gt;
&lt;property name="names" value="ISIN,Quantity,Price,Customer" /&gt;
&lt;property name="columns" value="1-12, 13-15, 16-20, 21-29" /&gt;
@@ -969,7 +976,7 @@ LINEB;2134776319DEF422.99M005LI</programlisting>
<classname>LineTokenizer</classname>s and patterns to
<classname>FieldSetMapper</classname>s to be configured:</para>
<programlisting>&lt;bean id="orderFileLineMapper"
<programlisting language="xml">&lt;bean id="orderFileLineMapper"
class="org.spr...PatternMatchingCompositeLineMapper"&gt;
&lt;property name="tokenizers"&gt;
&lt;map&gt;
@@ -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>&lt;entry key="*" value-ref="defaultLineTokenizer" /&gt;</programlisting>
<programlisting language="xml">&lt;entry key="*" value-ref="defaultLineTokenizer" /&gt;</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&lt;T&gt; {
<programlisting language="java">public interface LineAggregator&lt;T&gt; {
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&lt;T&gt; implements LineAggregator&lt;T&gt; {
<programlisting language="java">public class PassThroughLineAggregator&lt;T&gt; implements LineAggregator&lt;T&gt; {
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>&lt;bean id="itemWriter" class="org.spr...FlatFileItemWriter"&gt;
<programlisting language="xml">&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;
@@ -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&lt;T&gt; {
<programlisting language="java">public interface FieldExtractor&lt;T&gt; {
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&lt;Name&gt; extractor = new BeanWrapperFieldExtractor&lt;Name&gt;();
<programlisting language="java">BeanWrapperFieldExtractor&lt;Name&gt; extractor = new BeanWrapperFieldExtractor&lt;Name&gt;();
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>&lt;bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
<programlisting language="xml">&lt;bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="lineAggregator"&gt;
&lt;bean class="org.spr...DelimitedLineAggregator"&gt;
@@ -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>&lt;bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
<programlisting language="xml">&lt;bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="lineAggregator"&gt;
&lt;bean class="org.spr...FormatterLineAggregator"&gt;
@@ -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>&lt;property name="format" value="%-9s%-2.0f" /&gt;</programlisting>
<programlisting language="xml">&lt;property name="format" value="%-9s%-2.0f" /&gt;</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>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;records&gt;
&lt;trade xmlns="http://springframework.org/batch/sample/io/oxm/domain"&gt;
&lt;isin&gt;XYZ0001&lt;/isin&gt;
@@ -1518,7 +1525,7 @@ assertEquals(born, values[2]);</programlisting>
&lt;price&gt;99.99&lt;/price&gt;
&lt;customer&gt;Customer3&lt;/customer&gt;
&lt;/trade&gt;
&lt;/records&gt;</programlisting></para>
&lt;/records&gt;</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>&lt;bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.springframework.batch.item.xml.StaxEventItemReader"&gt;
&lt;property name="fragmentRootElementName" value="trade" /&gt;
&lt;property name="resource" value="data/iosample/input/input.xml" /&gt;
&lt;property name="unmarshaller" ref="tradeMarshaller" /&gt;
&lt;/bean&gt;
</programlisting></para>
&lt;/bean&gt;</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>&lt;bean id="tradeMarshaller"
<programlisting language="xml">&lt;bean id="tradeMarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller"&gt;
&lt;property name="aliases"&gt;
<emphasis role="bold"> &lt;util:map id="aliases"&gt;
@@ -1566,7 +1572,7 @@ assertEquals(born, values[2]);</programlisting>
&lt;entry key="name" value="java.lang.String" /&gt;
&lt;/util:map&gt;</emphasis>
&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
&lt;/bean&gt;</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>&lt;bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter"&gt;
<programlisting language="xml">&lt;bean id="itemWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter"&gt;
&lt;property name="resource" ref="outputResource" /&gt;
&lt;property name="marshaller" ref="customerCreditMarshaller" /&gt;
&lt;property name="rootTagName" value="customers" /&gt;
@@ -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>&lt;bean id="customerCreditMarshaller"
<programlisting language="xml">&lt;bean id="customerCreditMarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller"&gt;
&lt;property name="aliases"&gt;
&lt;util:map id="aliases"&gt;
@@ -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>&lt;bean id="multiResourceReader" class="org.spr...MultiResourceItemReader"&gt;
<programlisting language="xml">&lt;bean id="multiResourceReader" class="org.spr...MultiResourceItemReader"&gt;
&lt;property name="resources" value="classpath:data/input/file-*.txt" /&gt;
&lt;property name="delegate" ref="flatFileItemReader" /&gt;
&lt;/bean&gt;</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>&lt;bean id="itemReader" class="org.spr...JdbcCursorItemReader"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.spr...JdbcCursorItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="sql" value="select ID, NAME, CREDIT from CUSTOMER"/&gt;
&lt;property name="rowMapper"&gt;
@@ -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>&lt;bean id="itemReader"
<programlisting language="xml">&lt;bean id="itemReader"
class="org.springframework.batch.item.database.HibernateCursorItemReader"&gt;
&lt;property name="sessionFactory" ref="sessionFactory" /&gt;
&lt;property name="queryString" value="from CustomerCredit" /&gt;
@@ -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>&lt;bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader"&gt;
<programlisting language="xml">&lt;bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="procedureName" value="sp_customer_credit"/&gt;
&lt;property name="rowMapper"&gt;
@@ -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>&lt;bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader"&gt;
<programlisting language="xml">&lt;bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="procedureName" value="sp_customer_credit"/&gt;
&lt;property name="refCursorPosition" value="1"/&gt;
@@ -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>&lt;bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader"&gt;
<programlisting language="xml">&lt;bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="procedureName" value="sp_customer_credit"/&gt;
&lt;property name="function" value="true"/&gt;
@@ -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>&lt;bean id="reader" class="org.springframework.batch.item.database.StoredProcedureItemReader"&gt;
<programlisting language="xml">&lt;bean id="reader" class="o.s.batch.item.database.StoredProcedureItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="procedureName" value="spring.cursor_func"/&gt;
&lt;property name="parameters"&gt;
@@ -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>&lt;bean id="itemReader" class="org.spr...JdbcPagingItemReader"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.spr...JdbcPagingItemReader"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;property name="queryProvider"&gt;
&lt;bean class="org.spr...SqlPagingQueryProviderFactoryBean"&gt;
@@ -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>&lt;bean id="itemReader" class="org.spr...JpaPagingItemReader"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.spr...JpaPagingItemReader"&gt;
&lt;property name="entityManagerFactory" ref="entityManagerFactory"/&gt;
&lt;property name="queryString" value="select c from CustomerCredit c"/&gt;
&lt;property name="pageSize" value="1000"/&gt;
@@ -2280,7 +2286,7 @@ itemReader.close(executionContext);</programlisting>
<classname>IbatisPagingItemReader</classname> reading CustomerCredits
as in the examples above:</para>
<programlisting>&lt;bean id="itemReader" class="org.spr...IbatisPagingItemReader"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.spr...IbatisPagingItemReader"&gt;
&lt;property name="sqlMapClient" ref="sqlMapClient"/&gt;
&lt;property name="queryId" value="getPagedCustomerCredits"/&gt;
&lt;property name="pageSize" value="1000"/&gt;
@@ -2291,7 +2297,7 @@ itemReader.close(executionContext);</programlisting>
Here is an example of what that query should look like for
MySQL.</para>
<programlisting>&lt;select id="getPagedCustomerCredits" resultMap="customerCreditResult"&gt;
<programlisting language="xml">&lt;select id="getPagedCustomerCredits" resultMap="customerCreditResult"&gt;
select id, name, credit from customer order by id asc LIMIT #_skiprows#, #_pagesize#
&lt;/select&gt;</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>&lt;select id="getPagedCustomerCredits" resultMap="customerCreditResult"&gt;
<programlisting language="xml">&lt;select id="getPagedCustomerCredits" resultMap="customerCreditResult"&gt;
select * from (
select * from (
select t.id, t.name, t.credit, ROWNUM ROWNUM_ from customer t order by id
)) where ROWNUM_ &lt;![CDATA[ &gt; ]]&gt; ( #_page# * #_pagesize# )
) where ROWNUM &lt;![CDATA[ &lt;= ]]&gt; #_pagesize#
&lt;/select&gt;
</programlisting>
&lt;/select&gt;</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>&lt;bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter"&gt;
<programlisting language="xml">&lt;bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter"&gt;
&lt;property name="targetObject" ref="fooService" /&gt;
&lt;property name="targetMethod" value="generateFoo" /&gt;
&lt;/bean&gt;
@@ -2423,7 +2428,7 @@ itemReader.close(executionContext);</programlisting>
<classname>ItemWriter</classname>. The <classname>ItemWriter</classname>
implementation is equally as simple:</para>
<programlisting>&lt;bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter"&gt;
<programlisting language="xml">&lt;bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter"&gt;
&lt;property name="targetObject" ref="fooService" /&gt;
&lt;property name="targetMethod" value="processFoo" /&gt;
&lt;/bean&gt;
@@ -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>&lt;bean class="org.springframework.batch.item.validator.ValidatingItemProcessor"&gt;
<programlisting language="xml">&lt;bean class="org.springframework.batch.item.validator.ValidatingItemProcessor"&gt;
&lt;property name="validator" ref="validator" /&gt;
&lt;/bean&gt;
@@ -2516,7 +2521,7 @@ itemReader.close(executionContext);</programlisting>
restart. For this reason, all readers and writers include the 'saveState'
property:</para>
<programlisting>&lt;bean id="playerSummarizationSource" class="org.spr...JdbcCursorItemReader"&gt;
<programlisting language="xml">&lt;bean id="playerSummarizationSource" class="org.spr...JdbcCursorItemReader"&gt;
&lt;property name="dataSource" ref="dataSource" /&gt;
&lt;property name="rowMapper"&gt;
&lt;bean class="org.springframework.batch.sample.PlayerSummaryMapper" /&gt;
@@ -2563,7 +2568,7 @@ itemReader.close(executionContext);</programlisting>
basic contract of <classname>ItemReader</classname>,
<methodname>read</methodname>:</para>
<programlisting>public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;{
<programlisting language="java">public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;{
List&lt;T&gt; 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&lt;String&gt; items = new ArrayList&lt;String&gt;();
<programlisting language="java">List&lt;String&gt; items = new ArrayList&lt;String&gt;();
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&lt;T&gt; implements ItemReader&lt;T&gt;, ItemStream {
<programlisting language="java">public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;, ItemStream {
List&lt;T&gt; 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&lt;T&gt; implements ItemWriter&lt;T&gt; {
<programlisting language="java">public class CustomItemWriter&lt;T&gt; implements ItemWriter&lt;T&gt; {
List&lt;T&gt; output = TransactionAwareProxyFactory.createTransactionalList();

View File

@@ -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>&lt;aop:config&gt;
<programlisting language="xml">&lt;aop:config&gt;
&lt;aop:pointcut id="transactional"
expression="execution(* com..*Service.processMessage(..))" /&gt;
&lt;aop:advisor pointcut-ref="transactional"

View File

@@ -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 {
&lt;T&gt; T execute(RetryCallback&lt;T&gt; retryCallback) throws Exception;
@@ -36,14 +36,16 @@
&lt;T&gt; T execute(RetryCallback&lt;T&gt; retryCallback, RecoveryCallback&lt;T&gt; 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&lt;T&gt; {
<programlisting language="java">public interface RetryCallback&lt;T&gt; {
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&lt;Foo&gt;() {
feature clients just pass in the callbacks together to the same method,
for example:</para>
<para><programlisting>Foo foo = template.execute(new RetryCallback&lt;Foo&gt;() {
<programlisting language="java">Foo foo = template.execute(new RetryCallback&lt;Foo&gt;() {
public Foo doWithRetry(RetryContext context) {
// business logic here
},
@@ -107,7 +109,8 @@ Foo result = template.execute(new RetryCallback&lt;Foo&gt;() {
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&lt;Foo&gt;() {
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&lt;Foo&gt;() {
<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&lt;Foo&gt;() {
<para>The interface looks like this:</para>
<para><programlisting>public interface RetryListener {
<programlisting language="java">public interface RetryListener {
void open(RetryContext context, RetryCallback&lt;T&gt; callback);
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>
<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&lt;Foo&gt;() {
<methodname>remoteCall</methodname> (for more detail on how to configure
AOP interceptors see the Spring User Guide):</para>
<programlisting>&lt;aop:config&gt;
<programlisting language="xml">&lt;aop:config&gt;
&lt;aop:pointcut id="transactional"
expression="execution(* com..*Service.remoteCall(..))" /&gt;
&lt;aop:advisor pointcut-ref="transactional"

View File

@@ -46,7 +46,7 @@
<classname>TaskExecutor</classname> to your Step configuration, e.g. as an
attribute of the <literal>tasklet</literal>:</para>
<programlisting>&lt;step id="loading"&gt;
<programlisting language="xml">&lt;step id="loading"&gt;
&lt;tasklet task-executor="taskExecutor"&gt;...&lt;/tasklet&gt;
&lt;/step&gt;</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>&lt;step id="loading"&gt; &lt;tasklet
<programlisting language="xml">&lt;step id="loading"&gt; &lt;tasklet
task-executor="taskExecutor"
throttle-limit="20"&gt;...&lt;/tasklet&gt;
&lt;/step&gt;</programlisting>
&lt;/step&gt;</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>&lt;job id="job1"&gt;
<programlisting language="xml">&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;
@@ -130,7 +130,7 @@
&lt;step id="step4" parent="s4"/&gt;
&lt;/job&gt;
&lt;beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/&gt;</programlisting></para>
&lt;beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/&gt;</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>&lt;step id="step1.master"&gt;
<programlisting language="xml">&lt;step id="step1.master"&gt;
&lt;partition step="step1" partitioner="partitioner"&gt;
&lt;handler grid-size="10" task-executor="taskExecutor"/&gt;
&lt;/partition&gt;
&lt;/step&gt;</programlisting></para>
&lt;/step&gt;</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>&lt;step id="step1.master"&gt;
<programlisting language="xml">&lt;step id="step1.master"&gt;
&lt;partition step="step1" handler="handler"/&gt;
&lt;/step&gt;
@@ -287,7 +287,7 @@
&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>
&lt;/bean&gt;</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&lt;String, ExecutionContext&gt; 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>&lt;bean id="itemReader" scope="step"
<programlisting language="xml">&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>

View File

@@ -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>

View File

@@ -77,8 +77,7 @@
namespace declarations to your Spring XML Application Context
file:
</para>
<programlisting>
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
<programlisting language="xml">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:batch-int=&quot;http://www.springframework.org/schema/batch-integration&quot;
xsi:schemaLocation=&quot;
@@ -87,14 +86,12 @@
...
&lt;/beans&gt;
</programlisting>
&lt;/beans&gt;</programlisting>
<para>
A fully configured Spring XML Application Context file for Spring
Batch Integration may look like the following:
</para>
<programlisting>
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
<programlisting language="xml">&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:int=&quot;http://www.springframework.org/schema/integration&quot;
xmlns:batch=&quot;http://www.springframework.org/schema/batch&quot;
@@ -111,8 +108,7 @@
...
&lt;/beans&gt;
</programlisting>
&lt;/beans&gt;</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>
&lt;int:channel id=&quot;inboundFileChannel&quot;/&gt;
<programlisting language="xml">&lt;int:channel id=&quot;inboundFileChannel&quot;/&gt;
&lt;int:channel id=&quot;outboundJobRequestChannel&quot;/&gt;
&lt;int:channel id=&quot;jobLaunchReplyChannel&quot;/&gt;
@@ -304,8 +297,7 @@ public class FileMessageToJobRequest {
&lt;batch-int:job-launching-gateway request-channel=&quot;outboundJobRequestChannel&quot;
reply-channel=&quot;jobLaunchReplyChannel&quot;/&gt;
&lt;int:logging-channel-adapter channel=&quot;jobLaunchReplyChannel&quot;/&gt;
</programlisting>
&lt;int:logging-channel-adapter channel=&quot;jobLaunchReplyChannel&quot;/&gt;</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>
&lt;bean id=&quot;itemReader&quot; class=&quot;org.springframework.batch.item.file.FlatFileItemReader&quot;
<programlisting language="xml">&lt;bean id=&quot;itemReader&quot; class=&quot;org.springframework.batch.item.file.FlatFileItemReader&quot;
scope=&quot;step&quot;&gt;
&lt;property name=&quot;resource&quot; value=&quot;file://#{jobParameters['input.file.name']}&quot;/&gt;
...
&lt;/bean&gt;
</programlisting>
&lt;/bean&gt;</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>
&lt;batch-int:job-launching-gateway request-channel=&quot;queueChannel&quot;
<programlisting language="xml">&lt;batch-int:job-launching-gateway request-channel=&quot;queueChannel&quot;
reply-channel=&quot;replyChannel&quot; job-launcher=&quot;jobLauncher&quot;&gt;
&lt;int:poller fixed-rate=&quot;1000&quot;/&gt;
&lt;/batch-int:job-launching-gateway&gt;
</programlisting>
&lt;/batch-int:job-launching-gateway&gt;</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>
&lt;int:channel id=&quot;stepExecutionsChannel&quot;/&gt;
<programlisting language="xml">&lt;int:channel id=&quot;stepExecutionsChannel&quot;/&gt;
&lt;int:gateway id=&quot;notificationExecutionsListener&quot;
service-interface=&quot;org.springframework.batch.core.StepExecutionListener&quot;
default-request-channel=&quot;stepExecutionsChannel&quot;/&gt;
&lt;int:logging-channel-adapter channel=&quot;stepExecutionsChannel&quot;/&gt;
</programlisting>
&lt;int:logging-channel-adapter channel=&quot;stepExecutionsChannel&quot;/&gt;</programlisting>
<para>
Then modify your job to add a step level listener:
</para>
<programlisting>
&lt;job id=&quot;importPayments&quot;&gt;
<programlisting language="xml">&lt;job id=&quot;importPayments&quot;&gt;
&lt;step id=&quot;step1&quot;&gt;
&lt;tasklet ../&gt;
&lt;chunk ../&gt;
@@ -555,8 +540,7 @@ public class FileMessageToJobRequest {
&lt;/tasklet&gt;
...
&lt;/step&gt;
&lt;/job&gt;
</programlisting>
&lt;/job&gt;</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>
&lt;bean id=&quot;processor&quot;
<programlisting language="xml">&lt;bean id=&quot;processor&quot;
class=&quot;org.springframework.batch.integration.async.AsyncItemProcessor&quot;&gt;
&lt;property name=&quot;delegate&quot;&gt;
&lt;bean class=&quot;your.ItemProcessor&quot;/&gt;
@@ -590,8 +573,7 @@ public class FileMessageToJobRequest {
&lt;property name=&quot;taskExecutor&quot;&gt;
&lt;bean class=&quot;org.springframework.core.task.SimpleAsyncTaskExecutor&quot;/&gt;
&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
&lt;/bean&gt;</programlisting>
<para>
The property &quot;<classname>delegate</classname>&quot; 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>
&lt;bean id=&quot;itemWriter&quot;
<programlisting language="xml">&lt;bean id=&quot;itemWriter&quot;
class=&quot;org.springframework.batch.integration.async.AsyncItemWriter&quot;&gt;
&lt;property name=&quot;delegate&quot;&gt;
&lt;bean id=&quot;itemWriter&quot; class=&quot;your.ItemWriter&quot;/&gt;
&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
&lt;/bean&gt;</programlisting>
<para>
Again, the property &quot;<classname>delegate</classname>&quot; 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>
&lt;job id=&quot;personJob&quot;&gt;
<programlisting language="xml">&lt;job id=&quot;personJob&quot;&gt;
&lt;step id=&quot;step1&quot;&gt;
&lt;tasklet&gt;
&lt;chunk reader=&quot;itemReader&quot; writer=&quot;itemWriter&quot; commit-interval=&quot;200&quot;/&gt;
&lt;/tasklet&gt;
...
&lt;/step&gt;
&lt;/job&gt;
</programlisting>
&lt;/job&gt;</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>
&lt;bean id=&quot;connectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
<programlisting language="xml">&lt;bean id=&quot;connectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
&lt;property name=&quot;brokerURL&quot; value=&quot;tcp://localhost:61616&quot;/&gt;
&lt;/bean&gt;
@@ -727,8 +704,7 @@ public class FileMessageToJobRequest {
&lt;int-jms:message-driven-channel-adapter id=&quot;jmsReplies&quot;
destination-name=&quot;replies&quot;
channel=&quot;replies&quot;/&gt;
</programlisting>
channel=&quot;replies&quot;/&gt;</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>
&lt;bean id=&quot;connectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
<programlisting language="xml">&lt;bean id=&quot;connectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&gt;
&lt;property name=&quot;brokerURL&quot; value=&quot;tcp://localhost:61616&quot;/&gt;
&lt;/bean&gt;
@@ -776,8 +751,7 @@ public class FileMessageToJobRequest {
&lt;/property&gt;
&lt;/bean&gt;
&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
&lt;/bean&gt;</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>
&lt;bean id=&quot;partitionHandler&quot;
<programlisting language="xml">&lt;bean id=&quot;partitionHandler&quot;
class=&quot;org.springframework.batch.integration.partition.MessageChannelPartitionHandler&quot;&gt;
&lt;property name=&quot;stepName&quot; value=&quot;step1&quot;/&gt;
&lt;property name=&quot;gridSize&quot; value=&quot;3&quot;/&gt;
@@ -912,20 +885,17 @@ public class FileMessageToJobRequest {
&lt;/int:channel&gt;
&lt;bean id=&quot;stepLocator&quot;
class=&quot;org.springframework.batch.integration.partition.BeanFactoryStepLocator&quot; /&gt;
</programlisting>
class=&quot;org.springframework.batch.integration.partition.BeanFactoryStepLocator&quot; /&gt;</programlisting>
<para>
Also ensure the partition <classname>handler</classname> attribute
maps to the <classname>partitionHandler</classname> bean:
</para>
<programlisting>
&lt;job id=&quot;personJob&quot;&gt;
<programlisting language="xml">&lt;job id=&quot;personJob&quot;&gt;
&lt;step id=&quot;step1.master&quot;&gt;
&lt;partition partitioner=&quot;partitioner&quot; handler=&quot;partitionHandler&quot;/&gt;
...
&lt;/step&gt;
&lt;/job&gt;
</programlisting>
&lt;/job&gt;</programlisting>
</sect3>
</sect2>
</sect1>

View File

@@ -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 &lt; 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>&lt;job id="sampleJob" job-repository="jobRepository"&gt;
<programlisting language="xml">&lt;job id="sampleJob" job-repository="jobRepository"&gt;
&lt;step id="step1"&gt;
&lt;tasklet transaction-manager="transactionManager"&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="10"/&gt;
@@ -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>&lt;step id="parentStep"&gt;
<programlisting language="xml">&lt;step id="parentStep"&gt;
&lt;tasklet allow-start-if-complete="true"&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="10"/&gt;
&lt;/tasklet&gt;
@@ -183,7 +183,7 @@ itemWriter.write(items);</programlisting>
be abstract. The <classname>Step</classname> "concreteStep2" will have
'itemReader', 'itemWriter', and commitInterval=10.</para>
<programlisting>&lt;step id="abstractParentStep" abstract="true"&gt;
<programlisting language="xml">&lt;step id="abstractParentStep" abstract="true"&gt;
&lt;tasklet&gt;
&lt;chunk commit-interval="10"/&gt;
&lt;/tasklet&gt;
@@ -214,7 +214,7 @@ itemWriter.write(items);</programlisting>
<classname>listenerOne</classname> and
<classname>listenerTwo</classname>:</para>
<programlisting>&lt;step id="listenersParentStep" abstract="true"&gt;
<programlisting language="xml">&lt;step id="listenersParentStep" abstract="true"&gt;
&lt;listeners&gt;
&lt;listener ref="listenerOne"/&gt;
&lt;listeners&gt;
@@ -246,7 +246,7 @@ itemWriter.write(items);</programlisting>
number of items that are processed within a commit can be
configured.</para>
<programlisting>&lt;job id="sampleJob"&gt;
<programlisting language="xml">&lt;job id="sampleJob"&gt;
&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;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>&lt;step id="step1"&gt;
&lt;tasklet <emphasis role="bold">start-limit="1"</emphasis>&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet start-limit="1"&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="10"/&gt;
&lt;/tasklet&gt;
&lt;/step&gt;</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>&lt;step id="step1"&gt;
&lt;tasklet <emphasis role="bold">allow-start-if-complete="true"</emphasis>&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet allow-start-if-complete="true"&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="10"/&gt;
&lt;/tasklet&gt;
&lt;/step&gt;</programlisting>
@@ -319,7 +319,7 @@ itemWriter.write(items);</programlisting>
<section id="stepRestartExample">
<title>Step Restart Configuration Example</title>
<programlisting>&lt;job id="footballJob" restartable="true"&gt;
<programlisting language="xml">&lt;job id="footballJob" restartable="true"&gt;
&lt;step id="playerload" next="gameLoad"&gt;
&lt;tasklet&gt;
&lt;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>&lt;step id="step1"&gt;
listeners.
</para>
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="flatFileItemReader" writer="itemWriter"
commit-interval="10" <emphasis role="bold">skip-limit="10"</emphasis>&gt;
@@ -460,7 +462,7 @@ itemWriter.write(items);</programlisting>
&lt;/skippable-exception-classes&gt;</emphasis>
&lt;/chunk&gt;
&lt;/tasklet&gt;
&lt;/step&gt;</programlisting></para>
&lt;/step&gt;</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>&lt;step id="step1"&gt;
else:
</para>
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="flatFileItemReader" writer="itemWriter"
commit-interval="10" <emphasis role="bold">skip-limit="10"</emphasis>&gt;
@@ -485,7 +489,7 @@ itemWriter.write(items);</programlisting>
&lt;/skippable-exception-classes&gt;
</emphasis> &lt;/chunk&gt;
&lt;/tasklet&gt;
&lt;/step&gt;</programlisting></para>
&lt;/step&gt;</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>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="itemReader" writer="itemWriter"
commit-interval="2" <emphasis role="bold">retry-limit="3"</emphasis>&gt;
@@ -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>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="2"/&gt;
&lt;no-rollback-exception-classes&gt;
@@ -570,7 +574,7 @@ itemWriter.write(items);</programlisting>
this reason, the step can be configured to not buffer the
items:</para>
<programlisting>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="2"
<emphasis role="bold"> is-reader-transactional-queue="true"</emphasis>/&gt;
@@ -587,7 +591,7 @@ itemWriter.write(items);</programlisting>
transaction attributes can be found in the spring core
documentation.</para>
<programlisting>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="itemReader" writer="itemWriter" commit-interval="2"/&gt;
&lt;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>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="itemReader" writer="compositeWriter" commit-interval="2"&gt;
<emphasis role="bold">&lt;streams&gt;
@@ -675,7 +679,7 @@ itemWriter.write(items);</programlisting>
the most granular level that it applies (chunk in the example
given).</para>
<programlisting>&lt;step id="step1"&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet&gt;
&lt;chunk reader="reader" writer="writer" commit-interval="10"/&gt;
&lt;listeners&gt;
@@ -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&lt;T&gt; extends StepListener {
<classname>ItemReaderListener:</classname>
</para>
<programlisting language="java">public interface ItemReadListener&lt;T&gt; 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&lt;T, S&gt; extends StepListener {
<programlisting language="java">public interface ItemProcessListener&lt;T, S&gt; 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&lt;S&gt; extends StepListener {
<programlisting language="java">public interface ItemWriteListener&lt;S&gt; extends StepListener {
void beforeWrite(List&lt;? extends S&gt; items);
void afterWrite(List&lt;? extends S&gt; items);
void onWriteError(Exception exception, List&lt;? extends S&gt; 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&lt;T,S&gt; extends StepListener {
<programlisting language="java">public interface SkipListener&lt;T,S&gt; 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 &lt;chunk/&gt; element should be
used within the &lt;tasklet/&gt;:</para>
<programlisting>&lt;step id="step1"&gt;
&lt;tasklet <classname>ref="myTasklet"</classname>/&gt;
<programlisting language="xml">&lt;step id="step1"&gt;
&lt;tasklet ref="myTasklet"/&gt;
&lt;/step&gt;</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>&lt;bean id="myTasklet" class="org.springframework.batch.core.step.tasklet.MethodInvokingTaskletAdapter"&gt;
<programlisting language="xml">&lt;bean id="myTasklet" class="o.s.b.core.step.tasklet.MethodInvokingTaskletAdapter"&gt;
&lt;property name="targetObject"&gt;
&lt;bean class="org.mycompany.FooDao"/&gt;
&lt;/property&gt;
@@ -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>&lt;job id="taskletJob"&gt;
<programlisting language="xml">&lt;job id="taskletJob"&gt;
&lt;step id="deleteFilesInDir"&gt;
&lt;tasklet ref="fileDeletingTasklet"/&gt;
&lt;/step&gt;
@@ -1140,11 +1136,13 @@ itemWriter.write(items);</programlisting>
<para>This can be achieved using the 'next' attribute of the step
element:</para>
<para><programlisting>&lt;job id="job"&gt;
<programlisting language="xml">&lt;job id="job"&gt;
&lt;step id="stepA" parent="s1" next="stepB" /&gt;
&lt;step id="stepB" parent="s2" next="stepC"/&gt;
&lt;step id="stepC" parent="s3" /&gt;
&lt;/job&gt;</programlisting>In the scenario above, 'step A' will execute
&lt;/job&gt;</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>&lt;job id="job"&gt;
<programlisting language="xml">&lt;job id="job"&gt;
&lt;step id="stepA" parent="s1"&gt;
&lt;next on="*" to="stepB" /&gt;
&lt;next on="FAILED" to="stepC" /&gt;
&lt;/step&gt;
&lt;step id="stepB" parent="s2" next="stepC" /&gt;
&lt;step id="stepC" parent="s3" /&gt;
&lt;/job&gt;</programlisting></para>
&lt;/job&gt;</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>&lt;next on="FAILED" to="stepB" /&gt;</programlisting>
<programlisting language="xml">&lt;next on="FAILED" to="stepB" /&gt;</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>&lt;step id="step1" parent="s1"&gt;
<programlisting language="xml">&lt;step id="step1" parent="s1"&gt;
&lt;end on="FAILED" /&gt;
&lt;next on="COMPLETED WITH SKIPS" to="errorPrint1" /&gt;
&lt;next on="*" to="step2" /&gt;
@@ -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()) &amp;&amp;
@@ -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>&lt;step id="stepC" parent="s3"/&gt;</programlisting></para>
<programlisting language="xml">&lt;step id="stepC" parent="s3"/&gt;</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>&lt;step id="step1" parent="s1" next="step2"&gt;
<programlisting language="xml">&lt;step id="step1" parent="s1" next="step2"&gt;
&lt;step id="step2" parent="s2"&gt;
&lt;end on="FAILED"/&gt;
@@ -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>&lt;step id="step1" parent="s1" next="step2"&gt;
<programlisting language="xml">&lt;step id="step1" parent="s1" next="step2"&gt;
&lt;step id="step2" parent="s2"&gt;
&lt;fail on="FAILED" exit-code="EARLY TERMINATION"/&gt;
@@ -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>&lt;step id="step1" parent="s1"&gt;
<programlisting language="xml">&lt;step id="step1" parent="s1"&gt;
&lt;stop on="COMPLETED" restart="step2"/&gt;
&lt;/step&gt;
&lt;step id="step2" parent="s2"/&gt;</programlisting></para>
&lt;step id="step2" parent="s2"/&gt;</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>&lt;job id="job"&gt;
<programlisting language="xml">&lt;job id="job"&gt;
&lt;step id="step1" parent="s1" next="decision" /&gt;
&lt;decision id="decision" decider="decider"&gt;
@@ -1507,7 +1503,7 @@ itemWriter.write(items);</programlisting>
&lt;step id="step3" parent="s3" /&gt;
&lt;/job&gt;
&lt;beans:bean id="decider" class="com.MyDecider"/&gt;</programlisting></para>
&lt;beans:bean id="decider" class="com.MyDecider"/&gt;</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>&lt;split id="split1" next="step4"&gt;
<programlisting language="xml">&lt;split id="split1" next="step4"&gt;
&lt;flow&gt;
&lt;step id="step1" parent="s1" next="step2"/&gt;
&lt;step id="step2" parent="s2"/&gt;
@@ -1545,7 +1541,7 @@ itemWriter.write(items);</programlisting>
first is to simply declare the flow as a reference to one defined
elsewhere:</para>
<programlisting>&lt;job id="job"&gt;
<programlisting language="xml">&lt;job id="job"&gt;
&lt;flow id="job1.flow1" parent="flow1" next="step3"/&gt;
&lt;step id="step3" parent="s3"/&gt;
&lt;/job&gt;
@@ -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>&lt;job id="jobStepJob" restartable="true"&gt;
<programlisting language="xml">&lt;job id="jobStepJob" restartable="true"&gt;
&lt;step id="jobStepJob.step1"&gt;
&lt;job ref="<emphasis role="bold">job</emphasis>" job-launcher="jobLauncher"
job-parameters-extractor="jobParametersExtractor"/&gt;
@@ -1579,7 +1575,7 @@ itemWriter.write(items);</programlisting>
&lt;bean id="jobParametersExtractor" class="org.spr...DefaultJobParametersExtractor"&gt;
&lt;property name="keys" value="input.file"/&gt;
&lt;/bean&gt;</programlisting></para>
&lt;/bean&gt;</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>&lt;bean id="flatFileItemReader"
<programlisting language="xml">&lt;bean id="flatFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource"
value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" /&gt;
@@ -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>&lt;bean id="flatFileItemReader"
<programlisting language="xml">&lt;bean id="flatFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="${input.file.name}" /&gt;
&lt;/bean&gt;</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>&lt;bean id="flatFileItemReader" scope="step"
<programlisting language="xml">&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobParameters['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</programlisting>
@@ -1647,12 +1643,12 @@ itemWriter.write(items);</programlisting>
<classname>ExecutionContext</classname> can be accessed in the same
way:</para>
<programlisting>&lt;bean id="flatFileItemReader" scope="step"
<programlisting language="xml">&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{jobExecutionContext['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</programlisting>
<programlisting>&lt;bean id="flatFileItemReader" scope="step"
<programlisting language="xml">&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="<emphasis role="bold">#{stepExecutionContext['input.file.name']}</emphasis>" /&gt;
&lt;/bean&gt;</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>&lt;bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
<programlisting language="xml">&lt;bean id="flatFileItemReader" <emphasis role="bold">scope="step"</emphasis>
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{jobParameters[input.file.name]}" /&gt;
&lt;/bean&gt;</programlisting>
@@ -1692,7 +1688,7 @@ itemWriter.write(items);</programlisting>
scope must be added explicitly, either by using the
<literal>batch</literal> namespace:</para>
<programlisting>&lt;beans xmlns="http://www.springframework.org/schema/beans"
<programlisting language="xml">&lt;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="..."&gt;
@@ -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>&lt;bean class="org.springframework.batch.core.scope.StepScope" /&gt;</programlisting>
<programlisting language="xml">&lt;bean class="org.springframework.batch.core.scope.StepScope" /&gt;</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>&lt;bean id=&quot;...&quot; class=&quot;...&quot; <emphasis role="bold">scope=&quot;job&quot;</emphasis>&gt;
</para>
<programlisting language="xml">&lt;bean id=&quot;...&quot; class=&quot;...&quot; <emphasis role="bold">scope=&quot;job&quot;</emphasis>&gt;
&lt;property name=&quot;name&quot; value=&quot;#{jobParameters[input]}&quot; /&gt;
&lt;/bean&gt;
</programlisting>
<programlisting>&lt;bean id=&quot;...&quot; class=&quot;...&quot; <emphasis role="bold">scope=&quot;job&quot;</emphasis>&gt;
<programlisting language="xml">&lt;bean id=&quot;...&quot; class=&quot;...&quot; <emphasis role="bold">scope=&quot;job&quot;</emphasis>&gt;
&lt;property name=&quot;name&quot; value=&quot;#{jobExecutionContext['input.name']}.txt&quot; /&gt;
&lt;/bean&gt;
</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>&lt;beans xmlns="http://www.springframework.org/schema/beans"
<programlisting language="xml">&lt;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="..."&gt;
@@ -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>&lt;bean class="org.springframework.batch.core.scope.JobScope" /&gt;</programlisting>
<programlisting language="xml">&lt;bean class="org.springframework.batch.core.scope.JobScope" /&gt;</programlisting>
</section>
</section>
</chapter>

View File

@@ -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 &lt;= 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&lt;Integer&gt;() {
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() {