diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/BatchRetryTemplate.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/BatchRetryTemplate.java index f926b8c66..8179ca874 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/BatchRetryTemplate.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/BatchRetryTemplate.java @@ -123,7 +123,7 @@ public class BatchRetryTemplate implements RetryOperations { } @Override - protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Exception e) { + protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Throwable e) { BatchRetryState batchState = (BatchRetryState) state; BatchRetryContext batchContext = (BatchRetryContext) context; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java index 3c0adc2e3..780e68929 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/Chunk.java @@ -222,7 +222,7 @@ public class Chunk implements Iterable { return next; } - public void remove(Exception e) { + public void remove(Throwable e) { remove(); skips.add(new SkipWrapper(next, e)); } 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 86e7170ab..423e2bbf5 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 @@ -248,7 +248,7 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor recoveryCallback = new RecoveryCallback() { public O recover(RetryContext context) throws Exception { - Exception e = context.getLastThrowable(); + Throwable e = context.getLastThrowable(); if (itemProcessSkipPolicy.shouldSkip(e, contribution.getStepSkipCount())) { contribution.incrementProcessSkipCount(); iterator.remove(e); @@ -315,7 +315,7 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor 1 && !rollbackClassifier.classify(e)) { throw new RetryException("Invalid retry state during write caused by " + "exception that does not classify for rollback: ", e); @@ -387,12 +387,12 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor wrapper : outputs.getSkips()) { - Exception e = wrapper.getException(); + Throwable e = wrapper.getException(); try { getListener().onSkipInWrite(wrapper.getItem(), e); } @@ -414,7 +414,7 @@ public class FaultTolerantChunkProcessor extends SimpleChunkProcessor extends SimpleChunkProcessor.ChunkIterator inputIterator, Chunk.ChunkIterator outputIterator, - Exception e, StepContribution contribution) { + Throwable e, StepContribution contribution) { logger.debug("Checking skip policy after failed write"); if (itemWriteSkipPolicy.shouldSkip(e, contribution.getStepSkipCount())) { contribution.incrementWriteSkipCount(); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipWrapper.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipWrapper.java index 74bdba1f0..7d678c0b5 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipWrapper.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SkipWrapper.java @@ -24,7 +24,7 @@ package org.springframework.batch.core.step.item; */ public class SkipWrapper { - final private Exception exception; + final private Throwable exception; final private T item; @@ -38,12 +38,12 @@ public class SkipWrapper { /** * @param e */ - public SkipWrapper(Exception e) { + public SkipWrapper(Throwable e) { this(null, e); } - public SkipWrapper(T item, Exception e) { + public SkipWrapper(T item, Throwable e) { this.item = item; this.exception = e; } @@ -52,7 +52,7 @@ public class SkipWrapper { * Public getter for the exception. * @return the exception */ - public Exception getException() { + public Throwable getException() { return exception; } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java index e52028a96..8e5f3f16e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantChunkProcessorTests.java @@ -99,10 +99,10 @@ public class FaultTolerantChunkProcessorTests { } /** - * An Error pops right back up (no skips, no retry) + * An Error can be retried or skipped but by default it is just propagated * @throws Exception */ - @Test(expected=AssertionError.class) + @Test public void testWriteSkipOnError() throws Exception { processor.setWriteSkipPolicy(new AlwaysSkipItemSkipPolicy()); processor.setItemWriter(new ItemWriter() { diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java index b3e4b8df6..8242aa027 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryContext.java @@ -64,6 +64,6 @@ public interface RetryContext extends AttributeAccessor { * be null if this is the first attempt, but also if the enclosing policy * decides not to provide it (e.g. because of concerns about memory usage). */ - Exception getLastThrowable(); + Throwable getLastThrowable(); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryPolicy.java index be38283ac..c22b17216 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryPolicy.java @@ -58,6 +58,6 @@ public interface RetryPolicy { * @param context the current status object. * */ - void registerThrowable(RetryContext context, Exception throwable); + void registerThrowable(RetryContext context, Throwable throwable); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryState.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryState.java index b17ea816c..0dee88f99 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryState.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/RetryState.java @@ -55,6 +55,6 @@ public interface RetryState { * @param exception the exception that caused a retry attempt to fail * @return true if this exception should cause a rollback */ - boolean rollbackFor(Exception exception); + boolean rollbackFor(Throwable exception); } \ No newline at end of file diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/context/RetryContextSupport.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/context/RetryContextSupport.java index c67765dbe..321c67ee5 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/context/RetryContextSupport.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/context/RetryContextSupport.java @@ -26,7 +26,7 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret private int count; - private Exception lastException; + private Throwable lastException; private RetryContext parent; @@ -51,7 +51,7 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret return count; } - public Exception getLastThrowable() { + public Throwable getLastThrowable() { return lastException; } @@ -69,7 +69,7 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret * @param throwable the exception that caused the current retry attempt to * fail. */ - public void registerThrowable(Exception throwable) { + public void registerThrowable(Throwable throwable) { this.lastException = throwable; if (throwable != null) count++; diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/CompositeRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/CompositeRetryPolicy.java index 88967ddf4..77ae573d8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/CompositeRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/CompositeRetryPolicy.java @@ -107,7 +107,7 @@ public class CompositeRetryPolicy implements RetryPolicy { * * @see org.springframework.batch.retry.RetryPolicy#close(org.springframework.batch.retry.RetryContext) */ - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { RetryContext[] contexts = ((CompositeRetryContext) context).contexts; RetryPolicy[] policies = ((CompositeRetryContext) context).policies; for (int i = 0; i < contexts.length; i++) { diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java index dafe3570b..94f24dbba 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/ExceptionClassifierRetryPolicy.java @@ -99,9 +99,9 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { * Delegate to the policy currently activated in the context. * * @see org.springframework.batch.retry.RetryPolicy#registerThrowable(org.springframework.batch.retry.RetryContext, - * Exception) + * Throwable) */ - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { RetryPolicy policy = (RetryPolicy) context; policy.registerThrowable(context, throwable); ((RetryContextSupport) context).registerThrowable(throwable); @@ -144,7 +144,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { return this; } - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { policy = exceptionClassifier.classify(throwable); Assert.notNull(policy, "Could not locate policy for exception=[" + throwable + "]."); this.context = getContext(policy, context.getParent()); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/NeverRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/NeverRetryPolicy.java index b69c3cc67..343cf9afe 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/NeverRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/NeverRetryPolicy.java @@ -60,11 +60,11 @@ public class NeverRetryPolicy implements RetryPolicy { } /** - * Do nothing. + * Make the throwable available for downstream use through the context. * @see org.springframework.batch.retry.RetryPolicy#registerThrowable(org.springframework.batch.retry.RetryContext, - * Exception) + * Throwable) */ - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { ((NeverRetryContext) context).setFinished(); ((RetryContextSupport) context).registerThrowable(throwable); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java index b74384e97..4ebecef9e 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/SimpleRetryPolicy.java @@ -57,7 +57,8 @@ public class SimpleRetryPolicy implements RetryPolicy { * attempts. */ public SimpleRetryPolicy() { - this(DEFAULT_MAX_ATTEMPTS, Collections., Boolean>singletonMap(Exception.class, true)); + this(DEFAULT_MAX_ATTEMPTS, Collections + ., Boolean> singletonMap(Exception.class, true)); } /** @@ -105,10 +106,9 @@ public class SimpleRetryPolicy implements RetryPolicy { /** * Update the status with another attempted retry and the latest exception. * - * @see org.springframework.batch.retry.RetryPolicy#registerThrowable(org.springframework.batch.retry.RetryContext, - * Exception) + * @see RetryPolicy#registerThrowable(RetryContext, Throwable) */ - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { SimpleRetryContext simpleContext = ((SimpleRetryContext) context); simpleContext.registerThrowable(throwable); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/TimeoutRetryPolicy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/TimeoutRetryPolicy.java index 887f7e3c5..bfc7d3952 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/TimeoutRetryPolicy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/policy/TimeoutRetryPolicy.java @@ -61,7 +61,7 @@ public class TimeoutRetryPolicy implements RetryPolicy { return new TimeoutRetryContext(parent, timeout); } - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { ((RetryContextSupport) context).registerThrowable(throwable); // otherwise no-op - we only time out, otherwise retry everything... } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/DefaultRetryState.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/DefaultRetryState.java index cdb84c425..339d22c4d 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/DefaultRetryState.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/DefaultRetryState.java @@ -103,10 +103,10 @@ public class DefaultRetryState implements RetryState { * (non-Javadoc) * * @see - * org.springframework.batch.retry.IRetryState#rollbackFor(java.lang.Exception + * org.springframework.batch.retry.RetryState#rollbackFor(java.lang.Throwable * ) */ - public boolean rollbackFor(Exception exception) { + public boolean rollbackFor(Throwable exception) { if (rollbackClassifier == null) { return true; } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java index 3b0a12287..8704ab760 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/retry/support/RetryTemplate.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.batch.repeat.RepeatException; import org.springframework.batch.retry.ExhaustedRetryException; import org.springframework.batch.retry.RecoveryCallback; import org.springframework.batch.retry.RetryCallback; @@ -236,7 +237,7 @@ public class RetryTemplate implements RetryOperations { lastException = null; return retryCallback.doWithRetry(context); } - catch (Exception e) { + catch (Throwable e) { lastException = e; @@ -258,7 +259,7 @@ public class RetryTemplate implements RetryOperations { logger.debug("Checking for rethrow: count=" + context.getRetryCount()); if (shouldRethrow(retryPolicy, context, state)) { logger.debug("Rethrow in retry for policy: count=" + context.getRetryCount()); - throw e; + throw wrapIfNecessary(e); } } @@ -327,7 +328,7 @@ public class RetryTemplate implements RetryOperations { * @param context * @param e */ - protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Exception e) { + protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Throwable e) { if (state != null) { Object key = state.getKey(); if (context.getRetryCount() > 0 && !retryContextCache.containsKey(key)) { @@ -415,7 +416,7 @@ public class RetryTemplate implements RetryOperations { throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path", context .getLastThrowable()); } - throw context.getLastThrowable(); + throw wrapIfNecessary(context.getLastThrowable()); } /** @@ -462,4 +463,20 @@ public class RetryTemplate implements RetryOperations { } } + /** + * Re-throws the original throwable if it is unchecked, wraps checked + * exceptions into {@link RepeatException}. + */ + private static Exception wrapIfNecessary(Throwable throwable) { + if (throwable instanceof Error) { + throw (Error) throwable; + } + else if (throwable instanceof Exception) { + return (Exception) throwable; + } + else { + return new RetryException("Exception in batch process", throwable); + } + } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/CompositeRetryPolicyTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/CompositeRetryPolicyTests.java index a7335dc20..99e9d4afd 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/CompositeRetryPolicyTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/CompositeRetryPolicyTests.java @@ -62,7 +62,7 @@ public class CompositeRetryPolicyTests extends TestCase { return !errorRegistered; } - public void registerThrowable(RetryContext context, Exception throwable) { + public void registerThrowable(RetryContext context, Throwable throwable) { errorRegistered = true; } } }); @@ -70,7 +70,7 @@ public class CompositeRetryPolicyTests extends TestCase { assertNotNull(context); assertTrue(policy.canRetry(context)); policy.registerThrowable(context, null); - assertFalse(policy.canRetry(context)); + assertFalse("Should be still able to retry", policy.canRetry(context)); } public void testNonTrivialPoliciesClose() throws Exception { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java index 30db12318..74b4ae1d3 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/support/RetryTemplateTests.java @@ -16,14 +16,18 @@ package org.springframework.batch.retry.support; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; -import static org.easymock.EasyMock.*; - import java.util.Collections; import org.junit.Test; @@ -295,7 +299,7 @@ public class RetryTemplateTests { }, null, new DefaultRetryState(tested) { @Override - public boolean rollbackFor(Exception exception) { + public boolean rollbackFor(Throwable exception) { return true; }