RESOLVED - issue BATCH-268: retry configuration for ItemOrientedTasklet
http://jira.springframework.org/browse/BATCH-268 The retry only works if you have an ItemStream as an ItemReader (so that you get a proper rollback). Applied the patch and fixed it up to fit the new m5 way of looking at things.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package org.springframework.batch.sample.item.reader;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.springframework.batch.item.ExecutionAttributes;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.exception.MarkFailedException;
|
||||
import org.springframework.batch.item.exception.ResetFailedException;
|
||||
import org.springframework.batch.item.exception.StreamException;
|
||||
import org.springframework.batch.item.reader.AbstractItemReaderRecoverer;
|
||||
import org.springframework.batch.sample.domain.Trade;
|
||||
|
||||
/**
|
||||
* Generates configurable number of {@link Trade} items.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class GeneratingItemReader extends AbstractItemReaderRecoverer implements ItemStream {
|
||||
|
||||
private int limit = 1;
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
private int marked;
|
||||
|
||||
public Object read() throws Exception {
|
||||
if (counter < limit) {
|
||||
counter++;
|
||||
return new Trade(
|
||||
"isin" + counter,
|
||||
counter,
|
||||
new BigDecimal(counter),
|
||||
"customer" + counter);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param limit number of items that will be generated
|
||||
* (null returned on consecutive calls).
|
||||
*/
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemRecoverer#recover(java.lang.Object, java.lang.Throwable)
|
||||
*/
|
||||
public boolean recover(Object data, Throwable cause) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#close()
|
||||
*/
|
||||
public void close() throws StreamException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#isMarkSupported()
|
||||
*/
|
||||
public boolean isMarkSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#mark()
|
||||
*/
|
||||
public void mark() throws MarkFailedException {
|
||||
this.marked = this.counter;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#open()
|
||||
*/
|
||||
public void open() throws StreamException {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#reset()
|
||||
*/
|
||||
public void reset() throws ResetFailedException {
|
||||
this.counter = this.marked;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ItemStream#restoreFrom(org.springframework.batch.item.ExecutionAttributes)
|
||||
*/
|
||||
public void restoreFrom(ExecutionAttributes context) {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.batch.item.ExecutionAttributesProvider#getExecutionAttributes()
|
||||
*/
|
||||
public ExecutionAttributes getExecutionAttributes() {
|
||||
return new ExecutionAttributes();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.springframework.batch.sample.item.writer;
|
||||
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Simulates temporary output trouble - requires to
|
||||
* retry 3 times to pass successfully.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class RetrySampleItemWriter implements ItemWriter {
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
public void write(Object data) throws Exception {
|
||||
counter++;
|
||||
if (counter == 2 || counter == 3) {
|
||||
throw new RuntimeException("Temporary error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of times {@link #process(Object)} method was called.
|
||||
*/
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
}
|
||||
51
spring-batch-samples/src/main/resources/jobs/retrySample.xml
Normal file
51
spring-batch-samples/src/main/resources/jobs/retrySample.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
|
||||
|
||||
<bean parent="stepScope" />
|
||||
<bean parent="jobConfigurationRegistryBeanPostProcessor" />
|
||||
|
||||
<bean id="retrySample" parent="simpleJob">
|
||||
<property name="steps">
|
||||
<bean id="step1" parent="simpleStep">
|
||||
<property name="itemReader" ref="itemGenerator" />
|
||||
<property name="itemWriter" ref="itemWriter" />
|
||||
<property name="retryPolicy">
|
||||
<bean
|
||||
class="org.springframework.batch.retry.policy.SimpleRetryPolicy">
|
||||
<property name="maxAttempts" value="3" />
|
||||
<property name="retryableExceptionClasses"
|
||||
value="java.lang.Exception" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="exceptionHandler">
|
||||
<bean
|
||||
class="org.springframework.batch.repeat.exception.handler.SimpleLimitExceptionHandler">
|
||||
<property name="limit" value="5" />
|
||||
<property name="type"
|
||||
value="java.lang.Exception" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="testGenerator"
|
||||
class="org.springframework.batch.sample.item.reader.GeneratingItemReader">
|
||||
<property name="limit" value="10" />
|
||||
</bean>
|
||||
|
||||
<bean id="itemGenerator" parent="testGenerator" scope="step" autowire-candidate="false">
|
||||
<aop:scoped-proxy/>
|
||||
</bean>
|
||||
|
||||
<bean id="itemWriter"
|
||||
class="org.springframework.batch.sample.item.writer.RetrySampleItemWriter" />
|
||||
</beans>
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.springframework.batch.sample;
|
||||
|
||||
import org.springframework.batch.sample.item.reader.GeneratingItemReader;
|
||||
import org.springframework.batch.sample.item.writer.RetrySampleItemWriter;
|
||||
|
||||
/**
|
||||
* Checks that expected number of items have been processed.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class RetrySampleFunctionalTests extends AbstractValidatingBatchLauncherTests {
|
||||
|
||||
private GeneratingItemReader itemGenerator;
|
||||
|
||||
private RetrySampleItemWriter itemProcessor;
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] {"jobs/retrySample.xml"};
|
||||
}
|
||||
|
||||
protected void validatePostConditions() throws Exception {
|
||||
//items processed = items read + 2 exceptions
|
||||
assertEquals(itemGenerator.getLimit()+2, itemProcessor.getCounter());
|
||||
}
|
||||
|
||||
public void setItemGenerator(GeneratingItemReader itemGenerator) {
|
||||
this.itemGenerator = itemGenerator;
|
||||
}
|
||||
|
||||
public void setItemProcessor(RetrySampleItemWriter itemProcessor) {
|
||||
this.itemProcessor = itemProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.batch.sample.item.reader;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link GeneratingItemReader}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class GeneratingItemReaderTests extends TestCase {
|
||||
|
||||
private GeneratingItemReader reader = new GeneratingItemReader();
|
||||
|
||||
/**
|
||||
* Generates a given number of not-null records,
|
||||
* consecutive calls return null.
|
||||
*/
|
||||
public void testRead() throws Exception {
|
||||
int counter = 0;
|
||||
int limit = 10;
|
||||
reader.setLimit(limit);
|
||||
|
||||
while (reader.read() != null) {
|
||||
counter++;
|
||||
}
|
||||
|
||||
assertEquals(null, reader.read());
|
||||
assertEquals(limit, counter);
|
||||
assertEquals(counter, reader.getCounter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.springframework.batch.sample.item.writer;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link RetrySampleItemWriter}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class RetrySampleItemWriterTests extends TestCase {
|
||||
|
||||
private RetrySampleItemWriter processor = new RetrySampleItemWriter();
|
||||
|
||||
/**
|
||||
* Processing throws exception on 2nd and 3rd call.
|
||||
*/
|
||||
public void testProcess() throws Exception {
|
||||
Object item = null;
|
||||
processor.write(item);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
processor.write(item);
|
||||
fail();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
processor.write(item);
|
||||
|
||||
assertEquals(4, processor.getCounter());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user