diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipLimitStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipLimitStepFactoryBean.java index b114ff0b0..5e6856d8f 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipLimitStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipLimitStepFactoryBean.java @@ -435,6 +435,8 @@ public class SkipLimitStepFactoryBean extends SimpleStepFactoryBean catch (RuntimeException ex) { throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, t); } + } else { + throw new RetryException("Non-skippable exception in recoverer", t); } return null; } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/ItemSkipPolicy.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/ItemSkipPolicy.java index 8a5c84f0d..5975a84ef 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/ItemSkipPolicy.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/skip/ItemSkipPolicy.java @@ -29,7 +29,7 @@ public interface ItemSkipPolicy { * * @param t exception encountered while reading * @param skipCount currently running count of skips - * @return true if reading should continue, false otherwise. + * @return true if processing should continue, false otherwise. * @throws SkipLimitExceededException if a limit is breached * @throws IllegalArgumentException if the exception is null */ diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBeanTests.java index f2f9b21b1..9973a3c14 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBeanTests.java @@ -286,6 +286,56 @@ public class StatefulRetryStepFactoryBeanTests { assertEquals(0, stepExecution.getItemCount()); } + @Test + public void testNonSkippableException() throws Exception { + + // Very specific skippable exception + factory.setSkippableExceptionClasses(new Class[] { UnsupportedOperationException.class }); + // ...which is not retryable... + factory.setRetryableExceptionClasses(new Class[0]); + + factory.setSkipLimit(1); + List items = Arrays.asList(new String[] { "b" }); + ItemReader provider = new ListItemReader(items) { + public String read() { + String item = super.read(); + provided.add(item); + count++; + return item; + } + }; + ItemWriter itemWriter = new ItemWriter() { + public void write(List item) throws Exception { + processed.addAll(item); + logger.debug("Write Called! Item: [" + item + "]"); + throw new RuntimeException("Write error - planned but not skippable."); + } + }; + factory.setItemReader(provider); + factory.setItemWriter(itemWriter); + Step step = (Step) factory.getObject(); + + StepExecution stepExecution = new StepExecution(step.getName(), jobExecution); + try { + step.execute(stepExecution); + fail("Expected RuntimeException"); + } + catch (RuntimeException e) { + // expected + String message = e.getMessage(); + assertTrue("Wrong message: "+message, message.contains("Write error - planned but not skippable.")); + } + + assertEquals(0, stepExecution.getSkipCount()); + // [b] + assertEquals(1, provided.size()); + // [b] + assertEquals(1, processed.size()); + // [] + assertEquals(0, recovered.size()); + assertEquals(0, stepExecution.getItemCount()); + } + @Test public void testRetryPolicy() throws Exception { factory.setRetryPolicy(new SimpleRetryPolicy(4));