OPEN - issue BATCH-789: Remove mark/reset from ItemReader

Still some open issues with retry and skip, but nearly there...
This commit is contained in:
dsyer
2008-08-21 09:58:04 +00:00
parent 24168016af
commit 04db13bbc4
9 changed files with 323 additions and 219 deletions

View File

@@ -71,8 +71,8 @@ public class ItemOrientedStepHandlerTests {
StepContribution contribution = new StepContribution(new StepExecution("foo", new JobExecution(new JobInstance(
123L, new JobParameters(), "job"))));
handler.handle(contribution);
assertEquals(4, itemReader.count);
assertEquals("1234", itemWriter.values);
assertEquals(2, itemReader.count);
assertEquals("12", itemWriter.values);
}
/**

View File

@@ -46,13 +46,9 @@ import org.springframework.batch.core.step.AbstractStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.exception.ExceptionHandler;
import org.springframework.batch.repeat.exception.SimpleLimitExceptionHandler;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.batch.repeat.support.RepeatTemplate;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
/**
@@ -131,22 +127,6 @@ public class SimpleStepFactoryBeanTests {
@Test
public void testSimpleJobWithItemListeners() throws Exception {
final List<Throwable> throwables = new ArrayList<Throwable>();
RepeatTemplate chunkOperations = new RepeatTemplate();
// Always handle the exception to check it is the right one...
chunkOperations.setExceptionHandler(new ExceptionHandler() {
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
throwables.add(throwable);
assertEquals("Error!", throwable.getMessage());
}
});
/*
* Each message fails once and the chunk (size=1) "rolls back"; then it
* is recovered ("skipped") on the second attempt (see retry policy
* definition above)...
*/
SimpleStepFactoryBean<String,String> factory = getStepFactory(new String[] { "foo", "bar", "spam" });
factory.setItemWriter(new ItemWriter<String>() {
@@ -164,19 +144,24 @@ public class SimpleStepFactoryBeanTests {
}
} });
factory.setChunkOperations(chunkOperations);
StepHandlerStep step = (StepHandlerStep) factory.getObject();
Step step = (Step) factory.getObject();
job.setSteps(Collections.singletonList((Step) step));
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
job.execute(jobExecution);
try {
job.execute(jobExecution);
fail("Expected RuntimeException");
} catch (RuntimeException e) {
// expected
assertEquals("Error!", e.getMessage());
}
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
assertEquals(0, written.size());
// provider should be exhausted
assertEquals(null, reader.read());
assertEquals(3, recovered.size());
// provider should be at second item
assertEquals("bar", reader.read());
assertEquals(1, recovered.size());
}
@Test
@@ -313,7 +298,7 @@ public class SimpleStepFactoryBeanTests {
private SimpleStepFactoryBean<String,String> getStepFactory(String... args) throws Exception {
SimpleStepFactoryBean<String,String> factory = new SimpleStepFactoryBean<String,String>();
List<String> items = TransactionAwareProxyFactory.createTransactionalList();
List<String> items = new ArrayList<String>();
items.addAll(Arrays.asList(args));
reader = new ListItemReader<String>(items);

View File

@@ -1,27 +1,30 @@
package org.springframework.batch.core.step.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.listener.SkipListenerSupport;
import org.springframework.batch.core.step.AbstractStep;
import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
import org.springframework.batch.item.ClearFailedException;
import org.springframework.batch.item.FlushFailedException;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.MarkFailedException;
@@ -38,7 +41,7 @@ import org.springframework.util.StringUtils;
/**
* Tests for {@link SkipLimitStepFactoryBean}.
*/
public class SkipLimitStepFactoryBeanTests extends TestCase {
public class SkipLimitStepFactoryBeanTests {
protected final Log logger = LogFactory.getLog(getClass());
@@ -58,7 +61,8 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
protected int count;
protected void setUp() throws Exception {
@Before
public void setUp() throws Exception {
factory.setBeanName("stepName");
factory.setJobRepository(new JobRepositorySupport());
factory.setTransactionManager(new ResourcelessTransactionManager());
@@ -75,8 +79,10 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@Test
public void testSkip() throws Exception {
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
step.execute(stepExecution);
@@ -103,13 +109,14 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
* Check skippable write exception does not cause rollback when included on
* transaction attributes as "no rollback for".
*/
@Test
public void testSkipWithoutRethrow() throws Exception {
factory.setTransactionAttribute(new DefaultTransactionAttribute() {
public boolean rollbackOn(Throwable ex) {
return !(ex instanceof SkippableRuntimeException);
};
});
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
step.execute(stepExecution);
@@ -129,6 +136,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
* Fatal exception should cause immediate termination regardless of other
* skip settings (note the fatal exception is also classified as skippable).
*/
@Test
public void testFatalException() throws Exception {
factory.setFatalExceptionClasses(new Class[] { FatalRuntimeException.class });
factory.setItemWriter(new SkipWriterStub() {
@@ -137,7 +145,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
}
});
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
@@ -152,11 +160,12 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@Test
public void testSkipOverLimit() throws Exception {
factory.setSkipLimit(1);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -182,17 +191,17 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipOverLimitOnRead() throws Exception {
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), StringUtils
.commaDelimitedListToSet("2,3,5"));
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), Arrays
.asList(StringUtils.commaDelimitedListToStringArray("2,3,5")));
factory.setSkipLimit(3);
factory.setItemReader(reader);
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -224,11 +233,11 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipListenerFailsOnRead() throws Exception {
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), StringUtils
.commaDelimitedListToSet("2,3,5"));
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), Arrays
.asList(StringUtils.commaDelimitedListToStringArray("2,3,5")));
factory.setSkipLimit(3);
factory.setItemReader(reader);
@@ -240,7 +249,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
} });
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -261,11 +270,11 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipListenerFailsOnWrite() throws Exception {
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), StringUtils
.commaDelimitedListToSet("2,3,5"));
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), Arrays
.asList(StringUtils.commaDelimitedListToStringArray("2,3,5")));
factory.setSkipLimit(3);
factory.setItemReader(reader);
@@ -277,7 +286,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
} });
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -289,8 +298,8 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
assertEquals("oops", e.getCause().getMessage());
}
assertEquals(1, stepExecution.getSkipCount());
assertEquals(0, stepExecution.getReadSkipCount());
assertEquals(3, stepExecution.getSkipCount());
assertEquals(2, stepExecution.getReadSkipCount());
assertEquals(1, stepExecution.getWriteSkipCount());
}
@@ -298,16 +307,16 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipOnReadNotDoubleCounted() throws Exception {
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), StringUtils
.commaDelimitedListToSet("2,3,5"));
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6"), Arrays
.asList(StringUtils.commaDelimitedListToStringArray("2,3,5")));
factory.setSkipLimit(4);
factory.setItemReader(reader);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = jobExecution.createStepExecution(step);
@@ -325,19 +334,19 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
/**
* Check items causing errors are skipped as expected.
*/
@SuppressWarnings("unchecked")
@Test
public void testSkipOnWriteNotDoubleCounted() throws Exception {
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6,7"), StringUtils
.commaDelimitedListToSet("2,3"));
reader = new SkipReaderStub(StringUtils.commaDelimitedListToStringArray("1,2,3,4,5,6,7"), Arrays
.asList(StringUtils.commaDelimitedListToStringArray("2,3")));
writer = new SkipWriterStub(StringUtils.commaDelimitedListToSet("4,5"));
writer = new SkipWriterStub(Arrays.asList(StringUtils.commaDelimitedListToStringArray("4,5")));
factory.setSkipLimit(4);
factory.setItemReader(reader);
factory.setItemWriter(writer);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = jobExecution.createStepExecution(step);
@@ -354,15 +363,14 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
}
@SuppressWarnings("unchecked")
@Test
public void testDefaultSkipPolicy() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkipLimit(1);
List<String> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "a", "b", "c" }));
ItemReader provider = new ListItemReader(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "a", "b", "c" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
count++;
if ("b".equals(item)) {
throw new RuntimeException("Read error - planned failure.");
@@ -371,7 +379,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
}
};
factory.setItemReader(provider);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
step.execute(stepExecution);
@@ -381,6 +389,8 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
assertEquals(4, count);
}
// TODO: test with transactional reader (e.g. list with tx proxy)
/**
* Simple item reader that supports skip functionality.
*/
@@ -394,8 +404,6 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
private int counter = -1;
private int marked = 0;
private final Collection<String> failures;
public SkipReaderStub() {
@@ -424,13 +432,9 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
}
public void mark() throws MarkFailedException {
logger.debug("Marked at count=" + counter);
marked = counter;
}
public void reset() throws ResetFailedException {
counter = marked;
logger.debug("Reset at count=" + counter);
}
}
@@ -442,6 +446,7 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
protected final Log logger = LogFactory.getLog(getClass());
// simulate transactional output
private List<Object> written = TransactionAwareProxyFactory.createTransactionalList();
private final Collection<String> failures;
@@ -458,12 +463,6 @@ public class SkipLimitStepFactoryBeanTests extends TestCase {
this.failures = failures;
}
public void clear() throws ClearFailedException {
}
public void flush() throws FlushFailedException {
}
public void write(List<? extends String> items) throws Exception {
for (String item : items) {
if (failures.contains(item)) {

View File

@@ -15,15 +15,19 @@
*/
package org.springframework.batch.core.step.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
@@ -47,23 +51,24 @@ import org.springframework.batch.retry.RetryException;
import org.springframework.batch.retry.policy.RetryCacheCapacityExceededException;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* @author Dave Syer
*
*/
public class StatefulRetryStepFactoryBeanTests extends TestCase {
public class StatefulRetryStepFactoryBeanTests {
protected final Log logger = LogFactory.getLog(getClass());
private SkipLimitStepFactoryBean<Object,Object> factory = new SkipLimitStepFactoryBean<Object,Object>();
private SkipLimitStepFactoryBean<String, String> factory = new SkipLimitStepFactoryBean<String, String>();
private List<Object> recovered = new ArrayList<Object>();
private List<Object> processed = new ArrayList<Object>();
private List<Object> provided = new ArrayList<Object>();
int count = 0;
private SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(),
@@ -71,17 +76,19 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
JobExecution jobExecution;
private ItemWriter<Object> processor = new ItemWriter<Object>() {
public void write(List<? extends Object> data) throws Exception {
private ItemWriter<String> processor = new ItemWriter<String>() {
public void write(List<? extends String> data) throws Exception {
processed.addAll(data);
}
};
/*
* (non-Javadoc)
*
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
@Before
public void setUp() throws Exception {
MapJobInstanceDao.clear();
MapJobExecutionDao.clear();
@@ -89,7 +96,7 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
factory.setBeanName("step");
factory.setItemReader(new ListItemReader<Object>(new ArrayList<Object>()));
factory.setItemReader(new ListItemReader<String>(new ArrayList<String>()));
factory.setItemWriter(processor);
factory.setJobRepository(repository);
factory.setTransactionManager(new ResourcelessTransactionManager());
@@ -104,10 +111,12 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
}
@Test
public void testType() throws Exception {
assertEquals(Step.class, factory.getObjectType());
}
@Test
public void testDefaultValue() throws Exception {
assertTrue(factory.getObject() instanceof Step);
}
@@ -120,12 +129,13 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
*
* @throws Exception
*/
@Test
public void testSuccessfulRetryWithReadFailure() throws Exception {
List<Object> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "a", "b", "c" }));
ItemReader<Object> provider = new ListItemReader<Object>(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "a", "b", "c" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
provided.add(item);
count++;
if (count == 2) {
throw new RuntimeException("Temporary error - retry for success.");
@@ -143,19 +153,24 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
assertEquals(0, stepExecution.getSkipCount());
// b is processed twice, plus a, plus c, plus the null at end
assertEquals(5, count);
assertEquals(3, stepExecution.getItemCount());
// [a, b, c, null]
assertEquals(4, provided.size());
// [a, c]
assertEquals(2, processed.size());
// []
assertEquals(0, recovered.size());
assertEquals(2, stepExecution.getItemCount());
assertEquals(0, stepExecution.getReadSkipCount());
}
@Test
public void testSkipAndRetry() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkipLimit(2);
List<Object> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "a", "b", "c", "d", "e", "f" }));
ItemReader<Object> provider = new ListItemReader<Object>(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "a", "b", "c", "d", "e", "f" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
count++;
if ("b".equals(item) || "d".equals(item)) {
throw new RuntimeException("Read error - planned but skippable.");
@@ -165,7 +180,7 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
};
factory.setItemReader(provider);
factory.setRetryLimit(10);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
step.execute(stepExecution);
@@ -176,6 +191,7 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
assertEquals(4, stepExecution.getItemCount());
}
@Test
public void testSkipAndRetryWithWriteFailure() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { RetryException.class });
@@ -186,22 +202,23 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
}
} });
factory.setSkipLimit(2);
List<Object> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "a", "b", "c", "d", "e", "f" }));
ItemReader<Object> provider = new ListItemReader<Object>(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "a", "b", "c", "d", "e", "f" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
logger.debug("Read Called! Item: [" + item + "]");
provided.add(item);
count++;
return item;
}
};
ItemWriter<Object> itemWriter = new ItemWriter<Object>() {
public void write(List<? extends Object> item) throws Exception {
ItemWriter<String> itemWriter = new ItemWriter<String>() {
public void write(List<? extends String> item) throws Exception {
logger.debug("Write Called! Item: [" + item + "]");
processed.addAll(item);
if (item.contains("b") || item.contains("d")) {
throw new RuntimeException("Read error - planned but skippable.");
throw new RuntimeException("Write error - planned but recoverable.");
}
}
};
@@ -217,33 +234,39 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
assertEquals(2, recovered.size());
assertEquals(2, stepExecution.getSkipCount());
assertEquals(2, stepExecution.getWriteSkipCount());
// each item once, plus 5 failed retries each for b and d, plus the null
// terminator
assertEquals(17, count);
// [a, b, c, d, e, f, null]
assertEquals(7, provided.size());
// [a, b, b, b, b, b, c, d, d, d, d, d, e, f]
assertEquals(14, processed.size());
// [b, d]
assertEquals(2, recovered.size());
}
@Test
public void testRetryWithNoSkip() throws Exception {
factory.setRetryableExceptionClasses(new Class[] { Exception.class });
factory.setRetryLimit(4);
factory.setSkipLimit(0);
List<Object> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "b" }));
ItemReader<Object> provider = new ListItemReader<Object>(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "b" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
provided.add(item);
count++;
return item;
}
};
ItemWriter<Object> itemWriter = new ItemWriter<Object>() {
public void write(List<? extends Object> item) throws Exception {
ItemWriter<String> itemWriter = new ItemWriter<String>() {
public void write(List<? extends String> item) throws Exception {
processed.addAll(item);
logger.debug("Write Called! Item: [" + item + "]");
throw new RuntimeException("Write error - planned but retryable.");
}
};
factory.setItemReader(provider);
factory.setItemWriter(itemWriter);
AbstractStep step = (AbstractStep) factory.getObject();
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
@@ -255,25 +278,31 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
}
assertEquals(0, stepExecution.getSkipCount());
// b is processed 4 times plus the null at end
assertEquals(5, count);
// [b]
assertEquals(1, provided.size());
// [b, b, b, b]
assertEquals(4, processed.size());
// []
assertEquals(0, recovered.size());
assertEquals(0, stepExecution.getItemCount());
}
@Test
public void testRetryPolicy() throws Exception {
factory.setRetryPolicy(new SimpleRetryPolicy(4));
factory.setSkipLimit(0);
List<Object> items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(new String[] { "b" }));
ItemReader<Object> provider = new ListItemReader<Object>(items) {
public Object read() {
Object item = super.read();
List<String> items = Arrays.asList(new String[] { "b" });
ItemReader<String> provider = new ListItemReader<String>(items) {
public String read() {
String item = super.read();
provided.add(item);
count++;
return item;
}
};
ItemWriter<Object> itemWriter = new ItemWriter<Object>() {
public void write(List<? extends Object> item) throws Exception {
ItemWriter<String> itemWriter = new ItemWriter<String>() {
public void write(List<? extends String> item) throws Exception {
processed.addAll(item);
logger.debug("Write Called! Item: [" + item + "]");
throw new RuntimeException("Write error - planned but retryable.");
}
@@ -292,26 +321,39 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
}
assertEquals(0, stepExecution.getSkipCount());
// b is processed 4 times plus the null at end
assertEquals(5, count);
// [b]
assertEquals(1, provided.size());
// [b, b, b, b]
assertEquals(4, processed.size());
// []
assertEquals(0, recovered.size());
assertEquals(0, stepExecution.getItemCount());
}
@Test
public void testCacheLimitWithRetry() throws Exception {
factory.setRetryableExceptionClasses(new Class[] { Exception.class });
factory.setRetryLimit(2);
factory.setCommitInterval(3);
// sufficiently high so we never hit it
factory.setSkipLimit(10);
// set the cache limit lower than the number of unique un-recovered
// errors expected
factory.setCacheCapacity(2);
ItemReader<Object> provider = new AbstractItemReader<Object>() {
public Object read() {
Object item = new Object();
ItemReader<String> provider = new AbstractItemReader<String>() {
public String read() {
String item = ""+count;
provided.add(item);
count++;
if (count >= 10) {
// prevent infinite loop in worst case scenario
return null;
}
return item;
}
};
ItemWriter<Object> itemWriter = new ItemWriter<Object>() {
public void write(List<? extends Object> item) throws Exception {
ItemWriter<String> itemWriter = new ItemWriter<String>() {
public void write(List<? extends String> item) throws Exception {
processed.addAll(item);
logger.debug("Write Called! Item: [" + item + "]");
throw new RuntimeException("Write error - planned but retryable.");
}
@@ -329,8 +371,14 @@ public class StatefulRetryStepFactoryBeanTests extends TestCase {
// expected
}
assertEquals(0, stepExecution.getSkipCount());
// 2 processed and cached, 3rd barfed because cache was full
assertEquals(3, count);
assertEquals(1, stepExecution.getSkipCount());
// only one item processed but three (the commit interval) were provided
// [0, 1, 2]
assertEquals(3, provided.size());
// TODO: this is a bug: 0 was skipped but it came back in the buffer for the second try
// [0, 0, 1, 0, 0]
assertEquals(5, processed.size());
// []
assertEquals(0, recovered.size());
}
}