BATCH-220: Implement skip on ItemProcessor execution

This commit is contained in:
dsyer
2008-08-28 14:07:13 +00:00
parent 99af93c646
commit 375f9b7fa9
15 changed files with 381 additions and 59 deletions

View File

@@ -37,4 +37,13 @@ public class StepContributionTests extends TestCase {
assertEquals(1, contribution.getItemCount());
}
/**
* Test method for
* {@link org.springframework.batch.core.StepContribution#incrementItemCount()}.
*/
public void testIncrementFilterCount() {
assertEquals(0, contribution.getFilterCount());
contribution.incrementFilterCount(1);
assertEquals(1, contribution.getFilterCount());
}
}

View File

@@ -119,6 +119,11 @@ public class StepExecutionTests extends TestCase {
assertEquals(123, execution.getItemCount());
}
public void testGetFilterCount() {
execution.setFilterCount(123);
assertEquals(123, execution.getFilterCount());
}
public void testGetJobExecution() throws Exception {
assertNotNull(execution.getJobExecution());
}

View File

@@ -412,7 +412,7 @@ public class MulticasterBatchListenerTests {
*/
@Test
public void testOnSkipInRead() {
multicast.register(new SkipListenerSupport() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInRead(Throwable t) {
count++;
@@ -430,7 +430,7 @@ public class MulticasterBatchListenerTests {
*/
@Test
public void testOnSkipInReadFails() {
multicast.register(new SkipListenerSupport() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInRead(Throwable t) {
count++;
@@ -456,7 +456,7 @@ public class MulticasterBatchListenerTests {
*/
@Test
public void testOnSkipInWrite() {
multicast.register(new SkipListenerSupport() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInWrite(Object item, Throwable t) {
count++;
@@ -474,7 +474,7 @@ public class MulticasterBatchListenerTests {
*/
@Test
public void testOnSkipInWriteFails() {
multicast.register(new SkipListenerSupport() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInWrite(Object item, Throwable t) {
count++;
@@ -493,6 +493,50 @@ public class MulticasterBatchListenerTests {
assertEquals(1, count);
}
/**
* Test method for
* {@link org.springframework.batch.core.listener.MulticasterBatchListener#onSkipInWrite(java.lang.Object, java.lang.Throwable)}
* .
*/
@Test
public void testOnSkipInProcess() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInProcess(Object item, Throwable t) {
count++;
super.onSkipInWrite(item, t);
}
});
multicast.onSkipInProcess(null, new RuntimeException("foo"));
assertEquals(1, count);
}
/**
* Test method for
* {@link org.springframework.batch.core.listener.MulticasterBatchListener#onSkipInWrite(java.lang.Object, java.lang.Throwable)}
* .
*/
@Test
public void testOnSkipInProcessFails() {
multicast.register(new SkipListenerSupport<Object,Object>() {
@Override
public void onSkipInProcess(Object item, Throwable t) {
count++;
throw new RuntimeException("foo");
}
});
try {
multicast.onSkipInProcess(null, new RuntimeException("bar"));
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
String message = e.getMessage();
assertEquals("Wrong message: " + message, "foo", message);
}
assertEquals(1, count);
}
/**
* @author Dave Syer
*

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.core.step.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.List;
@@ -33,6 +34,7 @@ import org.springframework.batch.item.NoWorkFoundException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.support.PassthroughItemProcessor;
import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.repeat.context.RepeatContextSupport;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.batch.repeat.support.RepeatTemplate;
@@ -47,11 +49,11 @@ public class ChunkOrientedTaskletTests {
private StubItemReader itemReader = new StubItemReader();
private StubItemWriter itemWriter = new StubItemWriter();
private RepeatTemplate repeatTemplate = new RepeatTemplate();
private AttributeAccessor context = new RepeatContextSupport(null);
@Before
public void setUp() {
repeatTemplate.setCompletionPolicy(new SimpleCompletionPolicy(2));
@@ -68,6 +70,25 @@ public class ChunkOrientedTaskletTests {
assertEquals("12", itemWriter.values);
}
@Test
public void testHandleWithItemProcessorFailure() throws Exception {
ChunkOrientedTasklet<String, String> handler = new ChunkOrientedTasklet<String, String>(itemReader,
new StubItemProcessor(), itemWriter, repeatTemplate);
StepContribution contribution = new StepContribution(new StepExecution("foo", new JobExecution(new JobInstance(
123L, new JobParameters(), "job"))));
try {
handler.execute(contribution, context);
fail("Expected ValidationException");
}
catch (ValidationException e) {
// expected
}
assertEquals(2, itemReader.count);
assertEquals(2, contribution.getItemCount());
assertEquals(0, contribution.getFilterCount());
assertEquals("", itemWriter.values);
}
@Test
public void testHandleCompositeItem() throws Exception {
ChunkOrientedTasklet<String, String> handler = new ChunkOrientedTasklet<String, String>(itemReader,
@@ -76,6 +97,8 @@ public class ChunkOrientedTaskletTests {
123L, new JobParameters(), "job"))));
handler.execute(contribution, context);
assertEquals(2, itemReader.count);
assertEquals(2, contribution.getItemCount());
assertEquals(1, contribution.getFilterCount());
assertEquals("12", itemWriter.values);
}
@@ -100,6 +123,19 @@ public class ChunkOrientedTaskletTests {
}
}
/**
* @author Dave Syer
*
*/
private static class StubItemProcessor implements ItemProcessor<String, String> {
public String process(String item) throws Exception {
if ("2".equals(item)) {
throw new ValidationException("Planned failure");
}
return item;
}
}
/**
* @author Dave Syer
*
@@ -109,7 +145,7 @@ public class ChunkOrientedTaskletTests {
public void write(List<? extends String> items) throws Exception {
for (String item : items) {
values += item;
values += item;
}
}
}

View File

@@ -246,7 +246,7 @@ public class SkipLimitStepFactoryBeanTests {
factory.setSkipLimit(3);
factory.setItemReader(reader);
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
factory.setListeners(new StepListener[] { new SkipListenerSupport<String,String>() {
@Override
public void onSkipInRead(Throwable t) {
throw new RuntimeException("oops");
@@ -287,9 +287,9 @@ public class SkipLimitStepFactoryBeanTests {
factory.setSkipLimit(3);
factory.setItemReader(reader);
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
factory.setListeners(new StepListener[] { new SkipListenerSupport<String,String>() {
@Override
public void onSkipInWrite(Object item, Throwable t) {
public void onSkipInWrite(String item, Throwable t) {
throw new RuntimeException("oops");
}
} });

View File

@@ -211,8 +211,8 @@ public class StatefulRetryStepFactoryBeanTests {
add(RetryException.class);
}
});
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
public void onSkipInWrite(Object item, Throwable t) {
factory.setListeners(new StepListener[] { new SkipListenerSupport<String,String>() {
public void onSkipInWrite(String item, Throwable t) {
recovered.add(item);
assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
}
@@ -275,8 +275,8 @@ public class StatefulRetryStepFactoryBeanTests {
add(RetryException.class);
}
});
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
public void onSkipInWrite(Object item, Throwable t) {
factory.setListeners(new StepListener[] { new SkipListenerSupport<String,String>() {
public void onSkipInWrite(String item, Throwable t) {
recovered.add(item);
assertTrue(TransactionSynchronizationManager.isActualTransactionActive());
}

View File

@@ -55,10 +55,12 @@ public class StatefulRetryTaskletTests {
private int limit = 3;
protected int skipLimit = 2;
private int skipLimit = 2;
protected List<String> written = new ArrayList<String>();
private List<String> written = new ArrayList<String>();
private List<Integer> processed = new ArrayList<Integer>();
private StatefulRetryTasklet<Integer, String> handler;
private RepeatTemplate chunkOperations = new RepeatTemplate();
@@ -94,6 +96,7 @@ public class StatefulRetryTaskletTests {
private ItemSkipPolicy writeSkipPolicy = readSkipPolicy;
@Before
public void setUp() {
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
@@ -102,7 +105,7 @@ public class StatefulRetryTaskletTests {
@Test
public void testBasicHandle() throws Exception {
handler = new StatefulRetryTasklet<Integer, String>(itemReader, itemProcessor, itemWriter, chunkOperations,
retryTemplate, readSkipPolicy, writeSkipPolicy);
retryTemplate, readSkipPolicy, writeSkipPolicy, writeSkipPolicy);
StepContribution contribution = new StepExecution("foo", null).createStepContribution();
handler.execute(contribution, new BasicAttributeAccessor());
assertEquals(limit, contribution.getItemCount());
@@ -114,7 +117,7 @@ public class StatefulRetryTaskletTests {
public Integer read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException {
throw new RuntimeException("Barf!");
}
}, itemProcessor, itemWriter, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy);
}, itemProcessor, itemWriter, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy, writeSkipPolicy);
chunkOperations.setCompletionPolicy(new SimpleCompletionPolicy(1));
StepContribution contribution = new StepExecution("foo", null).createStepContribution();
BasicAttributeAccessor attributes = new BasicAttributeAccessor();
@@ -136,7 +139,7 @@ public class StatefulRetryTaskletTests {
written.addAll(items);
throw new RuntimeException("Barf!");
}
}, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy);
}, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy, writeSkipPolicy);
chunkOperations.setCompletionPolicy(new SimpleCompletionPolicy(1));
StepContribution contribution = new StepExecution("foo", null).createStepContribution();
BasicAttributeAccessor attributes = new BasicAttributeAccessor();
@@ -155,14 +158,14 @@ public class StatefulRetryTaskletTests {
}
@Test
public void testSkipMultipleItems() throws Exception {
public void testSkipMultipleItemsOnWrite() throws Exception {
handler = new StatefulRetryTasklet<Integer, String>(itemReader, itemProcessor, new ItemWriter<String>() {
public void write(List<? extends String> items) throws Exception {
logger.debug("Writing items: "+items);
written.addAll(items);
throw new RuntimeException("Barf!");
}
}, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy);
}, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy, writeSkipPolicy);
chunkOperations.setCompletionPolicy(new SimpleCompletionPolicy(2));
StepContribution contribution = new StepExecution("foo", null).createStepContribution();
BasicAttributeAccessor attributes = new BasicAttributeAccessor();
@@ -187,7 +190,7 @@ public class StatefulRetryTaskletTests {
attributes = new BasicAttributeAccessor();
try {
handler.execute(contribution, attributes);
fail("Expected RuntimeException on i=");
fail("Expected RuntimeException");
}
catch (Exception e) {
assertEquals("Barf!", e.getMessage());
@@ -205,4 +208,57 @@ public class StatefulRetryTaskletTests {
assertEquals(5, written.size());
}
@Test
public void testSkipMultipleItemsOnProcess() throws Exception {
handler = new StatefulRetryTasklet<Integer, String>(itemReader, new ItemProcessor<Integer, String>() {
public String process(Integer item) throws Exception {
logger.debug("Processing item: "+item);
processed.add(item);
throw new RuntimeException("Barf!");
}
}
, itemWriter, chunkOperations, retryTemplate, readSkipPolicy, writeSkipPolicy, writeSkipPolicy);
chunkOperations.setCompletionPolicy(new SimpleCompletionPolicy(2));
StepContribution contribution = new StepExecution("foo", null).createStepContribution();
BasicAttributeAccessor attributes = new BasicAttributeAccessor();
// Count to 3: (try + skip + try)
for (int i = 0; i < 3; i++) {
try {
handler.execute(contribution, attributes);
fail("Expected RuntimeException on i="+i);
}
catch (Exception e) {
assertEquals("Barf!", e.getMessage());
}
assertTrue(attributes.hasAttribute("INPUT_BUFFER_KEY"));
}
@SuppressWarnings("unchecked")
Chunk<Integer> chunk = (Chunk<Integer>) attributes.getAttribute("INPUT_BUFFER_KEY");
assertEquals(1, chunk.getSkips().size());
// The last recovery for this chunk...
handler.execute(contribution, attributes);
assertEquals(2, chunk.getSkips().size());
attributes = new BasicAttributeAccessor();
try {
handler.execute(contribution, attributes);
fail("Expected RuntimeException");
}
catch (Exception e) {
assertEquals("Barf!", e.getMessage());
}
try {
handler.execute(contribution, attributes);
fail("Expected SkipLimitExceededException");
}
catch (SkipLimitExceededException e) {
// expected
}
assertTrue(attributes.hasAttribute("INPUT_BUFFER_KEY"));
assertEquals(3, contribution.getItemCount());
assertEquals(2, contribution.getProcessSkipCount());
assertEquals(3, processed.size());
}
}