From 5b056e812667a49e35f04775279f8e1f43f66f4e Mon Sep 17 00:00:00 2001 From: Michael Minella Date: Tue, 23 Sep 2014 14:12:48 -0500 Subject: [PATCH] Handle scenario where scanned item is filtered out BATCH-2302 documents a scenario where an item throws an exception in a fault tollerant step in the write, then in the process, throws an exception as well. This leads to an infinite loop. To address this, prior to attempting to write items during scanning we need to validate that the there are items to be written (we were not which was causing a NoSuchElementException when we did inputs.next(). This commit also removes an eronous System.out left in the CoreNamespaceUtils. --- .../configuration/xml/CoreNamespaceUtils.java | 1 - .../item/FaultTolerantChunkProcessor.java | 4 +- .../step/skip/ReprocessExceptionTests.java | 123 ++++++++++++++++++ ...syncChunkOrientedStepIntegrationTests.java | 2 - .../ChunkOrientedStepIntegrationTests.java | 2 - .../src/test/resources/data/person.csv | 5 + .../skip/ReprocessExceptionTests-context.xml | 47 +++++++ 7 files changed, 176 insertions(+), 8 deletions(-) create mode 100644 spring-batch-core/src/test/java/org/springframework/batch/core/step/skip/ReprocessExceptionTests.java create mode 100644 spring-batch-core/src/test/resources/data/person.csv create mode 100644 spring-batch-core/src/test/resources/org/springframework/batch/core/step/skip/ReprocessExceptionTests-context.xml diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java index 7492990e7..b740d6312 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/CoreNamespaceUtils.java @@ -58,7 +58,6 @@ public class CoreNamespaceUtils { private static final String CORE_NAMESPACE_POST_PROCESSOR_CLASS_NAME = "org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor"; public static void autoregisterBeansForNamespace(ParserContext parserContext, Object source) { - System.out.println("******** CoreNamespaceUtils is called"); checkForStepScope(parserContext, source); checkForJobScope(parserContext, source); addRangePropertyEditor(parserContext); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java index 2458f1a1f..4088277e7 100755 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessor.java @@ -320,7 +320,6 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor retryCallback = new RetryCallback() { @Override public Object doWithRetry(RetryContext context) throws Exception { - contextHolder.set(context); if (!data.scanning()) { @@ -395,7 +394,6 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor extends SimpleChunkProcessor { + + private String mostRecentFirstName; + + @Override + public Person process(final Person person) throws Exception { + if (person.getFirstName().equals(mostRecentFirstName)) { + throw new RuntimeException("throwing a exception during process after a rollback"); + } + mostRecentFirstName = person.getFirstName(); + + final String firstName = person.getFirstName().toUpperCase(); + final String lastName = person.getLastName().toUpperCase(); + + final Person transformedPerson = new Person(firstName, lastName); + + System.out.println("Converting (" + person + ") into (" + transformedPerson + ")"); + + return transformedPerson; + } + } + + public static class PersonItemWriter implements ItemWriter { + @Override + public void write(List persons) throws Exception { + for (Person person : persons) { + System.out.println(person.getFirstName() + " " + person.getLastName()); + if (person.getFirstName().equals("JANE")) { + throw new RuntimeException("jane doe write exception causing rollback"); + } + } + } + } + + public static class Person { + private String lastName; + private String firstName; + + public Person() { + + } + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @Override + public String toString() { + return "firstName: " + firstName + ", lastName: " + lastName; + } + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/AsyncChunkOrientedStepIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/AsyncChunkOrientedStepIntegrationTests.java index 47d20da2d..18212ff73 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/AsyncChunkOrientedStepIntegrationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/AsyncChunkOrientedStepIntegrationTests.java @@ -18,7 +18,6 @@ package org.springframework.batch.core.step.tasklet; import org.apache.commons.dbcp.BasicDataSource; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.BatchStatus; @@ -120,7 +119,6 @@ public class AsyncChunkOrientedStepIntegrationTests { } @Test - @Ignore //FIXME public void testStatus() throws Exception { step.setTasklet(new TestingChunkOrientedTasklet(getReader(new String[] { "a", "b", "c", "a", "b", "c", diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/ChunkOrientedStepIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/ChunkOrientedStepIntegrationTests.java index 260f02f70..680136d90 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/ChunkOrientedStepIntegrationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/tasklet/ChunkOrientedStepIntegrationTests.java @@ -16,7 +16,6 @@ package org.springframework.batch.core.step.tasklet; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.BatchStatus; @@ -90,7 +89,6 @@ public class ChunkOrientedStepIntegrationTests { @SuppressWarnings("serial") @Test - @Ignore //FIXME public void testStatusForCommitFailedException() throws Exception { step.setTasklet(new TestingChunkOrientedTasklet(getReader(new String[] { "a", "b", "c" }), diff --git a/spring-batch-core/src/test/resources/data/person.csv b/spring-batch-core/src/test/resources/data/person.csv new file mode 100644 index 000000000..cead90f5a --- /dev/null +++ b/spring-batch-core/src/test/resources/data/person.csv @@ -0,0 +1,5 @@ +Jill,Doe +Joe,Doe +Justin,Doe +Jane,Doe +John,Doe diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/step/skip/ReprocessExceptionTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/skip/ReprocessExceptionTests-context.xml new file mode 100644 index 000000000..0566b34b2 --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/skip/ReprocessExceptionTests-context.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file