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 4088277e7..30c9f7039 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 @@ -16,9 +16,18 @@ package org.springframework.batch.core.step.item; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.listener.StepListenerFailedException; import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy; import org.springframework.batch.core.step.skip.NonSkippableProcessException; import org.springframework.batch.core.step.skip.SkipLimitExceededException; @@ -35,13 +44,6 @@ import org.springframework.retry.RetryContext; import org.springframework.retry.RetryException; import org.springframework.retry.support.DefaultRetryState; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - /** * FaultTolerant implementation of the {@link ChunkProcessor} interface, that * allows for skipping or retry of items that cause exceptions during writing. @@ -584,16 +586,25 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor fakeItemReader, + ItemProcessor fakeProcessor, + ItemWriter fakeItemWriter, + ItemProcessListener itemProcessListener) { + + return stepBuilderFactory.get("testStep").chunk(10) + .reader(fakeItemReader) + .processor(fakeProcessor) + .writer(fakeItemWriter) + .listener(itemProcessListener) + .faultTolerant().skipLimit(50).skip(RuntimeException.class) + .build(); + } + + @Bean + public FailingListener itemListener() { + return new FailingListener(); + } + + @Bean + public FailingItemReader fakeReader() { + return new FailingItemReader(); + } + + @Bean + public FailingItemProcessor fakeProcessor() { + return new FailingItemProcessor(); + } + + @Bean + public FailingItemWriter fakeItemWriter() { + return new FailingItemWriter(); + } + } + + public static class FailingItemWriter implements ItemWriter { + + private boolean goingToFail = false; + + @Override + public void write(List items) throws Exception { + if(goingToFail) { + throw new RuntimeException("failure in the writer"); + } + else { + for (String item : items) { + System.out.println(item); + } + } + } + + public void setGoingToFail(boolean goingToFail) { + this.goingToFail = goingToFail; + } + } + + public static class FailingItemProcessor implements ItemProcessor { + + private boolean goingToFail = false; + + @Override + public String process(String item) throws Exception { + if(goingToFail) { + throw new RuntimeException("failure in the processor"); + } + else { + return item; + } + } + + public void setGoingToFail(boolean goingToFail) { + this.goingToFail = goingToFail; + } + } + + public static class FailingItemReader implements ItemReader { + + private boolean goingToFail = false; + + private ItemReader delegate = new ListItemReader(Collections.singletonList("1")); + + private int count = 0; + + @Override + public String read() throws Exception { + count++; + if(goingToFail) { + throw new RuntimeException("failure in the reader"); + } + else { + return delegate.read(); + } + } + + public void setGoingToFail(boolean goingToFail) { + this.goingToFail = goingToFail; + } + + public int getCount() { + return count; + } + } + + public static class FailingListener extends ItemListenerSupport { + + private String methodToThrowExceptionFrom; + + public void setMethodToThrowExceptionFrom(String methodToThrowExceptionFrom) { + this.methodToThrowExceptionFrom = methodToThrowExceptionFrom; + } + + @Override + public void beforeRead() { + if (methodToThrowExceptionFrom.equals("beforeRead")) { + throw new RuntimeException("beforeRead caused this Exception"); + } + } + + @Override + public void afterRead(String item) { + if (methodToThrowExceptionFrom.equals("afterRead")) { + throw new RuntimeException("afterRead caused this Exception"); + } + } + + @Override + public void onReadError(Exception ex) { + if (methodToThrowExceptionFrom.equals("onReadError")) { + throw new RuntimeException("onReadError caused this Exception"); + } + } + + @Override + public void beforeProcess(String item) { + if (methodToThrowExceptionFrom.equals("beforeProcess")) { + throw new RuntimeException("beforeProcess caused this Exception"); + } + } + + @Override + public void afterProcess(String item, String result) { + if (methodToThrowExceptionFrom.equals("afterProcess")) { + throw new RuntimeException("afterProcess caused this Exception"); + } + } + + @Override + public void onProcessError(String item, Exception ex) { + if (methodToThrowExceptionFrom.equals("onProcessError")) { + throw new RuntimeException("onProcessError caused this Exception"); + } + } + + @Override + public void beforeWrite(List items) { + if (methodToThrowExceptionFrom.equals("beforeWrite")) { + throw new RuntimeException("beforeWrite caused this Exception"); + } + } + + @Override + public void afterWrite(List items) { + if (methodToThrowExceptionFrom.equals("afterWrite")) { + throw new RuntimeException("afterWrite caused this Exception"); + } + } + + @Override + public void onWriteError(Exception ex, List item) { + if (methodToThrowExceptionFrom.equals("onWriteError")) { + throw new RuntimeException("onWriteError caused this Exception"); + } + } + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTests.java index 8eeabbe47..3c3f8c2fa 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/builder/RegisterMultiListenerTests.java @@ -15,14 +15,12 @@ */ package org.springframework.batch.core.step.builder; -import static org.junit.Assert.assertEquals; - import java.util.List; - import javax.sql.DataSource; import org.junit.After; import org.junit.Test; + import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.ChunkListener; import org.springframework.batch.core.ExitStatus; @@ -55,6 +53,8 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import static org.junit.Assert.assertEquals; + /** * Test for registering a listener class that implements different listeners interfaces * just once in java based configuration.