From b716c94eba1fef9b20c5c6c6db872fa8ff33a731 Mon Sep 17 00:00:00 2001 From: dhgarrette Date: Fri, 5 Jun 2009 23:45:59 +0000 Subject: [PATCH] BATCH-1270: Update documentation for formatting consistency --- src/site/docbook/reference/appendix.xml | 10 +- .../docbook/reference/common-patterns.xml | 210 +++++++++--------- src/site/docbook/reference/domain.xml | 4 +- src/site/docbook/reference/job.xml | 52 ++--- .../docbook/reference/readersAndWriters.xml | 149 ++++++------- src/site/docbook/reference/repeat.xml | 37 ++- src/site/docbook/reference/retry.xml | 26 +-- src/site/docbook/reference/scalability.xml | 64 +++--- .../docbook/reference/schema-appendix.xml | 32 +-- src/site/docbook/reference/step.xml | 2 +- src/site/docbook/reference/testing.xml | 154 ++++++------- src/site/docbook/reference/whatsnew.xml | 2 +- 12 files changed, 352 insertions(+), 390 deletions(-) diff --git a/src/site/docbook/reference/appendix.xml b/src/site/docbook/reference/appendix.xml index 48cb76dbe..8c59b39de 100644 --- a/src/site/docbook/reference/appendix.xml +++ b/src/site/docbook/reference/appendix.xml @@ -1,10 +1,10 @@ - - List of ItemReaders + + List of ItemReaders and ItemWriters -
+
Item Readers @@ -124,7 +124,7 @@
-
+
Item Writers @@ -194,7 +194,7 @@ JdbcBatchItemWriter - Uses batching freatures from a + Uses batching features from a PreparedStatement, if available, and can take rudimentary steps to locate a failure during a flush. diff --git a/src/site/docbook/reference/common-patterns.xml b/src/site/docbook/reference/common-patterns.xml index 90609cdf8..9e3f132ed 100644 --- a/src/site/docbook/reference/common-patterns.xml +++ b/src/site/docbook/reference/common-patterns.xml @@ -24,7 +24,7 @@ ItemWriter can implement a listener interface as well, if appropriate. -
+
Logging Item Processing and Failures A common use case is the need for special handling of errors in a @@ -53,13 +53,12 @@ Having implemented this listener it must be registered with the step: - <bean id="simpleStep" - class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" > + <step id="simpleStep"> ... - <property name="listeners"> - <bean class="org.example...ItemFailureLoggerListener"/> - </property> - </bean> + <listeners> + <listener class="org.example...ItemFailureLoggerListener"/> + </listeners> +</step> Remember that if your listener does anything in an onError() method, it will be inside a transaction that is @@ -70,7 +69,7 @@ REQUIRES_NEW.
-
+
Stopping a Job Manually for Business Reasons Spring Batch provides a stop() method @@ -87,11 +86,9 @@ public class PoisonPillItemWriter implements ItemWriter<T> { public void write(T item) throws Exception { - if (isPoisonPill(item)) { - throw new PoisonPillException("Posion pill detected: "+item); + throw new PoisonPillException("Posion pill detected: " + item); } - } } @@ -106,15 +103,11 @@ public void setDelegate(ItemReader<T> delegate) { ... } public T read() throws Exception { - T item = delegate.read(); - if (isEndItem(item)) { return null; // end the step here } - return item; - } } @@ -126,13 +119,14 @@ injected into the Step through the SimpleStepFactoryBean: - <bean id="simpleStep" - class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" > - ... - <property name="chunkCompletionPolicy"> - <bean class="org.example...SpecialCompletionPolicy"/> - </property> - </bean> + <step id="simpleStep"> + <tasklet> + <chunk reader="reader" writer="writer" commit-interval="10" + chunk-completion-policy="completionPolicy"/> + </tasklet> +</step> + +<bean id="completionPolicy" class="org.example...SpecialCompletionPolicy"/> An alternative is to set a flag in the StepExecution, which is checked by the @@ -166,7 +160,7 @@ an abnormal ending to a job.
-
+
Adding a Footer Record Often when writing to flat files, a "footer" record must be appended @@ -177,25 +171,23 @@ FlatFileHeaderCallback) are optional properties of the FlatFileItemWriter: - <bean id="itemWriter" - class="org.springframework.batch.item.file.FlatFileItemWriter"> + <bean id="itemWriter" class="org.spr...FlatFileItemWriter"> <property name="resource" ref="outputResource" /> <property name="lineAggregator" ref="lineAggregator"/> <property name="headerCallback" ref="headerCallback" /> <property name="footerCallback" ref="footerCallback" /> - </bean> - +</bean> The footer callback interface is very simple. It has just one method that is called when the footer must be written: - public interface FlatFileFooterCallback { + public interface FlatFileFooterCallback { - void writeFooter(Writer writer) throws IOException; + void writeFooter(Writer writer) throws IOException; - } +} -
+
Writing a Summary Footer A very common requirement involving footer records is to aggregate @@ -208,10 +200,11 @@ Trades is placed in a footer, then the following ItemWriter implementation can be used: - public class TradeItemWriter implements ItemWriter<Trade>, - FlatFileFooterCallback { + public class TradeItemWriter implements ItemWriter<Trade>, + FlatFileFooterCallback { private ItemWriter<Trade> delegate; + private double totalAmount = 0.0; public void write(List<? extends Trade> items) { @@ -226,7 +219,7 @@ } public void setDelegate(ItemWriter delegate) {...} - } +} This TradeItemWriter stores a totalAmount value that is increased with the @@ -238,17 +231,15 @@ the FlatFileItemWriter as the footerCallback: - <bean id="tradeItemWriter" class="..TradeItemWriter"> - <property name="delegate" ref="flatFileItemWriter" /> - </bean> + <bean id="tradeItemWriter" class="..TradeItemWriter"> + <property name="delegate" ref="flatFileItemWriter" /> +</bean> - <bean id="flatFileItemWriter" - class="org.springframework.batch.item.file.FlatFileItemWriter"> - <property name="resource" ref="outputResource" /> - <property name="lineAggregator" ref="lineAggregator"/> - <property name="footerCallback" ref="tradeItemWriter" /> - </bean> - +<bean id="flatFileItemWriter" class="org.spr...FlatFileItemWriter"> + <property name="resource" ref="outputResource" /> + <property name="lineAggregator" ref="lineAggregator"/> + <property name="footerCallback" ref="tradeItemWriter" /> +</bean> The way that the TradeItemWriter has been so far will only function correctly if the Step @@ -290,7 +281,7 @@
-
+
Driving Query Based ItemReaders In the chapter on readers and writers, database input using paging @@ -347,10 +338,10 @@ spanning multiple lines with multiple formats. The following excerpt from a file illustrates this: - HEA;0013100345;2007-02-15 - NCU;Smith;Peter;;T;20014539;F - BAD;;Oak Street 31/A;;Small Town;00235;IL;US - FOT;2;2;267.34 + HEA;0013100345;2007-02-15 +NCU;Smith;Peter;;T;20014539;F +BAD;;Oak Street 31/A;;Small Town;00235;IL;US +FOT;2;2;267.34 Everything between the line starting with 'HEA' and the line starting with 'FOT' is considered one record. There are a few @@ -376,22 +367,21 @@ ItemReader should be implemented as a wrapper for the FlatFileItemReader. - <bean id="itemReader" - class="org.springframework.batch.sample.iosample.internal.MultiLineTradeItemReader"> + <bean id="itemReader" class="org.spr...MultiLineTradeItemReader"> <property name="delegate"> - <bean class="org.springframework.batch.item.file.FlatFileItemReader"> - <property name="resource" value="data/iosample/input/multiLine.txt" /> - <property name="lineMapper"> - <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"> - <property name="lineTokenizer" ref="orderFileTokenizer"/> - <property name="fieldSetMapper"> - <bean class="org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper" /> + <bean class="org.springframework.batch.item.file.FlatFileItemReader"> + <property name="resource" value="data/iosample/input/multiLine.txt" /> + <property name="lineMapper"> + <bean class="org.spr...DefaultLineMapper"> + <property name="lineTokenizer" ref="orderFileTokenizer"/> + <property name="fieldSetMapper"> + <bean class="org.spr...PassThroughFieldSetMapper" /> + </property> + </bean> </property> - </bean> - </property> - </bean> + </bean> </property> - </bean> +</bean> To ensure that each line is tokenized properly, which is especially important for fixed length input, the @@ -402,17 +392,16 @@ deliver a FieldSet for each line back to the wrapping ItemReader. - <bean id="orderFileTokenizer" - class="org.springframework.batch.io.file.transform.PatternMatchingCompositeLineTokenizer"> + <bean id="orderFileTokenizer" class="org.spr...PatternMatchingCompositeLineTokenizer"> <property name="tokenizers"> - <map> - <entry key="HEA*" value-ref="headerRecordTokenizer" /> - <entry key="FOT*" value-ref="footerRecordTokenizer" /> - <entry key="NCU*" value-ref="customerLineTokenizer" /> - <entry key="BAD*" value-ref="billingAddressLineTokenizer" /> - </map> + <map> + <entry key="HEA*" value-ref="headerRecordTokenizer" /> + <entry key="FOT*" value-ref="footerRecordTokenizer" /> + <entry key="NCU*" value-ref="customerLineTokenizer" /> + <entry key="BAD*" value-ref="billingAddressLineTokenizer" /> + </map> </property> - </bean> +</bean> This wrapper will have to be able recognize the end of a record so that it can continually call read() on its @@ -422,38 +411,38 @@ ItemProcessor and ItemWriter. - private FlatFileItemReader<FieldSet> delegate; + private FlatFileItemReader<FieldSet> delegate; - public Trade read() throws Exception { +public Trade read() throws Exception { Trade t = null; for (FieldSet line = null; (line = this.delegate.read()) != null;) { - String prefix = line.readString(0); - if (prefix.equals("HEA")) { - t = new Trade(); // Record must start with header - } - else if (prefix.equals("NCU")) { - Assert.notNull(t, "No header was found."); - t.setLast(line.readString(1)); - t.setFirst(line.readString(2)); - ... - } - else if (prefix.equals("BAD")) { - Assert.notNull(t, "No header was found."); - t.setCity(line.readString(4)); - t.setState(line.readString(6)); - ... - } - else if (prefix.equals("FOT")) { - return t; // Record must end with footer - } + String prefix = line.readString(0); + if (prefix.equals("HEA")) { + t = new Trade(); // Record must start with header + } + else if (prefix.equals("NCU")) { + Assert.notNull(t, "No header was found."); + t.setLast(line.readString(1)); + t.setFirst(line.readString(2)); + ... + } + else if (prefix.equals("BAD")) { + Assert.notNull(t, "No header was found."); + t.setCity(line.readString(4)); + t.setState(line.readString(6)); + ... + } + else if (prefix.equals("FOT")) { + return t; // Record must end with footer + } } Assert.isNull(t, "No 'END' was found."); return null; - } +}
-
+
Executing System Commands Many batch jobs may require that an external command be called from @@ -466,15 +455,15 @@ Tasklet implementation for calling system commands: - <bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet"> + <bean class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet"> <property name="command" value="echo hello" /> <!-- 5 second timeout for the command to complete --> <property name="timeout" value="5000" /> - </bean> +</bean>
-
- Handling Step completion when no input is found +
+ Handling Step Completion When No Input is Found In many batch scenarios, finding no rows in a database or file to process is not exceptional. The Step is simply @@ -490,17 +479,16 @@ a common use case, a listener is provided with just this functionality: - - public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport { + public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport { - public ExitStatus afterStep(StepExecution stepExecution) { - if (stepExecution.getReadCount() == 0) { - return ExitStatus.FAILED; + public ExitStatus afterStep(StepExecution stepExecution) { + if (stepExecution.getReadCount() == 0) { + return ExitStatus.FAILED; + } + return null; } - return null; - } - +} The above StepExecutionListener inspects the readCount property of the StepExecution during the @@ -511,8 +499,8 @@ Step.
-
- Passing data to future steps +
+ Passing Data to Future Steps It is often useful to pass information from one step to another. This can be done using the ExecutionContext. The @@ -580,8 +568,7 @@ </step> </job> -<beans:bean id="promotionListener" - class="org.spr....ExecutionContextPromotionListener"> +<beans:bean id="promotionListener" class="org.spr....ExecutionContextPromotionListener"> <beans:property name="keys" value="someKey"/> </beans:bean> @@ -597,7 +584,8 @@ @BeforeStep public void saveStepExecution(StepExecution stepExecution) { - ExecutionContext jobContext = stepExecution.getJobExecution().getExecutionContext(); + JobExecution jobExecution = stepExecution.getJobExecution(); + ExecutionContext jobContext = jobExecution.getExecutionContext(); this.someObject = jobContext.get("someKey"); } } diff --git a/src/site/docbook/reference/domain.xml b/src/site/docbook/reference/domain.xml index f646eb6b2..05617b41a 100644 --- a/src/site/docbook/reference/domain.xml +++ b/src/site/docbook/reference/domain.xml @@ -218,7 +218,7 @@ controlled and persisted:
- JobExecution properties + JobExecution Properties @@ -592,7 +592,7 @@ StepExecution:
- StepExecution properties + StepExecution Properties diff --git a/src/site/docbook/reference/job.xml b/src/site/docbook/reference/job.xml index 3a20276df..e65881e9f 100644 --- a/src/site/docbook/reference/job.xml +++ b/src/site/docbook/reference/job.xml @@ -2,7 +2,7 @@ - Configuring and Running A Job + Configuring and Running a Job In , the overall architecture design was discussed, using the following diagram as a guide: @@ -27,7 +27,7 @@ stored during that run. This chapter will explain the various configuration options and runtime concerns of a Job. -
+
Configuring a Job There are multiple implementations of the -
+
Restartability - One key issue when execution a batch job concerns the behavior of + One key issue when executing a batch job concerns the behavior of a Job when it is restarted? The launching of a Job is considered to be a 'restart' if a JobExecution already exists for the particular @@ -99,8 +99,8 @@ catch (JobRestartException e) { attempt will throw a JobRestartException.
-
- Intercepting Job execution +
+ Intercepting Job Execution During the course of the execution of a Job, it may be useful to be notified of various @@ -156,7 +156,7 @@ catch (JobRestartException e) {
-
+
Inheriting from a Parent Job If a group of Jobs share similar, but not @@ -193,7 +193,7 @@ catch (JobRestartException e) { for more detailed information.
-
+
JobFactory and Stateful Components in Steps Unlike many traditional Spring applications, many of the @@ -221,7 +221,7 @@ catch (JobRestartException e) {
-
+
Configuring a JobRepository As described in earlier, the <job-repository id="jobRepository" dataSource="dataSource" transactionManager="transactionManager" - isolation-level-for-create="serializable" + isolation-level-for-create="SERIALIZABLE" table-prefix="BATCH_" /> @@ -247,8 +247,8 @@ catch (JobRestartException e) { the id. If they are not set, the defaults shown above will be used. They are shown above for awareness purposes. -
- Transaction Configuration For the JobRepository +
+ Transaction Configuration for the JobRepository If the namespace is used, transactional advice will be automatically created around the repository. This is to ensure that the @@ -292,7 +292,7 @@ catch (JobRestartException e) {
- Changing the table prefix + Changing the Table Prefix Another modifiable property of the JobRepository is the table prefix of the @@ -316,7 +316,7 @@ catch (JobRestartException e) {
-
+
In-Memory Repository There are scenarios in which you may not want to persist your @@ -344,7 +344,7 @@ catch (JobRestartException e) { ResourcelessTransactionManager useful.
-
+
Non-standard Database Types in a Repository If you are using a database platform that is not in the list of @@ -447,7 +447,7 @@ catch (JobRestartException e) { executed.
-
+
Running a Job At a minimum, launching a batch job requires two things: the @@ -461,7 +461,7 @@ catch (JobRestartException e) { JobLauncher, configured for asynchronous job launching, that multiple requests will invoke to launch their jobs. -
+
Running Jobs from the Command Line For users that want to run their jobs from an enterprise @@ -473,7 +473,7 @@ catch (JobRestartException e) { even 'build tools' such as ant or maven. However, because most people are familiar with shell scripts, this example will focus on them. -
+
The CommandLineJobRunner Because the script launching the job must kick off a Java @@ -565,7 +565,7 @@ catch (JobRestartException e) { JobLauncher
-
+
ExitCodes When launching a batch job from the command-line, an enterprise @@ -623,8 +623,8 @@ catch (JobRestartException e) {
-
- Running Jobs from within a web container +
+ Running Jobs from within a Web Container Historically, offline processing such as batch jobs have been launched from the command-line, as described above. However, there are @@ -719,8 +719,8 @@ public class JobLauncherController { below, add additional functionality for querying and controlling the meta data. -
- Querying the repository +
+ Querying the Repository The most basic need before any advanced features is the ability to query the repository for existing executions. This functionality is @@ -761,7 +761,7 @@ public class JobLauncherController { p:dataSource-ref="dataSource" p:tablePrefix="BATCH_" />
-
+
JobOperator As previously discussed, the JobRepository @@ -827,7 +827,7 @@ public class JobLauncherController { </bean>
-
+
JobParametersIncrementer Most of the methods on JobOperator are @@ -894,7 +894,7 @@ public class JobLauncherController { </job>
-
+
Stopping a Job One of the most common use cases of diff --git a/src/site/docbook/reference/readersAndWriters.xml b/src/site/docbook/reference/readersAndWriters.xml index b87ee0fea..eae2ab122 100644 --- a/src/site/docbook/reference/readersAndWriters.xml +++ b/src/site/docbook/reference/readersAndWriters.xml @@ -11,7 +11,7 @@ ItemReader, ItemProcessor and ItemWriter. -
+
ItemReader Although a simple concept, an ItemReader is @@ -70,7 +70,7 @@ the first invocation of read.
-
+
ItemWriter ItemWriter is similar in functionality to an @@ -103,7 +103,7 @@ Session before returning.
-
+
ItemProcessor The ItemReader and @@ -200,7 +200,7 @@ public class BarWriter implements ItemWriter<Bar>{ </step> </job> -
+
Chaining ItemProcessors Performing a single transformation is useful in many scenarios, @@ -274,7 +274,7 @@ compositeProcessor.setItemProcessors(itemProcessors); </bean>
-
+
Filtering Records One typical use for an item processor is to filter out records @@ -350,7 +350,7 @@ compositeProcessor.setItemProcessors(itemProcessors); Quartz JobDataMap.
-
+
The Delegate Pattern and Registering with the Step Note that the CompositeItemWriter is an @@ -387,7 +387,7 @@ compositeProcessor.setItemProcessors(itemProcessors); <bean id="barWriter" class="...BarWriter" />
-
+
Flat Files One of the most common mechanisms for interchanging bulk data has @@ -398,7 +398,7 @@ compositeProcessor.setItemProcessors(itemProcessors); files are those in which fields are separated by a delimiter, such as a comma. Fixed Length files have fields that are a set length. -
+
The FieldSet When working with flat files in Spring Batch, regardless of @@ -562,7 +562,7 @@ boolean booleanValue = fs.readBoolean(2);
-
+
LineMapper As with RowMapper, which takes a low @@ -590,7 +590,7 @@ boolean booleanValue = fs.readBoolean(2); mapped to an object, as described below.
-
+
LineTokenizer An abstraction for turning a line of input into a line into a @@ -636,7 +636,7 @@ boolean booleanValue = fs.readBoolean(2);
-
+
FieldSetMapper The FieldSetMapper interface defines a @@ -659,7 +659,7 @@ boolean booleanValue = fs.readBoolean(2); JdbcTemplate.
-
+
DefaultLineMapper Now that the basic interfaces for reading in flat files have @@ -721,7 +721,7 @@ boolean booleanValue = fs.readBoolean(2); to the raw line is needed.
-
+
Simple Delimited File Reading Example The following example will be used to illustrate this using an @@ -794,8 +794,8 @@ Player player = itemReader.read(); reached, null will be returned.
-
- Mapping fields by name +
+ Mapping Fields by Name There is one additional piece of functionality that is allowed by both DelimitedLineTokenizer and @@ -808,8 +808,8 @@ Player player = itemReader.read(); tokenizer.setNames(new String[] {"ID", "lastName","firstName","position","birthYear","debutYear"}); - a FieldSetMapper can this use this - information as follows: + A FieldSetMapper can use this information + as follows: public class PlayerMapper implements FieldSetMapper<Player> { public Player mapFieldSet(FieldSet fs) { @@ -831,7 +831,7 @@ Player player = itemReader.read(); }
-
+
Automapping FieldSets to Domain Objects For many, having to write a specific @@ -863,8 +863,8 @@ Player player = itemReader.read(); required.
-
- Fixed Length file formats +
+ Fixed Length File Formats So far only delimited files have been discussed in much detail, however, they represent only half of the file reading picture. Many @@ -930,7 +930,7 @@ UK21341EAH4521535.11customer5
- Multiple record types within a single file + Multiple Record Types within a Single File All of the file reading examples up to this point have all made a key assumption for simplicity's sake: all of the records in a file @@ -1003,12 +1003,12 @@ LINEB;2134776319DEF422.99M005LI It is also common for a flat file to contain records that each span multiple lines. To handle this situation, a more complex strategy - is required. A demonstration of this common patter can be found in + is required. A demonstration of this common pattern can be found in .
-
- Exception Handling in flat files +
+ Exception Handling in Flat Files There are many scenarios when tokenizing a line may cause exceptions to be thrown. Many flat files are imperfect and contain @@ -1027,7 +1027,7 @@ LINEB;2134776319DEF422.99M005LI and indicates a more specific error encountered while tokenizing. -
+
IncorrectTokenCountException Both DelimitedLineTokenizer and @@ -1056,7 +1056,7 @@ catch(IncorrectTokenCountException e){ thrown.
-
+
IncorrectLineLengthException Files formatted in a fixed length format have additional @@ -1105,7 +1105,7 @@ assertEquals("", tokens.readString(1));
-
+
FlatFileItemWriter Writing out to flat files has the same problems and issues that @@ -1113,7 +1113,7 @@ assertEquals("", tokens.readString(1)); in either delimited or fixed length formats in a transactional manner. -
+
LineAggregator Just as the LineTokenizer interface is @@ -1138,7 +1138,7 @@ assertEquals("", tokens.readString(1)); item and returns a String. -
+
PassThroughLineAggregator The most basic implementation of the LineAggregator interface @@ -1160,7 +1160,7 @@ assertEquals("", tokens.readString(1));
-
+
Simplified File Writing Example Now that the LineAggregator interface and @@ -1191,17 +1191,15 @@ assertEquals("", tokens.readString(1)); A simple configuration 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" /> + <bean id="itemWriter" class="org.spr...FlatFileItemWriter"> + <property name="resource" value="file:target/test-outputs/output.txt" /> <property name="lineAggregator"> <bean class="org.spr...PassThroughLineAggregator"/> </property> </bean>
-
+
FieldExtractor The above example may be useful for the most basic uses of a @@ -1259,23 +1257,22 @@ assertEquals("", tokens.readString(1)); object, which can then be written out with a delimiter between the elements, or as part of a field-width line. -
+
PassThroughFieldExtractor - There are many cases where an array or something that can be - converted to an array, such as a Collection, - needs to be written out. For example, a List - could be passed through, in which case it only needs to be converted - to an Object array to be written out. For - this type of scenario the - PassThroughFieldExtractor can be used. It - should be noted, that if the object passed in is not an array, and - not a Collection, then an - Object array containing solely the item will - be returned. + There are many cases where a collection, such as an array, + Collection, or + FieldSet, needs to be written out. + "Extracting" an array from a one of these collection types is very + straightforward: simply convert the collection to an array. + Therefore, the PassThroughFieldExtractor + should be used in this scenario. It should be noted, that if the + object passed in is not a type of collection, then the + PassThroughFieldExtractor will return an + array containing solely the item to be extracted.
-
+
BeanWrapperFieldExtractor As with the BeanWrapperFieldSetMapper @@ -1311,7 +1308,7 @@ assertEquals(born, values[2]);
-
+
Delimited File Writing Example The most basic flat file format is one in which all fields are @@ -1354,7 +1351,7 @@ assertEquals(born, values[2]); then written out with commas between each field.
-
+
Fixed Width File Writing Example Delimited is not the only type of flat file format. Many prefer @@ -1392,8 +1389,8 @@ assertEquals(born, values[2]); url="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html">Formatter.
-
- Handling file creation +
+ Handling File Creation FlatFileItemReader has a very simple relationship with file resources. When the reader is initialized, it @@ -1416,7 +1413,7 @@ assertEquals(born, values[2]);
-
+
XML Item Readers and Writers Spring Batch provides transactional infrastructure for both reading @@ -1482,7 +1479,7 @@ assertEquals(born, values[2]); Now with an introduction to OXM and how one can use XML fragments to represent records, let's take a closer look at readers and writers. -
+
StaxEventItemReader The StaxEventItemReader configuration @@ -1618,7 +1615,7 @@ while (hasNext) { }
-
+
StaxEventItemWriter Output works symmetrically to input. The @@ -1686,7 +1683,7 @@ staxItemWriter.write(trade);
-
+
Multi-File Input It is a common requirement to process multiple files within a single @@ -1716,7 +1713,7 @@ staxItemWriter.write(trade); individual directories until completed successfully.
-
+
Database Like most enterprise application styles, a database is the central @@ -1734,7 +1731,7 @@ staxItemWriter.write(trade); provides two types of solutions for this problem: Cursor and Paging database ItemReaders. -
+
Cursor Based ItemReaders Using a database cursor is generally the default approach of most @@ -1782,7 +1779,7 @@ staxItemWriter.write(trade); collected (assuming no instance variables are maintaining references to them). -
+
JdbcCursorItemReader JdbcCursorItemReader is the Jdbc @@ -1879,7 +1876,7 @@ itemReader.close(executionContext); </property> </bean> -
+
Additional Properties Because there are so many varying options for opening a cursor @@ -1990,7 +1987,7 @@ itemReader.close(executionContext);
-
+
HibernateCursorItemReader Just as normal Spring users make important decisions about @@ -2050,7 +2047,7 @@ itemReader.close(executionContext);
-
+
Paging ItemReaders An alternative to using a database cursor is executing multiple @@ -2059,7 +2056,7 @@ itemReader.close(executionContext); specify the starting row number and the number of rows that we want returned for the page. -
+
JdbcPagingItemReader One implementation of a paging ItemReader @@ -2122,7 +2119,7 @@ itemReader.close(executionContext); 1.
-
+
JpaPagingItemReader Another implementation of a paging @@ -2159,7 +2156,7 @@ itemReader.close(executionContext); entities read from the database for each query execution.
-
+
IbatisPagingItemReader If you use IBATIS for your data access then you can use the @@ -2207,7 +2204,7 @@ itemReader.close(executionContext);
-
+
Database ItemWriters While both Flat Files and XML have specific ItemWriters, there is @@ -2279,7 +2276,7 @@ itemReader.close(executionContext);
-
+
Reusing Existing Services Batch systems are often used in conjunction with other application @@ -2325,7 +2322,7 @@ itemReader.close(executionContext);
-
+
Validating Input During the course of this chapter, multiple approaches to parsing @@ -2391,7 +2388,7 @@ itemReader.close(executionContext);
- Preventing state persistence + Preventing State Persistence By default, all of the ItemReader and ItemWriter implementations store their current @@ -2432,7 +2429,7 @@ itemReader.close(executionContext); executions in which it participates.
-
+
Creating Custom ItemReaders and ItemWriters @@ -2447,7 +2444,7 @@ itemReader.close(executionContext); implement ItemStream, in order to illustrate how to make a reader or writer restartable. -
+
Custom ItemReader Example For the purpose of this example, a simple @@ -2490,9 +2487,9 @@ assertEquals("2", itemReader.read()); assertEquals("3", itemReader.read()); assertNull(itemReader.read()); -
+
Making the <classname>ItemReader</classname> - restartable + Restartable The final challenge now is to make the ItemReader restartable. Currently, if the power @@ -2591,7 +2588,7 @@ assertEquals("2", itemReader.read());
-
+
Custom ItemWriter Example Implementing a Custom ItemWriter is similar @@ -2615,9 +2612,9 @@ assertEquals("2", itemReader.read()); } } -
+
Making the <classname>ItemWriter</classname> - restartable + Restartable To make the ItemWriter restartable we would follow the same process as for the ItemReader, adding and diff --git a/src/site/docbook/reference/repeat.xml b/src/site/docbook/reference/repeat.xml index e9cc6cd34..0027e27a3 100644 --- a/src/site/docbook/reference/repeat.xml +++ b/src/site/docbook/reference/repeat.xml @@ -1,14 +1,14 @@ - + Repeat -
+
RepeatTemplate Batch processing is about repetitive actions - either as a simple - optimisation, or as part of a job. To strategize and generalize the + optimization, or as part of a job. To strategize and generalize the repetition as well as to provide what amounts to an iterator framework, Spring Batch has the RepeatOperations interface. The RepeatOperations interface looks like @@ -64,7 +64,7 @@ template.iterate(new RepeatCallback() { completion decision is delegated to an external policy as in the case above. -
+
RepeatContext The method parameter for the RepeatCallback @@ -78,11 +78,11 @@ template.iterate(new RepeatCallback() { if there is a nested iteration in progress. The parent context is occasionally useful for storing data that need to be shared between calls to iterate. This is the case for instance - if you want to count the number of occurrences of an even in the + if you want to count the number of occurrences of an event in the iteration and remember it across subsequent calls.
-
+
RepeatStatus RepeatStatus is an enumeration used by @@ -90,7 +90,7 @@ template.iterate(new RepeatCallback() { possible RepeatStatus values: - ExitStatus properties + ExitStatus Properties @@ -124,13 +124,13 @@ template.iterate(new RepeatCallback() { -
+
Completion Policies Inside a RepeatTemplate the termination of the loop in the iterate method is determined by a CompletionPolicy which is also a factory for the - ReapeatContext. The + RepeatContext. The RepeatTemplate has the responsibility to use the current policy to create a RepeatContext and pass that in to the RepeatCallback at every stage in the @@ -153,7 +153,7 @@ template.iterate(new RepeatCallback() { would require a custom policy.
-
+
Exception Handling If there is an exception thrown inside a @@ -186,7 +186,7 @@ template.iterate(new RepeatCallback() { iteration (e.g. a set of chunks inside a step).
-
+
Listeners Often it is useful to be able to receive additional callbacks for @@ -210,8 +210,7 @@ template.iterate(new RepeatCallback() { void onError(RepeatContext context, Throwable e); void close(RepeatContext context); -} -The open and +}The open and close callbacks come before and after the entire iteration. before, after and onError apply to the individual @@ -224,7 +223,7 @@ template.iterate(new RepeatCallback() { close are called in reverse order.
-
+
Parallel Processing Implementations of RepeatOperations are not @@ -234,12 +233,12 @@ template.iterate(new RepeatCallback() { TaskExecutorRepeatTemplate, which uses the Spring TaskExecutor strategy to run the RepeatCallback. The default is to use a - SynchronousTaskExecutor, which has the effect of executing the whole - iteration in the same thread (the same as a normal + SynchronousTaskExecutor, which has the effect of + executing the whole iteration in the same thread (the same as a normal RepeatTemplate).
-
+
Declarative Iteration Sometimes there is some business processing that you know you want @@ -266,9 +265,7 @@ template.iterate(new RepeatCallback() { advice-ref="retryAdvice" order="-1"/> </aop:config> -<bean id="retryAdvice" - class="org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor"/> - +<bean id="retryAdvice" class="org.spr...RepeatOperationsInterceptor"/> The example above uses a default RepeatTemplate inside the interceptor. To change diff --git a/src/site/docbook/reference/retry.xml b/src/site/docbook/reference/retry.xml index 2fe8c0f68..555c91ced 100644 --- a/src/site/docbook/reference/retry.xml +++ b/src/site/docbook/reference/retry.xml @@ -4,7 +4,7 @@ Retry -
+
RetryTemplate To make processing more robust and less prone to failure, sometimes @@ -68,7 +68,7 @@ Foo result = template.execute(new RetryCallback<Foo>() { to the user. If that call fails then it is retried until a timeout is reached. -
+
RetryContext The method parameter for the RetryCallback @@ -82,7 +82,7 @@ Foo result = template.execute(new RetryCallback<Foo>() { between calls to execute.
-
+
RecoveryCallback When a retry is exhausted the @@ -104,7 +104,7 @@ Foo result = template.execute(new RetryCallback<Foo>() { alternate processing through the recovery callback.
-
+
Stateless Retry In the simplest case, a retry is just a while loop: the @@ -120,7 +120,7 @@ Foo result = template.execute(new RetryCallback<Foo>() { when it failed.
-
+
Stateful Retry Where the failure has caused a transactional resource to become @@ -183,7 +183,7 @@ Foo result = template.execute(new RetryCallback<Foo>() {
-
+
Retry Policies Inside a RetryTemplate the decision to retry @@ -245,7 +245,7 @@ template.execute(new RetryCallback<Foo>() { There is also a more flexible implementation called ExceptionClassifierRetryPolicy, which allows the user to configure different retry behavior for an arbitrary set of - excecption types though the ExceptionClassifier + exception types though the ExceptionClassifier abstraction. The policy works by calling on the classifier to convert an exception into a delegate RetryPolicy, so for example, one exception type can be retried more times before failure than @@ -257,7 +257,7 @@ template.execute(new RetryCallback<Foo>() { retryable.
-
+
Backoff Policies When retrying after a transient failure it often helps to wait a bit @@ -283,7 +283,7 @@ template.execute(new RetryCallback<Foo>() { ExponentialBackoffPolicy.
-
+
Listeners Often it is useful to be able to receive additional callbacks for @@ -304,8 +304,7 @@ template.execute(new RetryCallback<Foo>() { void onError(RetryContext context, RetryCallback<T> callback, Throwable e); void close(RetryContext context, RetryCallback<T> callback, Throwable e); -} -The open and +}The open and close callbacks come before and after the entire retry in the simplest case and onError applies to the individual RetryCallback calls. The @@ -319,7 +318,7 @@ template.execute(new RetryCallback<Foo>() { close will be called in reverse order.
-
+
Declarative Retry Sometimes there is some business processing that you know you want @@ -344,8 +343,7 @@ template.execute(new RetryCallback<Foo>() { </aop:config> <bean id="retryAdvice" - class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/> - + class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/> The example above uses a default RetryTemplate inside the interceptor. To change the diff --git a/src/site/docbook/reference/scalability.xml b/src/site/docbook/reference/scalability.xml index 4425d5e11..d27c0e505 100644 --- a/src/site/docbook/reference/scalability.xml +++ b/src/site/docbook/reference/scalability.xml @@ -39,7 +39,7 @@ Next we review the single-process options first, and then the multi-process options. -
+
Multi-threaded Step The simplest way to start parallel processing is to add a @@ -47,11 +47,11 @@ attribute of the tasklet: <step id="loading"> - <tasklet reader="stagingReader" - processor="stagingProcessor" - writer="tradeWriter" - commit-interval="1" - task-executor="taskExecutor"/> + <tasklet reader="stagingReader" + processor="stagingProcessor" + writer="tradeWriter" + commit-interval="1" + task-executor="taskExecutor"/> </step> In this example the taskExecutor is a reference to another bean @@ -78,7 +78,7 @@ track of items that have been processed in a database input table.
-
+
Parallel Steps As long as the application logic that needs to be parallelized can @@ -89,19 +89,19 @@ step3, you could configure a flow like this: <job id="job1"> - <split id="split1" task-executor="taskExecutor" next="step4"> - <flow> - <step id="step1" parent="s1" next="step2"/> - <step id="step2" parent="s2"/> - </flow> - <flow> - <step id="step3" parent="s3"/> - </flow> - </split> - <step id="step4" parent="s4"/> + <split id="split1" task-executor="taskExecutor" next="step4"> + <flow> + <step id="step1" parent="s1" next="step2"/> + <step id="step2" parent="s2"/> + </flow> + <flow> + <step id="step3" parent="s3"/> + </flow> + </split> + <step id="step4" parent="s4"/> </job> -<beans:bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/> +<beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/> The configurable "task-executor" attribute is used to specify which TaskExecutor implementation should be used to execute the individual @@ -217,7 +217,7 @@ org.springframework.batch.core.partition package). -
+
PartitionHandler The PartitionHandler is the component that @@ -247,10 +247,10 @@ TaskExecutorPartitionHandler, and it can be configured like this: - <bean class="org.sfw..TaskExecutorPartitionHandler"> - <property name="taskExecutor" ref="taskExecutor"/> - <property name="step" ref="step1" /> - <property name="gridSize" value="10" /> + <bean class="org.spr...TaskExecutorPartitionHandler"> + <property name="taskExecutor" ref="taskExecutor"/> + <property name="step" ref="step1" /> + <property name="gridSize" value="10" /> </bean> The gridSize determines the number of separate @@ -264,7 +264,7 @@ replicating filesystems into content management systems.
-
+
StepExecutionSplitter The StepExecutionSplitter is responsible @@ -274,9 +274,9 @@ principal method for this in the interface is public interface StepExecutionSplitter { - ... - Set<StepExecution> split(StepExecution stepExecution, int gridSize) - throws JobExecutionException; + ... + Set<StepExecution> split(StepExecution stepExecution, int gridSize) + throws JobExecutionException; } So an execution instance for the Master step is passed in, along @@ -295,7 +295,7 @@ restarts). It has a single method: public interface Partitioner { - Map<String, ExecutionContext> partition(int gridSize); + Map<String, ExecutionContext> partition(int gridSize); } The return value from this method associates a unique name for @@ -322,7 +322,7 @@ this convention.
-
+
Binding Input Data to Steps It is very efficient for the steps that are executed by the @@ -374,10 +374,10 @@ Then the file name can be bound to a step using late binding to the execution context: - <bean id="itemReader" scope="step" - class="org.sfw...MultiResourceItemReader"> + <bean id="itemReader" scope="step" + class="org.spr...MultiResourceItemReader"> <property name="resource" value="#{stepExecutionContext[fileName]}/*"/> - </bean> +</bean>
diff --git a/src/site/docbook/reference/schema-appendix.xml b/src/site/docbook/reference/schema-appendix.xml index a3db5da9f..1309941e9 100644 --- a/src/site/docbook/reference/schema-appendix.xml +++ b/src/site/docbook/reference/schema-appendix.xml @@ -1,10 +1,10 @@ - + Meta-Data Schema -
+
Overview The Spring Batch Meta-Data tables very closely match the Domain @@ -37,7 +37,7 @@ -
+
Example DDL Scripts The root of the Spring Batch Core JAR file contains example @@ -49,7 +49,7 @@ short name of the target database platform.
-
+
Version Many of the database tables discussed in this appendix contain a @@ -64,7 +64,7 @@ different machines, they are all using the same database tables.
-
+
Identity BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, and BATCH_STEP_EXECUTION @@ -83,7 +83,7 @@ CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ; CREATE SEQUENCE BATCH_JOB_SEQ; Many database vendors don't support sequences. In these cases, - work arounds are used, such as the following for mySQL: + work-arounds are used, such as the following for mySQL: CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=MYISAM; INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0); @@ -99,7 +99,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
BATCH_JOB_INSTANCE The BATCH_JOB_INSTANCE table holds all information relevant to a @@ -145,7 +145,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
BATCH_JOB_PARAMS The BATCH_JOB_PARAMS table holds all information relevant to the @@ -211,7 +211,7 @@ INSERT INTO BATCH_JOB_SEQ values(0); generated key, without causing any issues to the framework itself.
-
+
BATCH_JOB_EXECUTION The BATCH_JOB_EXECUTION table holds all information relevant to the @@ -298,7 +298,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
BATCH_STEP_EXECUTION The BATCH_STEP_EXECUTION table holds all information relevant to the @@ -437,7 +437,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
BATCH_JOB_EXECUTION_CONTEXT The BATCH_JOB_EXECUTION_CONTEXT table holds all information relevant @@ -478,7 +478,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
BATCH_STEP_EXECUTION_CONTEXT The BATCH_STEP_EXECUTION_CONTEXT table holds all information @@ -519,7 +519,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
Archiving Because there are entries in multiple tables every time a batch job @@ -551,7 +551,7 @@ INSERT INTO BATCH_JOB_SEQ values(0);
-
+
Recommendations for Indexing Meta Data Tables Spring Batch provides DDL samples for the meta-data tables in the @@ -565,8 +565,8 @@ INSERT INTO BATCH_JOB_SEQ values(0); projects can make up their own minds about indexing.
- Where clauses in SQL statements (exluding primary keys) and their - approximate frequency of use. + Where clauses in SQL statements (excluding primary keys) and + their approximate frequency of use. diff --git a/src/site/docbook/reference/step.xml b/src/site/docbook/reference/step.xml index b6d04d320..c659e6d95 100644 --- a/src/site/docbook/reference/step.xml +++ b/src/site/docbook/reference/step.xml @@ -339,7 +339,7 @@ itemWriter.write(items);
- Step restart configuration example + Step Restart Configuration Example <job id="footballJob" restartable="true"> <step id="playerload" next="gameLoad"> diff --git a/src/site/docbook/reference/testing.xml b/src/site/docbook/reference/testing.xml index 43e1a8d76..56a6257af 100644 --- a/src/site/docbook/reference/testing.xml +++ b/src/site/docbook/reference/testing.xml @@ -12,7 +12,7 @@ focus on. The spring-batch-test project includes classes that will help facilitate this end-to-end test approach. -
+
Creating a Unit Test Class In order for the unit test to run a batch job, the framework must @@ -31,16 +31,14 @@ - - @RunWith(SpringJUnit4ClassRunner.class) - @ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/skipSampleJob.xml" }) - public class SkipSampleFunctionalTests extends AbstractJobTests { ... } - - + @RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", + "/jobs/skipSampleJob.xml" }) +public class SkipSampleFunctionalTests extends AbstractJobTests { ... }
-
- End To End Testing Batch Jobs +
+ End-To-End Testing of Batch Jobs 'End To End' testing can be defined as testing the complete run of a batch job from beginning to end. This allows for a test that sets up a @@ -61,36 +59,35 @@ case below, the test verifies that the Job ended with status "COMPLETED". - - @RunWith(SpringJUnit4ClassRunner.class) - @ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/skipSampleJob.xml" }) - public class SkipSampleFunctionalTests extends AbstractJobTests { + @RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", + "/jobs/skipSampleJob.xml" }) +public class SkipSampleFunctionalTests extends AbstractJobTests { - private SimpleJdbcTemplate simpleJdbcTemplate; + private SimpleJdbcTemplate simpleJdbcTemplate; - @Autowired - public void setDataSource(DataSource dataSource) { - this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); - } + @Autowired + public void setDataSource(DataSource dataSource) { + this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); + } - @Transactional - @Test - public void testJob() throws Exception { - simpleJdbcTemplate.update("delete from CUSTOMER"); - for (int i = 1; i <= 10; i++) { - simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)", i, "customer" + i); - } + @Transactional + @Test + public void testJob() throws Exception { + simpleJdbcTemplate.update("delete from CUSTOMER"); + for (int i = 1; i <= 10; i++) { + simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)", + i, "customer" + i); + } - JobExecution jobExecution = this.launchJob(); + JobExecution jobExecution = this.launchJob(); - Assert.assertEquals("COMPLETED", jobExecution.getExitStatus()); - } - } - - + Assert.assertEquals("COMPLETED", jobExecution.getExitStatus()); + } +}
-
+
Testing Individual Steps For complex batch jobs, test cases in the end-to-end testing @@ -102,13 +99,10 @@ targeted tests by allowing the test to set up data for just that step and to validate its results directly. - - JobExecution jobExecution = this.launchStep("loadFileStep"); - - + JobExecution jobExecution = this.launchStep("loadFileStep");
-
+
Validating Output Files When a batch job writes to the database, it is easy to query the @@ -123,35 +117,30 @@ file with the expected output and to compare it to the actual result: - - private static final String EXPECTED_FILE = "src/main/resources/data/iosample/input/multiLine.txt"; - private static final String OUTPUT_FILE = "target/test-outputs/multiLineOutput.txt"; + private static final String EXPECTED_FILE = "src/main/resources/data/input.txt"; +private static final String OUTPUT_FILE = "target/test-outputs/output.txt"; - AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE), new FileSystemResource(OUTPUT_FILE)); - - +AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE), + new FileSystemResource(OUTPUT_FILE));
-
+
Mocking Domain Objects Another common issue encountered while writing unit and integration tests for Spring Batch components is how to mock domain objects. A good example is a StepExecutionListener, as illustrated - below: + below: - - public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport { + public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport { public ExitStatus afterStep(StepExecution stepExecution) { - if (stepExecution.getReadCount() == 0) { - throw new NoWorkFoundException("Step has not processed any items"); - } - return stepExecution.getExitStatus(); + if (stepExecution.getReadCount() == 0) { + throw new NoWorkFoundException("Step has not processed any items"); + } + return stepExecution.getExitStatus(); } - } - - +} The above listener is provided by the framework and checks a StepExecution for an empty read count, thus @@ -160,26 +149,22 @@ attempting to unit test classes that implement interfaces requiring Spring Batch domain objects. Consider the above listener's unit test: - - private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener(); + private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener(); - @Test - public void testAfterStep() { - StepExecution stepExecution = new StepExecution("NoProcessingStep", - new JobExecution( - new JobInstance(1L, new JobParameters(), "NoProcessingJob"))); +@Test +public void testAfterStep() { + StepExecution stepExecution = new StepExecution("NoProcessingStep", + new JobExecution(new JobInstance(1L, new JobParameters(), "NoProcessingJob"))); - stepExecution.setReadCount(0); + stepExecution.setReadCount(0); - try { - tested.afterStep(stepExecution); - fail(); - } catch (NoWorkFoundException e) { - assertEquals("Step has not processed any items", e.getMessage()); - } + try { + tested.afterStep(stepExecution); + fail(); + } catch (NoWorkFoundException e) { + assertEquals("Step has not processed any items", e.getMessage()); } - - +} Because the Spring Batch domain model follows good object orientated principles, the StepExecution requires a @@ -190,27 +175,24 @@ model, it does make creating stub objects for unit testing verbose. To address this issue, the Spring Batch test module includes a factory for creating domain objects: MetaDataInstanceFactory. - Given this factory, the unit test can be updated to be more concise: - + Given this factory, the unit test can be updated to be more + concise: - - private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener(); + private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener(); - @Test - public void testAfterStep() { - StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(); +@Test +public void testAfterStep() { + StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(); - stepExecution.setReadCount(0); + stepExecution.setReadCount(0); - try { - tested.afterStep(stepExecution); - fail(); - } catch (NoWorkFoundException e) { - assertEquals("Step has not processed any items", e.getMessage()); - } + try { + tested.afterStep(stepExecution); + fail(); + } catch (NoWorkFoundException e) { + assertEquals("Step has not processed any items", e.getMessage()); } - - +} The above method for creating a simple StepExecution is just one convenience method diff --git a/src/site/docbook/reference/whatsnew.xml b/src/site/docbook/reference/whatsnew.xml index c090cdd2f..18bcfafce 100644 --- a/src/site/docbook/reference/whatsnew.xml +++ b/src/site/docbook/reference/whatsnew.xml @@ -2,7 +2,7 @@ - What's new in Spring Batch 2.0 + What's New in Spring Batch 2.0 The Spring Batch 2.0 release has six major themes: