diff --git a/docs/src/site/docbook/reference/readersAndWriters.xml b/docs/src/site/docbook/reference/readersAndWriters.xml
index f47fdb5fd..aebae3723 100644
--- a/docs/src/site/docbook/reference/readersAndWriters.xml
+++ b/docs/src/site/docbook/reference/readersAndWriters.xml
@@ -50,12 +50,12 @@
ItemReader is a basic interface for generic
input operations:
- public interface ItemReader<T> {
+ {
T read() throws Exception, UnexpectedInputException, ParseException;
}
-
+]]>
The read method defines the most essential
contract of the ItemReader, calling it returns one
@@ -92,12 +92,12 @@
As with ItemReader,
ItemWriter is a fairly generic interface:
- public interface ItemWriter<T> {
+ {
- void write(List<? extends T> items) throws Exception;
+ void write(List extends T> items) throws Exception;
}
-
+]]>
As with read on
ItemReader, write provides
@@ -121,7 +121,7 @@
readers and writers need to be opened, closed, and require a mechanism for
persisting state:
- public interface ItemStream {
+
+]]>
Before describing each method, its worth briefly mentioning the
ExecutionContext. Clients of an
@@ -189,11 +189,11 @@
fields so that the fields may be accessed either by index or name as
patterned after ResultSet:
- String[] tokens = new String[]{"foo", "1", "true"};
+
+ boolean booleanValue = fs.readBoolean(2);]]>
There are many more options on the FieldSet
interface, such as Date, long,
@@ -226,9 +226,9 @@
Framework, Chapter 4.Resources. Therefore, this
guide will not go into the details of creating
Resource objects. However, a simple example of a
- file system resource can be found below:
+ file system resource can be found below:
+ ]]>
In complex batch environments the directory structures are often
managed by the EAI infrastructure where drop zones for external
@@ -316,10 +316,10 @@
As with RowMapper, which takes a low
level construct such as ResultSet and returns an Object,
flat file procesing requires the same construct to convert a String
- line into an Object:public interface LineMapper<T> {
+ line into an Object: {
T mapLine(String line, int lineNumber) throws Exception;
-}
+}]]>
@@ -334,11 +334,11 @@
LineTokenizer to translate a line of data from
a resource into an object of the desired type:
- public interface FieldSetMapper<T> {
+ {
T mapFieldSet(FieldSet fieldSet);
- }
+ }]]>
The pattern used is the same as RowMapper
used by JdbcTemplate.
@@ -354,11 +354,11 @@
FieldSet is necessary. In Spring Batch, this is
called a LineTokenizer:
- public interface LineTokenizer {
+
+ }]]>
The contract of a LineTokenizer is such
that, given a line of input (in theory the
@@ -418,14 +418,14 @@
In code, the above flow looks like the following:
- String line = readLine();
+
+ return null;]]>
Exception handling has been removed for clarity.
@@ -433,16 +433,16 @@
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: ID,lastName,firstName,position,birthYear,debutYear
+ players from the following file:
+ "AdamCh00,Adams,Charlie,wr,1979,2003" ]]>
The contents of this file will be mapped to the following Player
- domain object:
+ domain object:
+ ]]>
In order to map a FieldSet into a Player
object, a FieldSetMapper that returns players
needs to be defined:
-
- protected static class PlayerFieldSetMapper implements FieldSetMapper<Player> {
+ {
public Object mapLine(FieldSet fieldSet) {
Player player = new Player();
@@ -482,14 +482,14 @@
return player;
}
- }
+ } ]]>
The file can then be read by correctly constructing a
FlatFileItemReader and calling
read:
-
- FlatFileItemReader<Player> itemReader = new FlatFileItemReader<Player>();
+ itemReader = new FlatFileItemReader();
itemReader.setResource(new FileSystemResource("resources/players.csv"));
//DelimitedLineTokenizer defaults to comma as it's delimiter
itemReader.setLineTokenizer(new DelimitedLineTokenizer());
@@ -497,7 +497,7 @@
itemReader.open(new ExecutionContext());
Player player = itemReader.read();
-
+]]>
Each call to read will return a new
Player object from each line in the file. When the end of the file is
@@ -515,9 +515,9 @@
fields in the flat file are injected into the
LineTokenizer:
-
+
+ ]]>
a FieldSetMapper can this use this
information as follows:
@@ -557,14 +557,14 @@
FieldSetMapper configuration looks like the
following:
- <bean id="fieldSetMapper"
- class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
- <property name="prototypeBeanName" value="player" />
- </bean>
+
+
+
- <bean id="player"
+
+ scope="prototype" />]]>
For each entry in the FieldSet, the
mapper will look for a corresponding setter on a new instance of the
@@ -584,11 +584,11 @@
organizations that use flat files use fixed length formats. An example
fixed length file is below:
- UK21341EAH4121131.11customer1
+
+ UK21341EAH4521535.11customer5]]>
While this looks like one large field, it actually represent 4
distinct fields:
@@ -618,14 +618,14 @@
FixedLengthLineTokenizer, each of these lengths
must be provided in the form of ranges:
-
- <bean id="fixedLengthLineTokenizer"
- class="org.springframework.batch.io.file.transform.FixedLengthTokenizer">
- <property name="names" value="ISIN, Quantity, Price, Customer" />
- <property name="columns" value="1-12, 13-15, 16-20, 21-29" />
- </bean>
+
+
+
+
-
+]]>
This LineTokenizer will return the same
FieldSet as if a dlimiter had been used,
@@ -637,18 +637,18 @@
specialized property editor be configured anywhere in the
ApplicationContext:
-
- <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
- <property name="customEditors">
- <map>
- <entry key="org.springframework.batch.item.file.transform.Range[]">
- <bean class="org.springframework.batch.item.file.transform.RangeArrayPropertyEditor" />
- </entry>
- </map>
- </property>
- </bean>
+
+
+
+
+
-
+]]>
@@ -660,7 +660,7 @@
might have records spanning multiple lines with multiple formats. The
following excerpt from a file illustrates this:
- HEA;0013100345;2007-02-15
+
+ FOT;2;2;267.34]]>
Everything between the line starting with 'HEA' and the line
starting with 'FOT' is considered one record. The
PrefixMatchingCompositeLineTokenizer makes this easier by matching the
prefix in a line with a particular tokenizer:
- <bean id="orderFileDescriptor"
- class="org.springframework.batch.io.file.transform.PrefixMatchingCompositeLineTokenizer">
- <property name="tokenizers">
- <map>
- <entry key="HEA" value-ref="headerRecordDescriptor" />
- <entry key="FOT" value-ref="footerRecordDescriptor" />
- <entry key="BCU" value-ref="businessCustomerLineDescriptor" />
- <entry key="NCU" value-ref="customerLineDescriptor" />
- <entry key="BAD" value-ref="billingAddressLineDescriptor" />
- <entry key="SAD" value-ref="shippingAddressLineDescriptor" />
- <entry key="BIN" value-ref="billingLineDescriptor" />
- <entry key="SIN" value-ref="shippingLineDescriptor" />
- <entry key="LIT" value-ref="itemLineDescriptor" />
- <entry key="" value-ref="defaultLineDescriptor" />
- </map>
- </property>
- </bean>
+
+
+
+
+ ]]>
This ensures that the line will be parsed correctly, which is
especially important for fixed length input. Any users of the
@@ -733,7 +733,7 @@
IncorrectTokenCountException is thrown, which contains the number of
tokens encountered, and the number expected:
-
+
+]]>
Because the tokenizer was configured with 4 columns, but only
3 tokens were found in the file, an IncorrectTokenCountException was
@@ -758,9 +758,9 @@
requirements when parsing because unlike a delimited format, each
column must strictly adhere to the width defined for it. If the
total line length doesn't add up to the widest value of this column,
- an exception is thrown:
+ an exception is thrown:
-
+
+]]>
The configured ranges for the tokenizer above are: 1-5, 6-10,
and 11-15, thus the total length of the line expected is 15.
@@ -785,14 +785,14 @@
For this reason, validation of line length can be turned off via the
'strict' property:
-
+
+]]>
The above example is almost identical to the one before it,
except the tokenizer.setStrict(false) was called. This setting tells
@@ -821,11 +821,11 @@
writing to a file. In Spring Batch this is the
LineAggregator:
- public interface LineAggregator<T> {
+ {
public String aggregate(T item);
- }
+ }]]>
The LineAggregator is the opposite of a
LineTokenizer.
@@ -862,21 +862,21 @@
FlatFileItemWriter expresses this in
code:
- public void write(T item) throws Exception {
+
+ }]]>
A simple configuration with the smallest ammount of setters
would look like the following:
- <bean id="itemWriter"
- class="org.springframework.batch.io.file.FlatFileItemWriter">
- <property name="resource"
- value="file:target/test-outputs/20070122.testStream.multilineStep.txt" />
- <property name="lineAggregator">
- <bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator"/>
- </property>
- </bean>
+
+
+
+
+
+ ]]>
@@ -980,28 +980,28 @@
stream. First, lets examine a set of XML records that the
StaxEventItemReader can process.
-
-<?xml version="1.0" encoding="UTF-8"?>
-<records>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0001</isin>
- <quantity>5</quantity>
- <price>11.39</price>
- <customer>Customer1</customer>
- </trade>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0002</isin>
- <quantity>2</quantity>
- <price>72.99</price>
- <customer>Customer2c</customer>
- </trade>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0003</isin>
- <quantity>9</quantity>
- <price>99.99</price>
- <customer>Customer3</customer>
- </trade>
-</records>
+
+
+
+ XYZ0001
+ 5
+ 11.39
+ Customer1
+
+
+ XYZ0002
+ 2
+ 72.99
+ Customer2c
+
+
+ XYZ0003
+ 9
+ 99.99
+ Customer3
+
+]]>
To be able to process the XML records the following is needed:
@@ -1023,22 +1023,22 @@
- <property name="itemReader">
- <bean class="org.springframework.batch.io.xml.StaxEventItemReader">
- <property name="fragmentRootElementName" value="trade" />
- <property name="resource" value="data/staxJob/input/20070918.testStream.xmlFileStep.xml" />
- <property name="fragmentDeserializer">
- <bean class="org.springframework.batch.io.xml.oxm.UnmarshallingEventReaderDeserializer">
- <constructor-arg>
- <bean class="org.springframework.oxm.xstream.XStreamMarshaller">
- <property name="aliases" ref="aliases" />
- </bean>
- </constructor-arg>
- </bean>
- </property>
- </bean>
-</property>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
Notice that in this example we have chosen to use an
XStreamMarshaller that requires an alias passed
@@ -1049,16 +1049,16 @@
the map. In the configuration file we can use a spring configuration
utility to describe the required alias as follows:
-
- <util:map id="aliases">
- <entry key="trade"
- value="org.springframework.batch.sample.domain.Trade" />
- <entry key="isin" value="java.lang.String" />
- <entry key="quantity" value="long" />
- <entry key="price" value="java.math.BigDecimal" />
- <entry key="customer" value="java.lang.String" />
- </util:map>
-
+
+
+
+
+
+
+
+ ]]>
On input the reader reads the XML resource until it recognizes a
new fragment is about to start (by matching the tag name by default).
@@ -1072,7 +1072,7 @@
injection provided by the spring configuration would look something like
the following:
-
+
+]]>
@@ -1118,13 +1118,13 @@
MarshallingEventWriterSerializer. The Spring
configuration for this setup looks as follows:
- <bean class="org.springframework.batch.item.xml.StaxEventItemWriter" id="tradeStaxWriter">
- <property name="resource"value="file:target/test-outputs/20070918.testStream.xmlFileStep.output.xml" />
- <property name="serializer" ref="tradeMarshallingSerializer" />
- <property name="rootTagName" value="trades" />
- <property name="overwriteOutput" value="true" />
-</bean>
-
+
+
+
+
+
+
+]]>
The configuration sets up the three required properties and
optionally sets the overwriteOutput=true, mentioned earlier in the
@@ -1144,7 +1144,7 @@
all of the points discussed, demonstrating the programmatic setup of the
required properties.
- StaxEventItemWriter staxItemWriter = new StaxEventItemWriter()
+
+ staxItemWriter.flush()]]>
For a complete example configuration of XML input and output and a
corresponding Job see the sample xmlStaxJob.
@@ -1189,11 +1189,11 @@
java.io.File. Both XML and Flat File resources can
be configured using standard Spring constructs:
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
- <property name="resource"
- value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" />
- </bean>
+
+
+ ]]>
The above Resource will load the file from
the file system, at the location specificied. Note that absolute locations
@@ -1203,10 +1203,10 @@
determined at runtime as a parameter to the job. This could be solved
using '-D' parameters, i.e. a system property:
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
- <property name="resource" value="${input.file.name}" />
-</bean>
+
+
+]]>
All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that although
@@ -1223,10 +1223,10 @@
either job name, step name, or any values from the
JobParameters, by surrounding them with %:
- <bean id="inputFile"
- class="org.springframework.batch.core.resource.StepExecutionResourceProxy" />
- <property name="filePattern" value="//%JOB_NAME%/%STEP_NAME%/%file.name%" />
- </bean>
+
+
+ ]]>
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
@@ -1237,11 +1237,11 @@
StepExecution, it must be registered as a
StepListener:
- <bean id="fooStep" parent="abstractStep"
+
+ p:itemWriter-ref="itemWriter">
+
+ ]]>
The StepListener interface will be discussed
in more detail in Chapter 4. For now, it is sufficient to know that the
@@ -1257,20 +1257,20 @@
for both XML and FlatFile processing. Consider the following files in a
directory:
- file-1.txt file-2.txt ignored.txt
+
file-1.txt and file-2.txt are formatted the same and for business
reasons should be processed together. The
MuliResourceItemReader can be used to read in both
files by using wildcards:
-
- <bean id="multiResourceReader" class="org.springframework.batch.item.SortedMultiResourceItemReader">
- <property name="resources" value="classpath:data/multiResourceJob/input/file-*.txt" />
- <property name="delegate" ref="flatFileItemReader" />
- </bean>
+
+
+
+
-
+]]>
The referenced delegate is a simple
FlatFileItemReader. The above configuration will
@@ -1357,18 +1357,18 @@
DataSource. The following database schema will
be used as an example:
- CREATE TABLE CUSTOMER (
+
+);]]>
Many people prefer to use a domain object for each row, so we'll
use an implementation of the RowMapper
interface to map a CustomerCredit
object:
- public class CustomerCreditRowMapper implements RowMapper {
+
+}]]>
Because JdbcTemplate is so familiar to
users of Spring, and the JdbcCursorItemReader
@@ -1395,12 +1395,12 @@
CUSTOMER database. The first example will be using
JdbcTemplate:
-
+
+]]>
After running this code snippet the customerCredits list will
contain 1,000 CustomerCredit objects. In the
@@ -1411,7 +1411,7 @@
constrast this with the approach of the
JdbcCursorItemReader:
-
+
+]]>
After running this code snippet the counter will equal 1,000. If
the code above had put the returned customerCredit into a list, the
@@ -1542,7 +1542,7 @@
configuration using the same 'customer credit' example as the JDBC
reader:
-
+
+]]>
This configured ItemReader will return
CustomerCredit objects in the exact same manner
@@ -1600,12 +1600,12 @@
The SimpleDelegatingPagingQueryProvider
requires that you specify a select clause and a from clause. You can
also provide an optional where clause. These clauses will be used to
- build an SQL statement combined with the required sortKey.
+ build an SQL statement combined with the required sortKey.
After the reader has been opened, it will pass back one item per
call to read in the same basic fashion as any
other ItemReader. The paging happens behind the
- scenes when additional rows are needed.
+ scenes when additional rows are needed.
Below is an example configuration using a similar 'customer
credit' example as the cursor based ItemReaders above:
@@ -1626,7 +1626,7 @@
-
+
]]>
@@ -1635,7 +1635,7 @@
CustomerCredit objects using the
ParameterizedRowMapper that must be specified.
The 'pageSize' property determines the number of entities read from
- the database for each query execution.
+ the database for each query execution.
The 'parameterValues' property can be used to specify a Map of
parameter values for the query. If you use named parameters in the
@@ -1683,6 +1683,56 @@
mapping file. The 'pageSize' property determines the number of
entities read from the database for each query execution.
+
+
+ IbatisPagingItemReader
+
+ If you use IBATIS for your data access ten you can use the
+ IbatisPagingItemReader which, as the name
+ indicates, is an implementation of a paging
+ ItemReader. IBATIS doesn't have direct support
+ for reading rows in pages but by providing a couple of standard
+ variables you can add paging support to your IBATIS queries.
+
+ Here is an example of a configuration for a
+ IbatisPagingItemReader reading CustomerCredits
+ as in the examples above:
+
+
+
+
+
+
+]]>
+
+ The IbatisPagingItemReader configuration
+ above references an IBATIS query called "getPagedCustomerCredits".
+ Here is an example of what that query should look like for
+ MySQL.
+
+
+ select id, name, credit from customer order by id asc LIMIT #_skiprows#, #_pagesize#
+
+]]>
+
+ The _skiprows and
+ _pagesize variables are provided by the
+ IbatisPagingItemReader and there is also a
+ _page variable that can be used if necessary.
+ The syntax for the paging queries varies with the database used. Here
+ is an example for Oracle (unfortunatey we need to use CDATA for some
+ operators since this belongs in an XML document):
+
+ <select id="getPagedCustomerCredits" resultMap="customerCreditResult">
+ select * from (
+ select * from (
+ select t.id, t.name, t.credit, ROWNUM ROWNUM_ from customer t order by id
+ ) where ROWNUM_ <![CDATA[ > ]]> ( #_page# * #_pagesize# )
+ ) where ROWNUM <![CDATA[ <= ]]> #_pagesize#
+ </select>
+
+
@@ -1746,12 +1796,12 @@
real complication is how those keys are obtained. The
KeyCollector interface abstracts this:
- public interface KeyCollector {
+
+ }]]>
The primary method in this interface is the
retrieveKeys method. It is expected that this
@@ -1766,12 +1816,12 @@
retrieveKeys method can then use this value
to retrieve a subset of the original keys:
- ExecutionContext executionContext = new ExecutionContext();
+
+ //keys should now contains 500 through 1,000]]>
This generalization illustrates the
KeyCollector contract. If we assume that
@@ -1834,36 +1884,36 @@
The following code helps illustrate how to setup and use a
SingleColumnJdbcKeyCollector:
- SingleColumnJdbcKeyCollector keyCollector = new SingleColumnJdbcKeyCollector(getJdbcTemplate(),
+ ? order by ID");
ExecutionContext executionContext = new ExecutionContext();
List keys = keyStrategy.retrieveKeys(new ExecutionContext());
- for (int i = 0; i < keys.size(); i++) {
+ for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
- }
+ }]]>
If this code were run in the proper environment with the correct
database tables setup, then it would output the following:
- 1
+
+5]]>
Now, let's modify the code slightly to show what would happen if
the code were started again after a restart, having failed after
processing key 3 successfully:
- SingleColumnJdbcKeyCollector keyCollector = new SingleColumnJdbcKeyCollector(getJdbcTemplate(),
+ ? order by ID");
ExecutionContext executionContext = new ExecutionContext();
@@ -1871,19 +1921,19 @@
List keys = keyStrategy.retrieveKeys(executionContext);
- for (int i = 0; i < keys.size(); i++) {
+ for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
- }
+ }]]>
Running this code snippet would result in the following:
- 4
-5
+
The key difference between the two examples is the following
line:
- keyStrategy.updateContext(new Long(3), executionContext);
+
This tells the key collector to update the provided
ExecutionContext with the key of three. This
@@ -1894,7 +1944,7 @@
ExecutionContext that was updated to contain 3,
the argument of 3 will be passed to the restartSql:
- keyCollector.setRestartSql("SELECT ID from T_FOOS where ID > ? order by ID");
+ ? order by ID");]]>
This will cause only keys 4 and 5 to be returned, since they are
the only ones with an ID greater than 3.
@@ -1921,13 +1971,13 @@
An ExecutionContextRowMapper provides
this:
- public interface ExecutionContextRowMapper extends RowMapper {
+
+]]>
The ExecutionContextRowMapper interface
extends the standard RowMapper interface to
@@ -2025,36 +2075,36 @@
items to be skipped reliably. The following example illustrates how to
configure the HibernateAwareItemWriter:
- <bean id="hibernateItemWriter"
- class="org.springframework.batch.item.database.HibernateAwareItemWriter">
- <property name="sessionFactory" ref="sessionFactory" />
- <property name="delegate" ref="customerCreditWriter" />
- </bean>
+
+
+
+
- <bean id="customerCreditWriter"
- class="org.springframework.batch.sample.dao.HibernateCreditDao">
- <property name="sessionFactory" ref="sessionFactory" />
- </bean>
+
+
+
-
+]]>
If you are using JPA then the
JpaAwareItemWriter provides comparable
functionality. The following example illustrates how to configure the
JpaAwareItemWriter:
- <bean id="hibernateItemWriter"
- class="org.springframework.batch.item.database.JpaAwareItemWriter">
- <property name="entityManagerFactory" ref="entityManagerFactory" />
- <property name="delegate" ref="customerCreditWriter" />
- </bean>
+
+
+
+
- <bean id="customerCreditWriter"
- class="org.springframework.batch.sample.dao.JpaCreditDao">
- <property name="entityManagerFactory" ref="entityManagerFactory" />
- </bean>
+
+
+
-
+]]>
@@ -2079,12 +2129,12 @@
standard Spring method invoking delegator pattern and are fairly simple to
set up. Below is an example of the reader:
- <bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter">
- <property name="targetObject" ref="fooService" />
- <property name="targetMethod" value="generateFoo" />
- </bean>
+
+
+
+
- <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
+ ]]>
One important point to note is that the contract of the targetMethod
must be the same as the contract for read: when
@@ -2095,13 +2145,13 @@
ItemWriter. The ItemWriter
implementation is equally as simple:
- <bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
- <property name="targetObject" ref="fooService" />
- <property name="targetMethod" value="processFoo" />
- </bean>
+
+
+
+
- <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
-
+
+]]>
@@ -2116,11 +2166,11 @@
that contains another ItemReader. For
example:
- public class CompositeItemWriter<T> implements ItemWriter<T> {
+ implements ItemWriter {
- ItemWriter<T> itemWriter;
+ ItemWriter itemWriter;
- public CompositeItemWriter(ItemWriter<T> itemWriter) {
+ public CompositeItemWriter(ItemWriter itemWriter) {
this.itemWriter = itemWriter;
}
@@ -2138,7 +2188,7 @@
public void flush() throws FlushFailedException {
itemWriter.flush();
}
-}
+}]]>
The class above contains another ItemWriter
that it delgates to after having provided some business logic. It should
@@ -2155,10 +2205,10 @@
just want to modify the item. For this scenario, Spring Batch provides the
ItemTransformer interface:
- public interface ItemProcessor<I, O> {
+ {
O process(I item) throws Exception;
-}
+}]]>
An ItemTransformer is very simple, given one
object, transorm it and return another. The object provided may or may not
@@ -2174,7 +2224,7 @@
ItemTransformer can be written that performs the
conversion:
- public class Foo {}
+
+ }]]>
In the very simple example above, there is a class
Foo, a class Bar, and a
@@ -2214,10 +2264,10 @@
Bar returned. The resulting
Bar will then be written:
- ItemTransformerItemWriter itemTransformerItemWriter = new ItemTransformerItemWriter();
+
+ itemTransformerItemWriter.write(new Foo());]]>
The Delegate Pattern and Registering with the Step
@@ -2250,7 +2300,7 @@
Transformed to Bar, which will be transformed to
Foobar and written out:
- public class Foo {}
+
+ }]]>
A FooTransformer and
BarTransformer can be 'chained' together to give
the resultant Foobar:
- CompositeItemProcessor compositeTransformer = new CompositeItemProcessor();
+
+ compositeTransformer.setItemTransformers(itemTransformers);]]>
The compositeTransformer could be said to accept a
Foo and return a Foobar.
@@ -2331,11 +2381,11 @@
rather provides a very simple interface that can be implemented by any
number of frameworks:
- public interface Validator {
+
+ }]]>
The contract is that the validate method
will throw an exception if the object is invalid, and return normally if
@@ -2464,11 +2514,11 @@
basic contract of ItemReader,
read:
- public class CustomItemReader<T> implements ItemReader<T>{
+ implements ItemReader{
- List<T> items;
+ List items;
- public CustomItemReader(List<T> items) {
+ public CustomItemReader(List items) {
this.items = items;
}
@@ -2480,23 +2530,23 @@
}
return null;
}
- }
+ }]]>
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,
thus satisfying the most basic requirements of an
ItemReader, as illustrated below:
- List<String> items = new ArrayList<String>();
+ items = new ArrayList();
items.add("1");
items.add("2");
items.add("3");
- ItemReader itemReader = new CustomItemReader<String>(items);
+ ItemReader itemReader = new CustomItemReader(items);
assertEquals("1", itemReader.read());
assertEquals("2", itemReader.read());
assertEquals("3", itemReader.read());
- assertNull(itemReader.read());
+ assertNull(itemReader.read());]]>
Making the ItemReader
@@ -2518,20 +2568,20 @@
implemented with the ItemStream
interface:
- public class CustomItemReader<T> implements ItemReader<T>, ItemStream {
+ implements ItemReader, ItemStream {
- List<T> items;
+ List items;
int currentIndex = 0;
private static final String CURRENT_INDEX = "current.index";
- public CustomItemReader(List<T> items) {
+ public CustomItemReader(List items) {
this.items = items;
}
public T read() throws Exception, UnexpectedInputException,
ParseException {
- if (currentIndex < items.size()) {
+ if (currentIndex < items.size()) {
return items.get(currentIndex++);
}
@@ -2552,7 +2602,7 @@
public void update(ExecutionContext executionContext) throws ItemStreamException {
executionContext.putLong(CURRENT_INDEX, new Long(currentIndex).longValue());
};
- }
+ }]]>
On each call to ItemStream
update method, the current index of the
@@ -2564,19 +2614,19 @@
current index is moved to that location. This is a fairly trivial
example, but it still meets the general contract:
- ExecutionContext executionContext = new ExecutionContext();
+ items = new ArrayList();
items.add("1");
items.add("2");
items.add("3");
- itemReader = new CustomItemReader<String>(items);
+ itemReader = new CustomItemReader(items);
((ItemStream)itemReader).open(executionContext);
- assertEquals("2", itemReader.read());
+ assertEquals("2", itemReader.read());]]>
Most ItemReaders have much more sophisticated restart logic. The
DrivingQueryItemReader, for example, only loads
@@ -2607,18 +2657,18 @@
example. As with the ItemReader example, a List
will be used in order to keep the example as simple as possible:
- public class CustomItemWriter<T> implements ItemWriter<T> {
+ implements ItemWriter {
- List<T> output = TransactionAwareProxyFactory.createTransactionalList();
+ List output = TransactionAwareProxyFactory.createTransactionalList();
- public void write(List<? extends T> items) throws Exception {
+ public void write(List extends T> items) throws Exception {
output.addAll(items);
}
- public List<T> getOutput() {
+ public List getOutput() {
return output;
}
- }
+ }]]>