BATCH-1035: Added section on Late binding.

This commit is contained in:
lucasward
2009-01-28 05:31:40 +00:00
parent 505d8532f9
commit 2be012fa4c
6 changed files with 201 additions and 113 deletions

View File

@@ -90,7 +90,7 @@
<para>In Spring Batch, a Job is simply a continer for Steps. It combines
multiple steps that belong logically together in a flow and allows for
configuration of properties global to all steps, such as restartability.
The job configuration contains: </para>
The job configuration contains:</para>
<itemizedlist>
<listitem>
@@ -303,7 +303,7 @@
<tgroup cols="2">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>JOB_NAME</entry>
</row>
@@ -323,7 +323,7 @@
<tgroup cols="4">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>TYPE_CD</entry>
@@ -339,7 +339,7 @@
<entry>schedule.Date</entry>
<entry>2008-01-01 00:00:00</entry>
<entry>2008-01-01</entry>
</row>
</tbody>
</tgroup>
@@ -351,9 +351,9 @@
<tgroup cols="5">
<tbody>
<row>
<entry>JOB_EXECUTION_ID</entry>
<entry>JOB_EXEC_ID</entry>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>START_TIME</entry>
@@ -367,9 +367,9 @@
<entry>1</entry>
<entry>2008-01-01 21:00:23.571</entry>
<entry>2008-01-01 21:00</entry>
<entry>2008-01-01 21:30:17.132</entry>
<entry>2008-01-01 21:30</entry>
<entry>FAILED</entry>
</row>
@@ -378,8 +378,8 @@
</table>
<note>
<para>extra columns in the tables have been removed for added
clarity.</para>
<para>column names may have been abbreviated or removed for clarity
and formatting</para>
</note>
<para>Now that the job has failed, let's assume that it took the entire
@@ -409,7 +409,7 @@
<tgroup cols="2">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>JOB_NAME</entry>
</row>
@@ -435,7 +435,7 @@
<tgroup cols="4">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>TYPE_CD</entry>
@@ -473,9 +473,9 @@
<tgroup cols="5">
<tbody>
<row>
<entry>JOB_EXECUTION_ID</entry>
<entry>JOB_EXEC_ID</entry>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>START_TIME</entry>
@@ -522,6 +522,11 @@
</tbody>
</tgroup>
</table>
<note>
<para>column names may have been abbreviated or removed for clarity
and formatting</para>
</note>
</section>
</section>
@@ -717,7 +722,7 @@
<tgroup cols="2">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>JOB_NAME</entry>
</row>
@@ -735,7 +740,7 @@
<tgroup cols="4">
<tbody>
<row>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>TYPE_CD</entry>
@@ -751,7 +756,7 @@
<entry>schedule.Date</entry>
<entry>2008-01-01 00:00:00</entry>
<entry>2008-01-01 </entry>
</row>
</tbody>
</tgroup>
@@ -761,9 +766,9 @@
<tgroup cols="5">
<tbody>
<row>
<entry>JOB_EXECUTION_ID</entry>
<entry>JOB_EXEC_ID</entry>
<entry>JOB_INSTANCE_ID</entry>
<entry>JOB_INST_ID</entry>
<entry>START_TIME</entry>
@@ -777,9 +782,9 @@
<entry>1</entry>
<entry>2008-01-01 21:00:23.571</entry>
<entry>2008-01-01 21:00</entry>
<entry>2008-01-01 21:30:17.132</entry>
<entry>2008-01-01 21:30</entry>
<entry>FAILED</entry>
</row>
@@ -791,9 +796,9 @@
<tgroup cols="6">
<tbody>
<row>
<entry>STEP_EXECUTION_ID</entry>
<entry>STEP_EXEC_ID</entry>
<entry>JOB_EXECUTION_ID</entry>
<entry>JOB_EXEC_ID</entry>
<entry>STEP_NAME</entry>
@@ -811,9 +816,9 @@
<entry>loadDate</entry>
<entry>2008-01-01 21:00:23.571</entry>
<entry>2008-01-01 21:00</entry>
<entry>2008-01-01 21:30:17.132</entry>
<entry>2008-01-01 21:30</entry>
<entry>FAILED</entry>
</row>
@@ -825,7 +830,7 @@
<tgroup cols="2">
<tbody>
<row>
<entry>STEP_EXECUTION_ID</entry>
<entry>STEP_EXEC_ID</entry>
<entry>SHORT_CONTEXT</entry>
</row>
@@ -945,8 +950,8 @@
<programlisting> public interface JobLauncher {
public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
JobRestartException;
public JobExecution run(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException, JobRestartException;
}
</programlisting>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -154,11 +154,12 @@
<programlisting>
void afterJob(JobExecution jobExecution){
if( jobExecution.getStatus = BatchStatus.COMPLETED ){
//job success
}
else if(jobExecution.getStatus = BatchStatus.FAILED){
//job failure
if( jobExecution.getStatus = BatchStatus.COMPLETED ){
//job success
}
else if(jobExecution.getStatus = BatchStatus.FAILED){
//job failure
}
}
</programlisting>
@@ -379,7 +380,8 @@
that you just don't need to persist status for a particular job. Spring
batch provides a solution:</para>
<programlisting> &lt;bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" /&gt;</programlisting>
<programlisting> &lt;bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" /&gt;</programlisting>
</section>
</section>

View File

@@ -182,11 +182,14 @@
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>
String[] tokens = new String[]{"foo", "1", "true"};
FieldSet fs = new DefaultFieldSet(tokens);
String name = fs.readString(0);
int value = fs.readInt(1);
boolean booleanValue = fs.readBoolean(2);</programlisting>
boolean booleanValue = fs.readBoolean(2);
</programlisting>
<para>There are many more options on the <classname>FieldSet</classname>
interface, such as <classname>Date</classname>, long,
@@ -217,7 +220,7 @@
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");
Resource resource = new FileSystemResource("resources/trades.csv");
</programlisting></para>
<para>In complex batch environments the directory structures are often
@@ -320,10 +323,13 @@
<para>As with <classname>RowMapper</classname>, which takes a low
level construct such as <classname>ResultSet and returns an Object,
flat file procesing requires the same construct to convert a String
line into an Object:</classname><programlisting>public interface LineMapper&lt;T&gt; {
line into an Object:</classname><programlisting>
public interface LineMapper&lt;T&gt; {
T mapLine(String line, int lineNumber) throws Exception;
}</programlisting></para>
T mapLine(String line, int lineNumber) throws Exception;
}
</programlisting></para>
<para>The basic constract is that, given the current line, and the
line number its associated with, return a resulting domain object.
@@ -349,11 +355,14 @@
necessary. In Spring Batch, this is called a
<classname>LineTokenizer</classname>:</para>
<programlisting> public interface LineTokenizer {
<programlisting>
public interface LineTokenizer {
FieldSet tokenize(String line);
}</programlisting>
}
</programlisting>
<para>The contract of a <classname>LineTokenizer</classname> is such
that, given a line of input (in theory the
@@ -397,11 +406,14 @@
<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>
public interface FieldSetMapper&lt;T&gt; {
T mapFieldSet(FieldSet fieldSet);
}</programlisting>
}
</programlisting>
<para>The pattern used is the same as <classname>RowMapper</classname>
used by <classname>JdbcTemplate</classname>.</para>
@@ -459,7 +471,9 @@
public void setFieldSetMapper(FieldSetMapper&lt;T&gt; fieldSetMapper) {
this.fieldSetMapper = fieldSetMapper;
}
}</programlisting>
}
</programlisting>
<para>The above functionality is provided in a default implementation,
rather than being built into the reader itself (as was done in
@@ -522,7 +536,8 @@
return player;
}
} </programlisting></para>
}
</programlisting></para>
<para>The file can then be read by correctly constructing a
<classname>FlatFileItemReader</classname> and calling
@@ -583,7 +598,8 @@
return player;
}
} </programlisting></para>
}
</programlisting></para>
</section>
<section>
@@ -599,14 +615,17 @@
<classname>FieldSetMapper</classname> configuration looks like the
following:</para>
<programlisting> &lt;bean id="fieldSetMapper"
<programlisting>
&lt;bean id="fieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"&gt;
&lt;property name="prototypeBeanName" value="player" /&gt;
&lt;/bean&gt;
&lt;bean id="player"
class="org.springframework.batch.sample.domain.Player"
scope="prototype" /&gt;</programlisting>
scope="prototype" /&gt;
</programlisting>
<para>For each entry in the <classname>FieldSet</classname>, the
mapper will look for a corresponding setter on a new instance of the
@@ -680,7 +699,8 @@
<classname>ApplicationContext</classname>:</para>
<programlisting>
&lt;bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"&gt;
&lt;bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer"&gt;
&lt;property name="customEditors"&gt;
&lt;map&gt;
&lt;entry key="org.springframework.batch.item.file.transform.Range[]"&gt;
@@ -717,7 +737,8 @@
PrefixMatchingCompositeLineTokenizer makes this easier by matching the
prefix in a line with a particular tokenizer:</para>
<programlisting> &lt;bean id="orderFileDescriptor"
<programlisting>
&lt;bean id="orderFileDescriptor"
class="org.springframework.batch.io.file.transform.PrefixMatchingCompositeLineTokenizer"&gt;
&lt;property name="tokenizers"&gt;
&lt;map&gt;
@@ -733,7 +754,9 @@
&lt;entry key="" value-ref="defaultLineDescriptor" /&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;
</programlisting>
<para>This ensures that the line will be parsed correctly, which is
especially important for fixed length input. Any users of the
@@ -863,11 +886,14 @@
writing to a file. In Spring Batch this is the
<classname>LineAggregator</classname>:</para>
<programlisting> public interface LineAggregator&lt;T&gt; {
<programlisting>
public interface LineAggregator&lt;T&gt; {
public String aggregate(T item);
}</programlisting>
}
</programlisting>
<para>The <classname>LineAggregator</classname> is the opposite of a
<classname>LineTokenizer</classname>.
@@ -927,20 +953,26 @@
<classname>FlatFileItemWriter</classname> expresses this in
code:</para>
<programlisting> public void write(T item) throws Exception {
<programlisting>
public void write(T item) throws Exception {
write(lineAggregator.aggregate(item) + LINE_SEPARATOR);
}</programlisting>
}
</programlisting>
<para>A simple configuration would look like the following:</para>
<programlisting> &lt;bean id="itemWriter"
<programlisting>
&lt;bean id="itemWriter"
class="org.springframework.batch.io.file.FlatFileItemWriter"&gt;
&lt;property name="resource"
value="file:target/test-outputs/20070122.testStream.multilineStep.txt" /&gt;
&lt;property name="lineAggregator"&gt;
&lt;bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;
</programlisting>
</section>
<section>
@@ -1201,12 +1233,12 @@
<imageobject role="fo">
<imagedata align="center"
fileref="src/site/docbook/reference/images/xmlinput.png"
format="PNG" width="50%" />
format="PNG" width="40%" />
</imageobject>
<imageobject role="html">
<imagedata align="center" fileref="images/xmlinput.png" format="PNG"
width="50%" />
width="40%" />
</imageobject>
<caption><para>Figure 3.1: XML Input</para></caption>
@@ -1228,12 +1260,12 @@
<imageobject role="fo">
<imagedata align="center"
fileref="src/site/docbook/reference/images/oxm-fragments.png"
format="PNG" width="50%" />
format="PNG" width="40%" />
</imageobject>
<imageobject role="html">
<imagedata align="center" fileref="images/oxm-fragments.png"
format="PNG" width="50%" />
format="PNG" width="40%" />
</imageobject>
<caption><para>Figure 3.2: OXM Binding</para></caption>
@@ -2241,7 +2273,8 @@
<programlisting>
&lt;job id="ioSampleJob"&gt;
&lt;step name="step1"&gt;
&lt;tasklet reader="fooReader" processor="fooProcessor" writer="compositeItemWriter" commit-interval="2"/&gt;
&lt;tasklet reader="fooReader" processor="fooProcessor" writer="compositeItemWriter"
commit-interval="2"/&gt;
&lt;streams&gt;
&lt;stream ref="barWriter" /&gt;
&lt;/streams&gt;
@@ -2357,11 +2390,14 @@
rather provides a very simple interface that can be implemented by any
number of frameworks:</para>
<programlisting> public interface Validator {
<programlisting>
public interface Validator {
void validate(Object value) throws ValidationException;
}</programlisting>
}
</programlisting>
<para>The contract is that the <methodname>validate</methodname> method
will throw an exception if the object is invalid, and return normally if
@@ -2381,9 +2417,14 @@
&lt;value&gt;
&lt;![CDATA[
{ orderId : ? &gt; 0 AND ? &lt;= 9999999999 : 'Incorrect order ID' : 'error.order.id' }
{ totalLines : ? = size(lineItems) : 'Bad count of order lines' : 'error.order.lines.badcount'}
{ customer.registered : customer.businessCustomer = FALSE OR ? = TRUE : 'Business customer must be registered' : 'error.customer.registration'}
{ customer.companyName : customer.businessCustomer = FALSE OR ? HAS TEXT : 'Company name for business customer is mandatory' :'error.customer.companyname'}
{ totalLines : ? = size(lineItems) : 'Bad count of order lines'
: 'error.order.lines.badcount'}
{ customer.registered : customer.businessCustomer = FALSE OR ? = TRUE
: 'Business customer must be registered'
: 'error.customer.registration'}
{ customer.companyName : customer.businessCustomer = FALSE OR ? HAS TEXT
: 'Company name for business customer is mandatory'
:'error.customer.companyname'}
]]&gt;
&lt;/value&gt;
&lt;/property&gt;
@@ -2468,7 +2509,8 @@
basic contract of <classname>ItemReader</classname>,
<methodname>read</methodname>:</para>
<programlisting> public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;{
<programlisting>
public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;{
List&lt;T&gt; items;
@@ -2484,7 +2526,9 @@
}
return null;
}
}</programlisting>
}
</programlisting>
<para>This very simple class takes a list of items, and returns one at a
time, removing it from the list. When the list empty, it returns null,

View File

@@ -27,12 +27,14 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/meta-data-erd.png" />
<imagedata align="center" fileref="images/meta-data-erd.png"
width="45%" />
</imageobject>
<imageobject role="html">
<imagedata fileref="src/site/docbook/reference/images/meta-data-erd.png"
format="PNG" />
<imagedata align="center"
fileref="src/site/docbook/reference/images/meta-data-erd.png"
width="45%" />
</imageobject>
</mediaobject>
@@ -518,13 +520,13 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
<listitem>
<para>The framework will use the meta-data tables to determine if a
particular JobInstance has been run before. If it has been run, and
the job is not restartable, then an exception will be thrown. </para>
the job is not restartable, then an exception will be thrown.</para>
</listitem>
<listitem>
<para>If an entry for a JobInstance is removed without having
completed successfully, the framework will think that the job is new,
rather than a restart. </para>
rather than a restart.</para>
</listitem>
<listitem>
@@ -536,4 +538,4 @@ INSERT INTO BATCH_JOB_SEQ values(0);</programlisting>
</listitem>
</itemizedlist>
</section>
</appendix>
</appendix>

View File

@@ -204,8 +204,8 @@
<programlisting>
&lt;step name="step1"&gt;
&lt;tasklet reader="itemReader" writer="itemWriter" commit-interval="10" <emphasis
role="bold">allow-start-if-complete="true"</emphasis>/&gt;
&lt;tasklet reader="itemReader" writer="itemWriter" commit-interval="10"
<emphasis role="bold">allow-start-if-complete="true"</emphasis>/&gt;
&lt;/step&gt;
</programlisting>
@@ -484,7 +484,8 @@
&lt;/tasklet&gt;
&lt;/step&gt;
&lt;beans:bean id="compositeWriter" class="org.springframework.batch.item.support.CompositeItemWriter"&gt;
&lt;beans:bean id="compositeWriter"
class="org.springframework.batch.item.support.CompositeItemWriter"&gt;
&lt;beans:property name="delegates"&gt;
&lt;beans:list&gt;
&lt;beans:ref bean="fileItemWriter1" /&gt;
@@ -972,13 +973,13 @@
<mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/sequential-flow.png"
scale="50" width="" />
scale="" width="40%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="src/site/docbook/reference/images/sequential-flow.png"
width="75%" />
width="40%" />
</imageobject>
</mediaobject>
@@ -1019,13 +1020,13 @@
rather than causing failure? <mediaobject>
<imageobject role="html">
<imagedata align="center" fileref="images/conditional-flow.png"
scale="75" width="" />
scale="" width="40%" />
</imageobject>
<imageobject role="fo">
<imagedata align="center"
fileref="src/site/docbook/reference/images/conditional-flow.png"
width="75%" />
width="40%" />
</imageobject>
</mediaobject></para>
@@ -1237,7 +1238,7 @@
</section>
<section>
<title>Creating File Names at Runtime</title>
<title>Late binding of Job and Step Attributes</title>
<para>Both the XML and Flat File examples above use the Spring
<classname>Resource</classname> abstraction to obtain the file to read
@@ -1246,11 +1247,14 @@
<classname>java.io.File</classname>. Both XML and Flat File resources
can be configured using standard Spring constructs:</para>
<programlisting> &lt;bean id="flatFileItemReader"
<programlisting>
&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;
&lt;/bean&gt;</programlisting>
&lt;/bean&gt;
</programlisting>
<para>The above <classname>Resource</classname> will load the file from
the file system, at the location specificied. Note that absolute
@@ -1260,10 +1264,13 @@
may need to be determined 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>
&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>
&lt;/bean&gt;
</programlisting>
<para>All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that
@@ -1275,35 +1282,63 @@
<para>Often in a batch setting it is preferable to parameterize the file
name in the <classname>JobParameters</classname> of the job, instead of
through system properties, and access them that way. To allow for this,
Spring Batch provides the
<classname>StepExecutionResourceProxy</classname>. The proxy can use
either job name, step name, or any values from the
<classname>JobParameters</classname>, by surrounding them with %:</para>
Spring Batch allows for the late binding of various Job and Step
attributes:</para>
<programlisting> &lt;bean id="inputFile"
class="org.springframework.batch.core.resource.StepExecutionResourceProxy" /&gt;
&lt;property name="filePattern" value="//%JOB_NAME%/%STEP_NAME%/%file.name%" /&gt;
&lt;/bean&gt;</programlisting>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{jobParameters[input.file.name]}" /&gt;
&lt;/bean&gt;
<para>Assuming a job name of 'fooJob', and a step name of 'fooStep', and
the key-value pair of 'file.name="fileName.txt"' is in the
<classname>JobParameters</classname> the job is started with, the
following filename will be passed as the
<classname>Resource</classname>:
"<filename>//fooJob/fooStep/fileName.txt</filename>". It should be noted
that in order for the proxy to have access to the
<classname>StepExecution</classname>, it must be registered as a
<classname>StepListener</classname>:</para>
</programlisting>
<programlisting> &lt;bean id="fooStep" parent="abstractStep"
p:itemReader-ref="itemReader"
p:itemWriter-ref="itemWriter"&gt;
&lt;property name="listeners" ref="inputFile" /&gt;
&lt;/bean&gt;</programlisting>
<para>Both the <classname>JobExecution</classname> and
<classname>StepExecution</classname> level
<classname>ExecutionContext</classname> can be access in the same
way:</para>
<para>The <classname>StepListener</classname> interface will be
discussed in more detail in Chapter 4. For now, it is sufficient to know
that the proxy must be registered.</para>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{<emphasis role="bold">jobExecutionContext</emphasis>[input.file.name]}" /&gt;
&lt;/bean&gt;
</programlisting>
<programlisting>
&lt;bean id="flatFileItemReader" scope="step"
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{<emphasis role="bold">stepExecutionContext</emphasis>[input.file.name]}" /&gt;
&lt;/bean&gt;
</programlisting>
<section>
<title>Step Scope</title>
<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>
class="org.springframework.batch.item.file.FlatFileItemReader"&gt;
&lt;property name="resource" value="#{jobParameters[input.file.name]}" /&gt;
&lt;/bean&gt;
</programlisting>
<para>Using a scope of <classname>Step</classname> is required in
order to use late binding, since the bean cannot actually be
instantiated until the <classname>Step</classname> starts, which
allows the attributes to be found. Because it is not part of the
Spring container by default, it must be added explicitely: </para>
<programlisting>
&lt;bean class="org.springframework.batch.core.scope.StepScope" /&gt;
</programlisting>
</section>
</section>
</section>
</chapter>