Improve parameterisation of exception handler

This commit is contained in:
dsyer
2008-08-23 13:22:35 +00:00
parent c3ed34b3a8
commit 8e498f0d52
21 changed files with 731 additions and 655 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.batch.core.step.item;
import java.util.Collection;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.exception.ExceptionHandler;
import org.springframework.batch.repeat.support.RepeatSynchronizationManager;
@@ -52,13 +54,13 @@ public class SimpleRetryExceptionHandler extends RetryListenerSupport implements
* exception is encountered
* @param exceptionHandler the delegate to use if an exception actually
* needs to be handled
* @param classes
* @param fatalExceptionClasses
*/
public SimpleRetryExceptionHandler(RetryPolicy retryPolicy, ExceptionHandler exceptionHandler, Class<?>[] classes) {
public SimpleRetryExceptionHandler(RetryPolicy retryPolicy, ExceptionHandler exceptionHandler, Collection<Class<? extends Throwable>> fatalExceptionClasses) {
this.retryPolicy = retryPolicy;
this.exceptionHandler = exceptionHandler;
this.fatalExceptionClassifier = new BinaryExceptionClassifier();
fatalExceptionClassifier.setExceptionClasses(classes);
fatalExceptionClassifier.setExceptionClasses(fatalExceptionClasses);
}
/**

View File

@@ -16,11 +16,11 @@
package org.springframework.batch.core.step.skip;
import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.HashSet;
import java.util.Map;
import java.util.ArrayList;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
@@ -70,7 +70,7 @@ public class LimitCheckingItemSkipPolicy implements ItemSkipPolicy {
private final int skipLimit;
private ExceptionClassifier<String> exceptionClassifier;
private ExceptionClassifier<String,Throwable> exceptionClassifier;
/**
* Convenience constructor that assumes all exception types are skippable
@@ -80,7 +80,7 @@ public class LimitCheckingItemSkipPolicy implements ItemSkipPolicy {
@SuppressWarnings("unchecked")
public LimitCheckingItemSkipPolicy(int skipLimit) {
this(skipLimit,
new ArrayList<Class<?>>(){{add(Exception.class);}},
new HashSet<Class<? extends Throwable>>(){{add(Exception.class);}},
Collections.EMPTY_LIST);
}
@@ -92,14 +92,14 @@ public class LimitCheckingItemSkipPolicy implements ItemSkipPolicy {
* (non-critical)
* @param fatalExceptions exception classes that should never be skipped
*/
public LimitCheckingItemSkipPolicy(int skipLimit, List<Class<?>> skippableExceptions, List<Class<?>>fatalExceptions) {
public LimitCheckingItemSkipPolicy(int skipLimit, Collection<Class<? extends Throwable>> skippableExceptions, Collection<Class<? extends Throwable>>fatalExceptions) {
this.skipLimit = skipLimit;
SubclassExceptionClassifier exceptionClassifier = new SubclassExceptionClassifier();
Map<Class<?>, String> typeMap = new HashMap<Class<?>, String>();
for (Class<?> throwable : skippableExceptions) {
Map<Class<? extends Throwable>, String> typeMap = new HashMap<Class<? extends Throwable>, String>();
for (Class<? extends Throwable> throwable : skippableExceptions) {
typeMap.put(throwable, SKIP);
}
for (Class<?> throwable : fatalExceptions) {
for (Class<? extends Throwable> throwable : fatalExceptions) {
typeMap.put(throwable, NEVER_SKIP);
}
exceptionClassifier.setTypeMap(typeMap);

View File

@@ -40,10 +40,9 @@ public class LimitCheckingItemSkipPolicyTests {
@Before
public void setUp() throws Exception {
List<Class<?>> skippableExceptions = new ArrayList<Class<?>>();
List<Class<? extends Throwable>> skippableExceptions = new ArrayList<Class<? extends Throwable>>();
skippableExceptions.add(FlatFileParseException.class);
List<Class<?>> fatalExceptions = new ArrayList<Class<?>>();
List<Class<? extends Throwable>> fatalExceptions = new ArrayList<Class<? extends Throwable>>();
failurePolicy = new LimitCheckingItemSkipPolicy(1, skippableExceptions, fatalExceptions);
}

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.batch.core.step.item;
import java.util.Collection;
import java.util.HashSet;
import junit.framework.TestCase;
import org.springframework.batch.core.step.item.SimpleRetryExceptionHandler;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
import org.springframework.batch.repeat.exception.SimpleLimitExceptionHandler;
@@ -37,6 +39,7 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
/*
* (non-Javadoc)
*
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
@@ -45,6 +48,7 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
/*
* (non-Javadoc)
*
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
@@ -53,14 +57,20 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}.
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}
* .
*/
public void testRethrowWhenRetryExhausted() throws Throwable {
RetryPolicy retryPolicy = new NeverRetryPolicy();
RuntimeException ex = new RuntimeException("foo");
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex, new Class[] { Error.class });
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex,
new HashSet<Class<? extends Throwable>>() {
{
add(Error.class);
}
});
// Then pretend to handle the exception in the parent context...
try {
@@ -79,14 +89,20 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}.
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}
* .
*/
public void testNoRethrowWhenRetryNotExhausted() throws Throwable {
RetryPolicy retryPolicy = new AlwaysRetryPolicy();
RuntimeException ex = new RuntimeException("foo");
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex, new Class[] { Error.class });
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex,
new HashSet<Class<? extends Throwable>>() {
{
add(Error.class);
}
});
// Then pretend to handle the exception in the parent context...
handler.handleException(context.getParent(), ex);
@@ -97,14 +113,20 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}.
* {@link org.springframework.batch.core.step.item.SimpleRetryExceptionHandler#handleException(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}
* .
*/
public void testRethrowWhenFatal() throws Throwable {
RetryPolicy retryPolicy = new AlwaysRetryPolicy();
RuntimeException ex = new RuntimeException("foo");
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex, new Class[] { RuntimeException.class });
SimpleRetryExceptionHandler handler = getHandlerAfterRetry(retryPolicy, ex,
new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
// Then pretend to handle the exception in the parent context...
try {
@@ -125,7 +147,8 @@ public class SimpleRetryExceptionHandlerTests extends TestCase {
* @param ex
* @return
*/
private SimpleRetryExceptionHandler getHandlerAfterRetry(RetryPolicy retryPolicy, RuntimeException ex, Class<?>[] fatalExceptions) {
private SimpleRetryExceptionHandler getHandlerAfterRetry(RetryPolicy retryPolicy, RuntimeException ex,
Collection<Class<? extends Throwable>> fatalExceptions) {
// Always rethrow if the retry is exhausted
SimpleRetryExceptionHandler handler = new SimpleRetryExceptionHandler(retryPolicy,

View File

@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -45,7 +46,12 @@ public class SkipLimitStepFactoryBeanTests {
private SkipLimitStepFactoryBean<String, String> factory = new SkipLimitStepFactoryBean<String, String>();
private Class<?>[] skippableExceptions = new Class[] { SkippableException.class, SkippableRuntimeException.class };
private Collection<Class<? extends Throwable>> skippableExceptions = new HashSet<Class<? extends Throwable>>() {
{
add(SkippableException.class);
add(SkippableRuntimeException.class);
}
};
private SkipReaderStub reader = new SkipReaderStub();
@@ -133,7 +139,11 @@ public class SkipLimitStepFactoryBeanTests {
*/
@Test
public void testFatalException() throws Exception {
factory.setFatalExceptionClasses(new Class[] { FatalRuntimeException.class });
factory.setFatalExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(FatalRuntimeException.class);
}
});
factory.setItemWriter(new SkipWriterStub() {
public void write(List<? extends String> items) {
throw new FatalRuntimeException("Ouch!");
@@ -194,7 +204,11 @@ public class SkipLimitStepFactoryBeanTests {
factory.setSkipLimit(3);
factory.setItemReader(reader);
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
Step step = (Step) factory.getObject();
@@ -238,7 +252,11 @@ public class SkipLimitStepFactoryBeanTests {
throw new RuntimeException("oops");
}
} });
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
Step step = (Step) factory.getObject();
@@ -275,7 +293,11 @@ public class SkipLimitStepFactoryBeanTests {
throw new RuntimeException("oops");
}
} });
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
Step step = (Step) factory.getObject();
@@ -311,15 +333,16 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = jobExecution.createStepExecution(step);
// TODO: uncomment this!
// step.execute(stepExecution);
// assertEquals(4, stepExecution.getSkipCount());
// assertEquals(3, stepExecution.getReadSkipCount());
// assertEquals(1, stepExecution.getWriteSkipCount());
//
// // skipped 2,3,4,5
// List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray("1,6"));
// assertEquals(expectedOutput, writer.written);
// TODO: uncomment this!
// step.execute(stepExecution);
// assertEquals(4, stepExecution.getSkipCount());
// assertEquals(3, stepExecution.getReadSkipCount());
// assertEquals(1, stepExecution.getWriteSkipCount());
//
// // skipped 2,3,4,5
// List<String> expectedOutput =
// Arrays.asList(StringUtils.commaDelimitedListToStringArray("1,6"));
// assertEquals(expectedOutput, writer.written);
}
@@ -343,21 +366,26 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = jobExecution.createStepExecution(step);
// TODO: uncomment this!
// step.execute(stepExecution);
// assertEquals(4, stepExecution.getSkipCount());
// assertEquals(2, stepExecution.getReadSkipCount());
// assertEquals(2, stepExecution.getWriteSkipCount());
//
// // skipped 2,3,4,5
// List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray("1,6,7"));
// assertEquals(expectedOutput, writer.written);
// TODO: uncomment this!
// step.execute(stepExecution);
// assertEquals(4, stepExecution.getSkipCount());
// assertEquals(2, stepExecution.getReadSkipCount());
// assertEquals(2, stepExecution.getWriteSkipCount());
//
// // skipped 2,3,4,5
// List<String> expectedOutput =
// Arrays.asList(StringUtils.commaDelimitedListToStringArray("1,6,7"));
// assertEquals(expectedOutput, writer.written);
}
@Test
public void testDefaultSkipPolicy() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
factory.setSkipLimit(1);
List<String> items = Arrays.asList(new String[] { "a", "b", "c" });
ItemReader<String> provider = new ListItemReader<String>(items) {

View File

@@ -22,6 +22,7 @@ import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -100,7 +101,11 @@ public class StatefulRetryStepFactoryBeanTests {
factory.setItemWriter(processor);
factory.setJobRepository(repository);
factory.setTransactionManager(new ResourcelessTransactionManager());
factory.setRetryableExceptionClasses(new Class[] { Exception.class });
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
factory.setCommitInterval(1); // trivial by default
JobSupport job = new JobSupport("jobName");
@@ -146,7 +151,7 @@ public class StatefulRetryStepFactoryBeanTests {
};
factory.setItemReader(provider);
factory.setRetryLimit(10);
factory.setSkippableExceptionClasses(new Class[0]);
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>());
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -166,7 +171,11 @@ public class StatefulRetryStepFactoryBeanTests {
@Test
public void testSkipAndRetry() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { Exception.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
factory.setSkipLimit(2);
List<String> items = Arrays.asList(new String[] { "a", "b", "c", "d", "e", "f" });
ItemReader<String> provider = new ListItemReader<String>(items) {
@@ -195,7 +204,11 @@ public class StatefulRetryStepFactoryBeanTests {
@Test
public void testSkipAndRetryWithWriteFailure() throws Exception {
factory.setSkippableExceptionClasses(new Class[] { RetryException.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RetryException.class);
}
});
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
public void onSkipInWrite(Object item, Throwable t) {
recovered.add(item);
@@ -226,7 +239,11 @@ public class StatefulRetryStepFactoryBeanTests {
factory.setItemReader(provider);
factory.setItemWriter(itemWriter);
factory.setRetryLimit(5);
factory.setRetryableExceptionClasses(new Class[] { RuntimeException.class });
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
AbstractStep step = (AbstractStep) factory.getObject();
step.setName("mytest");
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -248,7 +265,11 @@ public class StatefulRetryStepFactoryBeanTests {
public void testSkipAndRetryWithWriteFailureAndNonTrivialCommitInterval() throws Exception {
factory.setCommitInterval(3);
factory.setSkippableExceptionClasses(new Class[] { RetryException.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RetryException.class);
}
});
factory.setListeners(new StepListener[] { new SkipListenerSupport() {
public void onSkipInWrite(Object item, Throwable t) {
recovered.add(item);
@@ -279,7 +300,11 @@ public class StatefulRetryStepFactoryBeanTests {
factory.setItemReader(provider);
factory.setItemWriter(itemWriter);
factory.setRetryLimit(5);
factory.setRetryableExceptionClasses(new Class[] { RuntimeException.class });
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
AbstractStep step = (AbstractStep) factory.getObject();
step.setName("mytest");
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
@@ -300,7 +325,11 @@ public class StatefulRetryStepFactoryBeanTests {
@Test
public void testRetryWithNoSkip() throws Exception {
factory.setRetryableExceptionClasses(new Class[] { Exception.class });
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(Exception.class);
}
});
factory.setRetryLimit(4);
factory.setSkipLimit(0);
List<String> items = Arrays.asList(new String[] { "b" });
@@ -346,9 +375,13 @@ public class StatefulRetryStepFactoryBeanTests {
public void testNonSkippableException() throws Exception {
// Very specific skippable exception
factory.setSkippableExceptionClasses(new Class[] { UnsupportedOperationException.class });
factory.setSkippableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(UnsupportedOperationException.class);
}
});
// ...which is not retryable...
factory.setRetryableExceptionClasses(new Class<?>[0]);
factory.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>());
factory.setSkipLimit(1);
List<String> items = Arrays.asList(new String[] { "b" });

View File

@@ -56,7 +56,7 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
protected final Log logger = LogFactory.getLog(LogOrRethrowExceptionHandler.class);
private ExceptionClassifier<String> exceptionClassifier = new ExceptionClassifierSupport() {
private ExceptionClassifier<String,Throwable> exceptionClassifier = new ExceptionClassifierSupport() {
public String classify(Throwable throwable) {
return RETHROW;
}
@@ -68,7 +68,7 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
*
* @param exceptionClassifier the ExceptionClassifier to use
*/
public void setExceptionClassifier(ExceptionClassifier<String> exceptionClassifier) {
public void setExceptionClassifier(ExceptionClassifier<String,Throwable> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
@@ -81,7 +81,7 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
*/
public void handleException(RepeatContext context, Throwable throwable) throws Throwable {
Object key = exceptionClassifier.classify(throwable);
String key = exceptionClassifier.classify(throwable);
if (ERROR.equals(key)) {
logger.error("Exception encountered in batch repeat.", throwable);
} else if (WARN.equals(key)) {

View File

@@ -18,7 +18,6 @@ package org.springframework.batch.repeat.exception;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -26,7 +25,6 @@ import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextCounter;
import org.springframework.batch.support.ExceptionClassifier;
import org.springframework.batch.support.ExceptionClassifierSupport;
import org.springframework.util.Assert;
/**
* Implementation of {@link ExceptionHandler} that rethrows when exceptions of a
@@ -41,9 +39,9 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
protected final Log logger = LogFactory.getLog(RethrowOnThresholdExceptionHandler.class);
private ExceptionClassifier<String> exceptionClassifier = new ExceptionClassifierSupport();
private ExceptionClassifier<String,Throwable> exceptionClassifier = new ExceptionClassifierSupport();
private Map<Object, Integer> thresholds = new HashMap<Object, Integer>();
private Map<String, Integer> thresholds = new HashMap<String, Integer>();
private boolean useParent = false;
@@ -75,15 +73,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
*
* @param thresholds the threshold value map.
*/
public void setThresholds(Map<Object, Integer> thresholds) {
for (Entry<Object, Integer> entry : thresholds.entrySet()) {
if (!(entry.getKey() instanceof String)) {
logger.warn("Key in thresholds map is not of type String: " + entry.getKey());
}
Assert.state(entry.getValue() != null, "Threshold value must be of type Integer. "
+ "Try using the value-type attribute if you care configuring this map via xml.");
}
public void setThresholds(Map<String, Integer> thresholds) {
this.thresholds = thresholds;
}
@@ -95,7 +85,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
*
* @param exceptionClassifier ExceptionClassifier to use
*/
public void setExceptionClassifier(ExceptionClassifier<String> exceptionClassifier) {
public void setExceptionClassifier(ExceptionClassifier<String,Throwable> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}

View File

@@ -118,7 +118,7 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
* @param limit the limit
*/
public void setLimit(final int limit) {
delegate.setThresholds(new HashMap<Object, Integer>() {
delegate.setThresholds(new HashMap<String, Integer>() {
{
put(ExceptionClassifierSupport.DEFAULT, 0);
put(TX_INVALID, limit);

View File

@@ -37,7 +37,7 @@ import org.springframework.util.Assert;
*/
public class ExceptionClassifierRetryPolicy extends AbstractStatelessRetryPolicy {
private ExceptionClassifier<String> exceptionClassifier = new ExceptionClassifierSupport();
private ExceptionClassifier<String,Throwable> exceptionClassifier = new ExceptionClassifierSupport();
private Map<String, RetryPolicy> policyMap = new HashMap<String, RetryPolicy>();
@@ -64,7 +64,7 @@ public class ExceptionClassifierRetryPolicy extends AbstractStatelessRetryPolicy
*
* @param exceptionClassifier ExceptionClassifier to use
*/
public void setExceptionClassifier(ExceptionClassifier<String> exceptionClassifier) {
public void setExceptionClassifier(ExceptionClassifier<String,Throwable> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
@@ -113,7 +113,7 @@ public class ExceptionClassifierRetryPolicy extends AbstractStatelessRetryPolicy
private class ExceptionClassifierRetryContext extends RetryContextSupport implements RetryPolicy {
private ExceptionClassifier<String> exceptionClassifier;
private ExceptionClassifier<String,Throwable> exceptionClassifier;
// Dynamic: depends on the latest exception:
RetryPolicy policy;
@@ -126,7 +126,7 @@ public class ExceptionClassifierRetryPolicy extends AbstractStatelessRetryPolicy
Map<RetryPolicy, RetryContext> contexts = new HashMap<RetryPolicy, RetryContext>();
public ExceptionClassifierRetryContext(RetryContext parent, ExceptionClassifier<String> exceptionClassifier) {
public ExceptionClassifierRetryContext(RetryContext parent, ExceptionClassifier<String,Throwable> exceptionClassifier) {
super(parent);
this.exceptionClassifier = exceptionClassifier;
Object key = exceptionClassifier.getDefault();

View File

@@ -16,6 +16,9 @@
package org.springframework.batch.retry.policy;
import java.util.Collection;
import java.util.HashSet;
import org.springframework.batch.retry.RetryCallback;
import org.springframework.batch.retry.RetryContext;
import org.springframework.batch.retry.context.RetryContextSupport;
@@ -67,8 +70,13 @@ public class SimpleRetryPolicy extends AbstractStatelessRetryPolicy {
*/
public SimpleRetryPolicy(int maxAttempts) {
super();
setRetryableExceptionClasses(new Class[] { Exception.class });
setFatalExceptionClasses(new Class[] { Error.class });
Collection<Class<? extends Throwable>> classes;
classes = new HashSet<Class<? extends Throwable>>();
classes.add(Exception.class);
setRetryableExceptionClasses(classes);
classes = new HashSet<Class<? extends Throwable>>();
classes.add(Error.class);
setFatalExceptionClasses(classes);
this.maxAttempts = maxAttempts;
}
@@ -103,7 +111,7 @@ public class SimpleRetryPolicy extends AbstractStatelessRetryPolicy {
*
* @param retryableExceptionClasses defaults to {@link Exception}.
*/
public final void setRetryableExceptionClasses(Class<?>[] retryableExceptionClasses) {
public final void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
retryableClassifier.setExceptionClasses(retryableExceptionClasses);
}
@@ -114,7 +122,7 @@ public class SimpleRetryPolicy extends AbstractStatelessRetryPolicy {
*
* @param fatalExceptionClasses defaults to {@link Exception}.
*/
public final void setFatalExceptionClasses(Class<?>[] fatalExceptionClasses) {
public final void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
fatalClassifier.setExceptionClasses(fatalExceptionClasses);
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.batch.support;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -41,9 +42,9 @@ public class BinaryExceptionClassifier extends ExceptionClassifierSupport {
*
* @param exceptionClasses defaults to {@link Exception}.
*/
public final void setExceptionClasses(Class<?>[] exceptionClasses) {
Map<Class<?>, String> temp = new HashMap<Class<?>, String>();
for (Class<?> exceptionClass : exceptionClasses) {
public final void setExceptionClasses(Collection<Class<? extends Throwable>> exceptionClasses) {
Map<Class<? extends Throwable>, String> temp = new HashMap<Class<? extends Throwable>, String>();
for (Class<? extends Throwable> exceptionClass : exceptionClasses) {
temp.put(exceptionClass, NON_DEFAULT);
}
this.delegate.setTypeMap(temp);
@@ -55,7 +56,7 @@ public class BinaryExceptionClassifier extends ExceptionClassifierSupport {
*
* @param throwable the Throwable to classify
* @return true if it is default classified (i.e. not on the list provided
* in {@link #setExceptionClasses(Class[])}.
* in {@link #setExceptionClasses(Collection)}.
*/
public boolean isDefault(Throwable throwable) {
return classify(throwable).equals(DEFAULT);
@@ -67,7 +68,7 @@ public class BinaryExceptionClassifier extends ExceptionClassifierSupport {
* of the throwable or one of its ancestors is on the exception class list
* the classification is as {@link #NON_DEFAULT}.
*
* @see #setExceptionClasses(Class[])
* @see #setExceptionClasses(Collection)
* @see ExceptionClassifierSupport#classify(Throwable)
*/
public String classify(Throwable throwable) {

View File

@@ -22,24 +22,24 @@ package org.springframework.batch.support;
* @author Dave Syer
*
*/
public interface ExceptionClassifier<T> {
public interface ExceptionClassifier<T,C> {
/**
* Get a default value, normally the same as would be returned by
* {@link #classify(Throwable)} with null argument.
* {@link #classify(Object)} with null argument.
*
* @return the default value.
*/
T getDefault();
/**
* Classify the given exception and return a non-null object. The return
* Classify the given object and return a non-null object. The return
* type depends on the implementation but typically would be a key in a map
* which the client maintains.
*
* @param throwable the input exception. Can be null.
* @param classifiable the input object. Can be null.
* @return an object.
*/
T classify(Throwable throwable);
T classify(C classifiable);
}

View File

@@ -23,7 +23,7 @@ package org.springframework.batch.support;
* @author Dave Syer
*
*/
public class ExceptionClassifierSupport implements ExceptionClassifier<String> {
public class ExceptionClassifierSupport implements ExceptionClassifier<String,Throwable> {
/**
* Default classification key.
@@ -33,7 +33,7 @@ public class ExceptionClassifierSupport implements ExceptionClassifier<String> {
/**
* Always returns the value of {@link #DEFAULT}.
*
* @see org.springframework.batch.support.ExceptionClassifier#classify(java.lang.Throwable)
* @see org.springframework.batch.support.ExceptionClassifier#classify(Object)
*/
public String classify(Throwable throwable) {
return DEFAULT;

View File

@@ -30,7 +30,7 @@ import org.springframework.util.Assert;
*/
public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
private Map<Class<?>, String> classified = new HashMap<Class<?>, String>();
private Map<Class<? extends Throwable>, String> classified = new HashMap<Class<? extends Throwable>, String>();
/**
* Map of Throwable class types to keys for the classifier. Any subclass of
@@ -39,9 +39,9 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
*
* @param typeMap the typeMap to set
*/
public final void setTypeMap(Map<Class<?>, String> typeMap) {
Map<Class<?>, String> map = new HashMap<Class<?>, String>();
for (Map.Entry<Class<?>, String> entry : typeMap.entrySet()) {
public final void setTypeMap(Map<Class<? extends Throwable>, String> typeMap) {
Map<Class<? extends Throwable>, String> map = new HashMap<Class<? extends Throwable>, String>();
for (Map.Entry<Class<? extends Throwable>, String> entry : typeMap.entrySet()) {
addRetryableExceptionClass(entry.getKey(), entry.getValue(), map);
}
this.classified = map;
@@ -59,15 +59,15 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
return super.classify(throwable);
}
Class<?> exceptionClass = throwable.getClass();
Class<? extends Throwable> exceptionClass = throwable.getClass();
if (classified.containsKey(exceptionClass)) {
return classified.get(exceptionClass);
}
// check for subclasses
Set<Class<?>> classes = new TreeSet<Class<?>>(new ClassComparator());
Set<Class<? extends Throwable>> classes = new TreeSet<Class<? extends Throwable>>(new ClassComparator());
classes.addAll(classified.keySet());
for (Class<?> cls : classes) {
for (Class<? extends Throwable> cls : classes) {
if (cls.isAssignableFrom(exceptionClass)) {
String value = classified.get(cls);
addRetryableExceptionClass(exceptionClass, value, this.classified);
@@ -78,7 +78,7 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
return super.classify(throwable);
}
private void addRetryableExceptionClass(Class<?> exceptionClass, String classifiedAs, Map<Class<?>, String> map) {
private void addRetryableExceptionClass(Class<? extends Throwable> exceptionClass, String classifiedAs, Map<Class<? extends Throwable>, String> map) {
Assert.isAssignable(Throwable.class, exceptionClass);
map.put(exceptionClass, classifiedAs);
}

View File

@@ -55,7 +55,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
return "RuntimeException";
}
});
handler.setThresholds(Collections.singletonMap((Object)"RuntimeException", new Integer(1)));
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
// No exception...
handler.handleException(context, new RuntimeException("Foo"));
RepeatContextCounter counter = new RepeatContextCounter(context, RethrowOnThresholdExceptionHandler.class.getName() + ".RuntimeException");
@@ -69,7 +69,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
return "RuntimeException";
}
});
handler.setThresholds(Collections.singletonMap((Object)"RuntimeException", new Integer(2)));
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(2)));
// No exception...
handler.handleException(context, new RuntimeException("Foo"));
handler.handleException(context, new RuntimeException("Foo"));
@@ -88,7 +88,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
return "RuntimeException";
}
});
handler.setThresholds(Collections.singletonMap((Object)"RuntimeException", new Integer(1)));
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
// No exception...
handler.handleException(context, new RuntimeException("Foo"));
context = new RepeatContextSupport(parent);
@@ -107,7 +107,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
return "RuntimeException";
}
});
handler.setThresholds(Collections.singletonMap((Object)"RuntimeException", new Integer(1)));
handler.setThresholds(Collections.singletonMap("RuntimeException", new Integer(1)));
handler.setUseParent(true);
// No exception...
handler.handleException(context, new RuntimeException("Foo"));
@@ -120,16 +120,5 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
assertEquals("Foo", e.getMessage());
}
}
public void testNotStringAsKey() throws Exception {
try {
handler.setThresholds(Collections.singletonMap((Object)RuntimeException.class, new Integer(1)));
// It's not an error, but not advised...
}
catch (RuntimeException e) {
throw e;
}
}
}

View File

@@ -16,35 +16,30 @@
package org.springframework.batch.retry.policy;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import junit.framework.TestCase;
import org.springframework.batch.retry.RetryContext;
public class SimpleRetryPolicyTests extends TestCase {
public void testSetInvalidExceptionClass() throws Exception {
try {
new SimpleRetryPolicy().setRetryableExceptionClasses(new Class[] { String.class });
fail("Should only be able to set Exception classes.");
}
catch (IllegalArgumentException ex) {
}
}
public void testCanRetryIfNoException() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
RetryContext context = policy.open(null, null);
assertTrue(policy.canRetry(context));
}
@SuppressWarnings("unchecked")
public void testEmptyExceptionsNeverRetry() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
RetryContext context = policy.open(null, null);
// We can't retry any exceptions...
policy.setRetryableExceptionClasses(new Class[0]);
policy.setRetryableExceptionClasses(Collections.EMPTY_SET);
// ...so we can't retry this one...
policy.registerThrowable(context, new IllegalStateException());
@@ -92,14 +87,24 @@ public class SimpleRetryPolicyTests extends TestCase {
public void testFatalOverridesRetryable() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
policy.setFatalExceptionClasses(new Class[] {Exception.class});
policy.setRetryableExceptionClasses(new Class[] {RuntimeException.class});
policy.setFatalExceptionClasses(getClasses(Exception.class));
policy.setRetryableExceptionClasses(getClasses(RuntimeException.class));
RetryContext context = policy.open(null, null);
assertNotNull(context);
policy.registerThrowable(context, new RuntimeException("foo"));
assertFalse(policy.canRetry(context));
}
/**
* @param cls
* @return
*/
private Collection<Class<? extends Throwable>> getClasses(Class<? extends Throwable> cls) {
Collection<Class<? extends Throwable>> classes = new HashSet<Class<? extends Throwable>>();
classes.add(cls);
return classes;
}
public void testParent() throws Exception {
SimpleRetryPolicy policy = new SimpleRetryPolicy();
RetryContext context = policy.open(null, null);

View File

@@ -16,6 +16,8 @@
package org.springframework.batch.retry.support;
import java.util.HashSet;
import junit.framework.TestCase;
import org.springframework.batch.retry.ExhaustedRetryException;
@@ -96,7 +98,11 @@ public class RetryTemplateTests extends TestCase {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy();
template.setRetryPolicy(policy);
policy.setRetryableExceptionClasses(new Class[] { RuntimeException.class });
policy.setRetryableExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(RuntimeException.class);
}
});
int attempts = 3;

View File

@@ -16,7 +16,7 @@
package org.springframework.batch.support;
import org.springframework.batch.support.BinaryExceptionClassifier;
import java.util.HashSet;
import junit.framework.TestCase;
@@ -33,8 +33,11 @@ public class BinaryExceptionClassifierTests extends TestCase {
}
public void testClassifyExactMatch() {
classifier.setExceptionClasses(new Class[] {IllegalStateException.class});
classifier.setExceptionClasses(new HashSet<Class<? extends Throwable>>() {
{
add(IllegalStateException.class);
}
});
assertEquals(false, classifier.isDefault(new IllegalStateException("Foo")));
}
}

View File

@@ -33,28 +33,28 @@ public class SubclassExceptionClassifierTests extends TestCase {
}
public void testClassifyExactMatch() {
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
classifier.setTypeMap(new LinkedHashMap<Class<? extends Throwable>, String>() {{
put(IllegalStateException.class, "foo");
}});
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
public void testClassifySubclassMatch() {
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
classifier.setTypeMap(new LinkedHashMap<Class<? extends Throwable>, String>() {{
put(RuntimeException.class, "foo");
}});
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
public void testClassifySuperclassDoesNotMatch() {
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
classifier.setTypeMap(new LinkedHashMap<Class<? extends Throwable>, String>() {{
put(IllegalStateException.class, "foo");
}});
assertEquals(classifier.getDefault(), classifier.classify(new RuntimeException("Foo")));
}
public void testClassifyAncestorMatch() {
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
classifier.setTypeMap(new LinkedHashMap<Class<? extends Throwable>, String>() {{
put(Exception.class, "bar");
put(IllegalArgumentException.class, "foo");
put(RuntimeException.class, "bucket");