OPEN - issue BATCH-767: Update documentation for M2

http://jira.springframework.org/browse/BATCH-767

First pass through chapters 2, 3 and Appendix A.
This commit is contained in:
lucasward
2008-10-09 04:57:59 +00:00
parent 9e53779c72
commit 3225283a91
4 changed files with 95 additions and 231 deletions

View File

@@ -25,12 +25,14 @@
<tbody>
<row>
<entry align="left">AbstractBufferedItemReaderItemStream</entry>
<entry
align="left">AbstractItemCountingItemStreamItemReader</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Abstract base class that provides bufferring
functionality for any subclass.</entry>
<entry align="left">Abstract base class that provides basic
restart capabilities by counting the number of items returned from
an <classname>ItemReader</classname>.</entry>
</row>
<row>
@@ -42,15 +44,6 @@
time</entry>
</row>
<row>
<entry align="left">ValidatingItemReader</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">A simple extension of DelegatingItemReader
that provides for validation before returning input.</entry>
</row>
<row>
<entry align="left">ItemReaderAdapter</entry>
@@ -75,18 +68,6 @@
role="bold">END_RECORD</emphasis></entry>
</row>
<row>
<entry align="left">DelegatingItemReader</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Extends AbstractMethodInvokingDelegator, which
enables dynamically calling of a custom method of the injected
object. Provides a convenient API for dynamic method invocation
shielding subclasses from the low-level details and exception
handling.</entry>
</row>
<row>
<entry align="left">FlatFileItemReader</entry>
@@ -153,6 +134,26 @@
received through the injected JmsOperations receive()
method</entry>
</row>
<row>
<entry align="left">JpaPagingItemReader</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Given a JPQL statement, pages through the
rows, such that large datasets can be read without running out of
memory</entry>
</row>
<row>
<entry align="left">JdbcPagingItemReader</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Given a SQL statement, pages through the rows,
such that large datasets can be read without running out of
memory</entry>
</row>
</tbody>
</tgroup>
</table>
@@ -220,16 +221,6 @@
role="bold">ItemWriter</emphasis> objects</entry>
</row>
<row>
<entry align="left">DelegatingItemWriter</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Wraps ItemWriter and is BeanAware allowing it
to respond to Spring Bean events like
afterPropertiesSet().</entry>
</row>
<row>
<entry align="left">ItemWriterAdapter</entry>
@@ -251,17 +242,6 @@
name</entry>
</row>
<row>
<entry align="left">ItemTransformerItemWriter</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">Extends <emphasis
role="bold">DelegatingItemWriter</emphasis> by defining its
<emphasis role="bold">doProcess</emphasis> method to call an
injected <emphasis role="bold">ItemTransformer</emphasis></entry>
</row>
<row>
<entry align="left">FlatFileItemWriter</entry>
@@ -270,8 +250,8 @@
<entry align="left">Attempts to convert the item to a <emphasis
role="bold">String</emphasis>, <emphasis
role="bold">Collection</emphasis> or array using an injected
<emphasis role="bold">Coverter</emphasis> and then recurses. See
[HOWTO - Write to a File]</entry>
<emphasis role="bold">Coverter</emphasis> and then
recurses.</entry>
</row>
<row>
@@ -282,8 +262,19 @@
<entry align="left">This item writer is hibernate session aware
and handles some transaction-related work that a non-"hibernate
aware" item writer would not need to know about and then delegates
to another item writer to do the actual writing. See [HOWTO -
Write to a Database]</entry>
to another item writer to do the actual writing. </entry>
</row>
<row>
<entry align="left">JpaAwareItemWriter</entry>
<entry align="left">java.lang.Object</entry>
<entry align="left">This item writer is JPA EntityManager aware
and handles some transaction-related work that a non-"jpa aware"
<classname>ItemWriter</classname> would not need to know about and
then delegates to another writer to do the actual writing.
</entry>
</row>
<row>
@@ -294,7 +285,7 @@
<entry align="left">Uses an <emphasis
role="bold">ObjectToXmlSerializer</emphasis> implementation to
convert each item to XML and then writes it to an XML file using
StAX. See [HOWTO - Write to a File]</entry>
StAX.</entry>
</row>
</tbody>
</tgroup>

View File

@@ -605,9 +605,9 @@
<classname>Step</classname> interface. Two step implementation classes
are available in the Spring Batch framework, and they are each discussed
in detail in Chatper 4 of this guide. For most situations, the
<classname>StepHandlerStep</classname> implementation is sufficient,
but for situations where only one call is needed, such as a stored
procedure call or a wrapper around existing script, a
<classname>StepHandlerStep</classname> implementation is sufficient, but
for situations where only one call is needed, such as a stored procedure
call or a wrapper around existing script, a
<classname>TaskletStep</classname> may be a better option.</para>
</section>
@@ -1040,6 +1040,19 @@
various implementations can be found in Chapter 3.</para>
</section>
<section>
<title>Item Processor</title>
<para><classname>ItemProcessor</classname> is an abstraction that
represents the business processing of an item. While the
<classname>ItemReader</classname> reads one item, and the
<classname>ItemWriter</classname> writes them, the
<classname>ItemProcessor</classname> provides access to transform or apply
other business processing. If while processing the item it's determined
that it's not valid, returning null indicates that it should not be
written out.</para>
</section>
<section>
<title id="s.2.1.6">Tasklet</title>
@@ -1050,4 +1063,4 @@
logic that is not natural to split into read-(transform)-write phases,
such as invoking a system command or a stored procedure.</para>
</section>
</chapter>
</chapter>

View File

@@ -52,11 +52,8 @@
<programlisting>public interface ItemReader&lt;T&gt; {
T read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException;
T read() throws Exception, UnexpectedInputException, ParseException;
void mark() throws MarkFailedException;
void reset() throws ResetFailedException;
}
</programlisting>
@@ -68,13 +65,11 @@
(i.e. Trade, Foo, etc) but there is no requirement in the contract to do
so.</para>
<para>The <methodname>mark</methodname> and <methodname>reset</methodname>
methods are important due to the transactional nature of batch processing.
Mark() will be called before reading begins. Calling
<methodname>reset</methodname> at anytime will position the
<classname>ItemReader</classname> to its position when
<methodname>mark</methodname> was last called. The semantics are very
similar to <classname>java.io.Reader</classname>.</para>
<para>It is expected that implementations of the
<classname>ItemReader</classname> interface will be forward only. However,
if the underlying resource is transactional (such as a JMS queue) thehn
calling read may return the same logical item on subsequent calls in a
rollback scenario.</para>
<para>It is also worth noting that a lack of items to process by an
<classname>ItemReader</classname> will not cause an exception to be
@@ -99,31 +94,22 @@
<programlisting>public interface ItemWriter&lt;T&gt; {
void write(T item) throws Exception;
void write(List&lt;? extends T&gt; items) throws Exception;
void flush() throws FlushFailedException;
void clear() throws ClearFailedException;
}
</programlisting>
<para>As with <methodname>read</methodname> on
<classname>ItemReader</classname>, <methodname>write</methodname> provides
the basic contract of <classname>ItemWriter</classname>, it will attempt
to write out the item passed in as long as it is open. As with
<methodname>mark</methodname> and <methodname>reset</methodname>,
<methodname>flush</methodname> and <methodname>clear</methodname> are
necessary due to the transactional nature of batch processing. Because it
is generally expected that items will be 'batched' together into a chunk,
and then output, it is expected that an <classname>ItemWriter</classname>
will perform some type of buffering. <methodname>flush</methodname> will
empty the buffer by writing the items out, whereas
<methodname>clear</methodname> will simply throw the contents of the
buffer away. In most cases, a <classname>Step</classname> implementation
will call <methodname>flush</methodname> before a commit and
<methodname>clear</methodname> in case of rollback. It is expected that
implementations of the <classname>Step</classname> interface will call
these methods.</para>
to write out the list of items passed in as long as it is open. Because it
is generally expected that items will be 'batched' together into a chunk
and then output, the interface accepts a list, rather than an item by
itself. After writing out the list, any flushing that may be necessary can
be performed before returning from the write method. For example, if
writing to a Hibernate DAO, multiple calls to write can be made, one for
each item. The writer can then call close on the hibernate Session before
returning.</para>
</section>
<section>
@@ -324,6 +310,18 @@
</tgroup>
</table></para>
<section>
<title>LineMapper</title>
<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; {
T mapLine(String line, int lineNumber) throws Exception;
}</programlisting></para>
</section>
<section>
<title>FieldSetMapper</title>
@@ -2315,10 +2313,6 @@
}
return null;
}
public void mark() throws MarkFailedException { };
public void reset() throws ResetFailedException { };
}</programlisting>
<para>This very simple class takes a list of items, and returns one at a
@@ -2337,83 +2331,6 @@
assertEquals("3", itemReader.read());
assertNull(itemReader.read());</programlisting>
<section>
<title>Making the <classname>ItemReader</classname>
transactional</title>
<para>This most basic <classname>ItemReader</classname> will work, but
what happens if the transaction needs to be rolled back? This will
usually caused by an error in the ItemWriter, since the ItmReader
generally won't do anything that invalidates the transaction, but
without supporting it, there would be erroneous results. ItemReaders
are notified about rollbacks via the <methodname>mark</methodname> and
<methodname>reset</methodname> methods. In the example above they're
empty, but we'll need to add code to them in order to support the
rollback scenario:</para>
<programlisting> public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;{
List&lt;T&gt; items;
int currentIndex = 0;
int lastMarkedIndex = 0;
public CustomItemReader(List&lt;T&gt; items) {
this.items = items;
}
public T read() throws Exception, UnexpectedInputException,
NoWorkFoundException, ParseException {
if (currentIndex &lt; items.size()) {
return items.get(currentIndex++);
}
return null;
}
public void mark() throws MarkFailedException {
lastMarkedIndex = currentIndex;
};
public void reset() throws ResetFailedException {
currentIndex = lastMarkedIndex;
};
}</programlisting>
<para>The <classname>CustomItemReader</classname> has now been
modified to keep track of where it is currently, and where it was when
mark() was last called. This allows the new
<classname>ItemReader</classname> to fulfill the basic contract that
calling <methodname>reset</methodname> returns the
<classname>ItemReader</classname> to the state it was in when
<methodname>mark</methodname> was last called:</para>
<programlisting> //Assume same setup as last example, a list with "1", "2", and "3"
itemReader.mark();
assertEquals("1", itemReader.read());
assertEquals("2", itemReader.read());
itemReader.reset();
assertEquals("1", itemReader.read());</programlisting>
<para>In most real world scenarios, there will likely be some kind of
underlying resource that will require tracking. In the case of a file,
<methodname>mark</methodname> will hold the current location within
the file, and <methodname>reset</methodname> will move it back. The
<classname>JdbcCursorItemReader</classname>, for example, holds on to
the current row number, and on reset moves the cursor back by calling
the <classname> ResultSet</classname>
<methodname>absolute</methodname> method, which moves the current
cursor to the row number supplied. The
<classname>CustomItemReader</classname> now completely adheres to the
entire <classname>ItemReader</classname> contract.
<methodname>read</methodname> will return the appropriates items,
returning null when empty, and <methodname>reset</methodname> returns
the <classname>ItemReader</classname> back to it's state as of the
last call to <methodname>mark</methodname>, allowing for correct
support of a rollback. (It's assumed a <classname>Step</classname>
implementation will call <methodname>mark</methodname> and
<methodname>reset</methodname>).</para>
</section>
<section>
<title>Making the <classname>ItemReader</classname>
restartable</title>
@@ -2434,11 +2351,10 @@
implemented with the <classname>ItemStream</classname>
interface:</para>
<programlisting> public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;, ItemStream{
<programlisting> public class CustomItemReader&lt;T&gt; implements ItemReader&lt;T&gt;, ItemStream {
List&lt;T&gt; items;
int currentIndex = 0;
int lastMarkedIndex = 0;
private static final String CURRENT_INDEX = "current.index";
public CustomItemReader(List&lt;T&gt; items) {
@@ -2446,32 +2362,22 @@
}
public T read() throws Exception, UnexpectedInputException,
NoWorkFoundException, ParseException {
ParseException {
if (currentIndex &lt; items.size()) {
return items.get(currentIndex++);
}
return null;
}
public void mark() throws MarkFailedException {
lastMarkedIndex = currentIndex;
}
public void reset() throws ResetFailedException {
currentIndex = lastMarkedIndex;
}
public void open(ExecutionContext executionContext) throws ItemStreamException {
if(executionContext.containsKey(CURRENT_INDEX)){
currentIndex = new Long(executionContext.getLong(CURRENT_INDEX)).intValue();
}
else{
currentIndex = 0;
lastMarkedIndex = 0;
}
}
}
public void close(ExecutionContext executionContext) throws ItemStreamException {}
@@ -2534,65 +2440,19 @@
example. As with the <classname>ItemReader</classname> example, a List
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> public class CustomItemWriter&lt;T&gt; implements ItemWriter&lt;T&gt; {
List&lt;T&gt; output = new ArrayList&lt;T&gt;();
List&lt;T&gt; output = TransactionAwareProxyFactory.createTransactionalList();
public void write(T item) throws Exception {
output.add(item);
public void write(List&lt;? extends T&gt; items) throws Exception {
output.addAll(items);
}
public void clear() throws ClearFailedException { }
public void flush() throws FlushFailedException { }
}</programlisting>
<section>
<title>Making the <classname>ItemReader</classname>
transactional</title>
<para>The example is extremely simple, but it's worth showing to
illustrate an <classname>ItemWriter</classname> that doesn't respond
to rollbacks and commits (i.e. <methodname>clear</methodname> and
<methodname>flush</methodname>). If your potential writer is such that
it doesn't need to care about rollback or commit, likely because it's
writing to a database, then there is little value to the
<classname>ItemWriter</classname> interface in that scenario other
than using it to meet another class's requirement for an
implementation of the <classname>ItemWriter</classname> interface. In
that case, the <classname>ItemWriterAdapter</classname> would be a
better solution. However, if it does need to be transactional, then
<methodname>flush</methodname> and <methodname>clear</methodname>
should be implemented to allow for a buffering solution:</para>
<programlisting> public class CustomItemWriter&lt;T&gt; implements ItemWriter&lt;T&gt;{
List&lt;T&gt; output = new ArrayList&lt;T&gt;();
List&lt;T&gt; buffer = new ArrayList&lt;T&gt;();
public void write(T item) throws Exception {
buffer.add(item);
}
public void clear() throws ClearFailedException {
buffer.clear();
}
public void flush() throws FlushFailedException {
for(T t:buffer){
output.add(t);
}
public List&lt;T&gt; getOutput() {
return output;
}
}</programlisting>
<para>The <classname>ItemWriter</classname> buffers all output, only
writing to the actual output (in this case by added to a list) when
the <classname>ItemWriter</classname> <methodname>flush()</methodname>
method is called. The contents of the buffer are thrown away when
<classname>ItemWriter</classname> <methodname>clear</methodname>() is
called.</para>
</section>
<section>
<title>Making the <classname>ItemWriter</classname>
restartable</title>