BATCH-758: generified ExceptionClassifier and ExceptionClassifierSupport and subclasses plus tests

This commit is contained in:
trisberg
2008-08-04 16:32:53 +00:00
parent e19044b6a6
commit 858208fa19
13 changed files with 97 additions and 99 deletions

View File

@@ -54,9 +54,9 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
private int skipLimit = 0;
private Class<?>[] skippableExceptionClasses = new Class[] { Exception.class };
private Class<?>[] skippableExceptionClasses = new Class<?>[] { Exception.class };
private Class<?>[] fatalExceptionClasses = new Class[] { Error.class };
private Class<?>[] fatalExceptionClasses = new Class<?>[] { Error.class };
private ItemKeyGenerator itemKeyGenerator;
@@ -64,7 +64,7 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
private int retryLimit = 0;
private Class<?>[] retryableExceptionClasses = new Class[] {};
private Class<?>[] retryableExceptionClasses = new Class<?>[] {};
private BackOffPolicy backOffPolicy;
@@ -206,8 +206,7 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
ExceptionClassifierRetryPolicy classifierRetryPolicy = new ExceptionClassifierRetryPolicy();
SubclassExceptionClassifier exceptionClassifier = new SubclassExceptionClassifier();
HashMap<Class<?>, String> exceptionTypeMap = new HashMap<Class<?>, String>();
for (int i = 0; i < retryableExceptionClasses.length; i++) {
Class<?> cls = retryableExceptionClasses[i];
for (Class<?> cls : retryableExceptionClasses) {
exceptionTypeMap.put(cls, "retry");
}
exceptionClassifier.setTypeMap(exceptionTypeMap);
@@ -243,11 +242,24 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
}
List<Class<?>> exceptions = new ArrayList<Class<?>>(Arrays.asList(skippableExceptionClasses));
ItemSkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions, Arrays
.asList(fatalExceptionClasses));
exceptions.addAll(Arrays.asList(retryableExceptionClasses));
ItemSkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions, Arrays
.asList(fatalExceptionClasses));
ItemSkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions,
new ArrayList<Class<?>>(){{
for (Class<?> exceptionClass : fatalExceptionClasses) {
add(exceptionClass);
}
}});
exceptions.addAll(
new ArrayList<Class<?>>(){{
for (Class<?> exceptionClass : retryableExceptionClasses) {
add(exceptionClass);
}
}});
ItemSkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions,
new ArrayList<Class<?>>(){{
for (Class<?> exceptionClass : fatalExceptionClasses) {
add(exceptionClass);
}
}});
StatefulRetryItemHandler<T> itemHandler = new StatefulRetryItemHandler<T>(getItemReader(), getItemWriter(),
retryTemplate, itemKeyGenerator, readSkipPolicy, writeSkipPolicy);
itemHandler.setSkipListeners(BatchListenerFactoryHelper.getSkipListeners(getListeners()));
@@ -263,11 +275,14 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
}
public void addFatalExceptionIfMissing(Class<?> cls) {
List<Class<?>> fatalExceptionList = new ArrayList<Class<?>>(Arrays.asList(fatalExceptionClasses));
List<Class<?>> fatalExceptionList = new ArrayList<Class<?>>();
for (Class<?> exceptionClass : fatalExceptionClasses) {
fatalExceptionList.add(exceptionClass);
}
if (!fatalExceptionList.contains(cls)) {
fatalExceptionList.add(cls);
}
fatalExceptionClasses = (Class[]) fatalExceptionList.toArray(new Class[0]);
fatalExceptionClasses = fatalExceptionList.toArray(new Class[0]);
}
/**
@@ -318,8 +333,8 @@ public class SkipLimitStepFactoryBean<T> extends SimpleStepFactoryBean<T> {
* @param listeners
*/
public void setSkipListeners(SkipListener[] listeners) {
for (int i = 0; i < listeners.length; i++) {
registerSkipListener(listeners[i]);
for (SkipListener listener1 : listeners) {
registerSkipListener(listener1);
}
}

View File

@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
@@ -69,7 +70,7 @@ public class LimitCheckingItemSkipPolicy implements ItemSkipPolicy {
private final int skipLimit;
private ExceptionClassifier exceptionClassifier;
private ExceptionClassifier<String> exceptionClassifier;
/**
* Convenience constructor that assumes all exception types are skippable
@@ -78,7 +79,9 @@ public class LimitCheckingItemSkipPolicy implements ItemSkipPolicy {
*/
@SuppressWarnings("unchecked")
public LimitCheckingItemSkipPolicy(int skipLimit) {
this(skipLimit, (List) Collections.singletonList(Exception.class), Collections.EMPTY_LIST);
this(skipLimit,
new ArrayList<Class<?>>(){{add(Exception.class);}},
Collections.EMPTY_LIST);
}
/**

View File

@@ -56,8 +56,8 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
protected final Log logger = LogFactory.getLog(LogOrRethrowExceptionHandler.class);
private ExceptionClassifier exceptionClassifier = new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
private ExceptionClassifier<String> exceptionClassifier = new ExceptionClassifierSupport() {
public String classify(Throwable throwable) {
return RETHROW;
}
};
@@ -66,9 +66,9 @@ public class LogOrRethrowExceptionHandler implements ExceptionHandler {
* Setter for the {@link ExceptionClassifier} used by this handler. The default is to map all throwable instances to
* {@link #RETHROW}.
*
* @param exceptionClassifier
* @param exceptionClassifier the ExceptionClassifier to use
*/
public void setExceptionClassifier(ExceptionClassifier exceptionClassifier) {
public void setExceptionClassifier(ExceptionClassifier<String> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}

View File

@@ -41,7 +41,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
protected final Log logger = LogFactory.getLog(RethrowOnThresholdExceptionHandler.class);
private ExceptionClassifier exceptionClassifier = new ExceptionClassifierSupport();
private ExceptionClassifier<String> exceptionClassifier = new ExceptionClassifierSupport();
private Map<Object, Integer> thresholds = new HashMap<Object, Integer>();
@@ -65,7 +65,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
*/
public RethrowOnThresholdExceptionHandler() {
super();
thresholds.put(ExceptionClassifierSupport.DEFAULT, new Integer(0));
thresholds.put(ExceptionClassifierSupport.DEFAULT, 0);
}
/**
@@ -81,7 +81,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
if (!(entry.getKey() instanceof String)) {
logger.warn("Key in thresholds map is not of type String: " + entry.getKey());
}
Assert.state(entry.getValue() instanceof Integer, "Threshold value must be of type Integer. "
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.");
}
this.thresholds = thresholds;
@@ -93,9 +93,9 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
* {@link ExceptionClassifierSupport#DEFAULT}, which are then mapped to a
* threshold of 0 by the {@link #setThresholds(Map)} map.
*
* @param exceptionClassifier
* @param exceptionClassifier ExceptionClassifier to use
*/
public void setExceptionClassifier(ExceptionClassifier exceptionClassifier) {
public void setExceptionClassifier(ExceptionClassifier<String> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
@@ -114,7 +114,7 @@ public class RethrowOnThresholdExceptionHandler implements ExceptionHandler {
counter.increment();
int count = counter.getCount();
Integer threshold = thresholds.get(key);
if (threshold == null || count > threshold.intValue()) {
if (threshold == null || count > threshold) {
throw throwable;
}

View File

@@ -67,6 +67,8 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
/**
* Convenience constructor for the {@link SimpleLimitExceptionHandler} to
* set the limit.
*
* @param limit the limit
*/
public SimpleLimitExceptionHandler(int limit) {
this();
@@ -79,14 +81,14 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
public SimpleLimitExceptionHandler() {
super();
delegate.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
for (int i = 0; i < fatalExceptionClasses.length; i++) {
if (fatalExceptionClasses[i].isAssignableFrom(throwable.getClass())) {
public String classify(Throwable throwable) {
for (Class<?> fatalExceptionClass : fatalExceptionClasses) {
if (fatalExceptionClass.isAssignableFrom(throwable.getClass())) {
return FATAL;
}
}
for (int i = 0; i < exceptionClasses.length; i++) {
if (exceptionClasses[i].isAssignableFrom(throwable.getClass())) {
for (Class<?> exceptionClass : exceptionClasses) {
if (exceptionClass.isAssignableFrom(throwable.getClass())) {
return TX_INVALID;
}
}
@@ -113,14 +115,14 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
* The limit on the given exception type within a single context before it
* is rethrown.
*
* @param limit
* @param limit the limit
*/
public void setLimit(final int limit) {
delegate.setThresholds(new HashMap<Object, Integer>() {
{
put(ExceptionClassifierSupport.DEFAULT, new Integer(0));
put(TX_INVALID, new Integer(limit));
put(FATAL, new Integer(0));
put(ExceptionClassifierSupport.DEFAULT, 0);
put(TX_INVALID, limit);
put(FATAL, 0);
}
});
}
@@ -130,6 +132,7 @@ public class SimpleLimitExceptionHandler implements ExceptionHandler {
* Defaults to {@link Exception}. If more exceptionClasses are specified
* handler uses single counter that is incremented when one of the
* recognized exception exceptionClasses is handled.
* @param classes exceptionClasses
*/
public void setExceptionClasses(Class<?>[] classes) {
this.exceptionClasses = classes;

View File

@@ -42,9 +42,9 @@ public class BinaryExceptionClassifier extends ExceptionClassifierSupport {
* @param exceptionClasses defaults to {@link Exception}.
*/
public final void setExceptionClasses(Class<?>[] exceptionClasses) {
Map<Class<?>, Object> temp = new HashMap<Class<?>, Object>();
for (int i = 0; i < exceptionClasses.length; i++) {
temp.put(exceptionClasses[i], NON_DEFAULT);
Map<Class<?>, String> temp = new HashMap<Class<?>, String>();
for (Class<?> exceptionClass : exceptionClasses) {
temp.put(exceptionClass, NON_DEFAULT);
}
this.delegate.setTypeMap(temp);
}
@@ -70,7 +70,7 @@ public class BinaryExceptionClassifier extends ExceptionClassifierSupport {
* @see #setExceptionClasses(Class[])
* @see ExceptionClassifierSupport#classify(Throwable)
*/
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return delegate.classify(throwable);
}

View File

@@ -22,7 +22,7 @@ package org.springframework.batch.support;
* @author Dave Syer
*
*/
public interface ExceptionClassifier {
public interface ExceptionClassifier<T> {
/**
* Get a default value, normally the same as would be returned by
@@ -30,7 +30,7 @@ public interface ExceptionClassifier {
*
* @return the default value.
*/
Object getDefault();
T getDefault();
/**
* Classify the given exception and return a non-null object. The return
@@ -40,6 +40,6 @@ public interface ExceptionClassifier {
* @param throwable the input exception. Can be null.
* @return an object.
*/
Object classify(Throwable throwable);
T classify(Throwable throwable);
}

View File

@@ -23,7 +23,7 @@ package org.springframework.batch.support;
* @author Dave Syer
*
*/
public class ExceptionClassifierSupport implements ExceptionClassifier {
public class ExceptionClassifierSupport implements ExceptionClassifier<String> {
/**
* Default classification key.
@@ -35,7 +35,7 @@ public class ExceptionClassifierSupport implements ExceptionClassifier {
*
* @see org.springframework.batch.support.ExceptionClassifier#classify(java.lang.Throwable)
*/
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return DEFAULT;
}
@@ -44,7 +44,7 @@ public class ExceptionClassifierSupport implements ExceptionClassifier {
*
* @see org.springframework.batch.support.ExceptionClassifier#getDefault()
*/
public Object getDefault() {
public String getDefault() {
return classify(null);
}

View File

@@ -17,7 +17,6 @@ package org.springframework.batch.support;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -29,10 +28,9 @@ import org.springframework.util.Assert;
* @author Dave Syer
*
*/
@SuppressWarnings("unchecked")
public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
private Map classified = new HashMap();
private Map<Class<?>, String> classified = new HashMap<Class<?>, String>();
/**
* Map of Throwable class types to keys for the classifier. Any subclass of
@@ -41,10 +39,9 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
*
* @param typeMap the typeMap to set
*/
public final void setTypeMap(Map typeMap) {
Map map = new HashMap();
for (Iterator iter = typeMap.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
public final void setTypeMap(Map<Class<?>, String> typeMap) {
Map<Class<?>, String> map = new HashMap<Class<?>, String>();
for (Map.Entry<Class<?>, String> entry : typeMap.entrySet()) {
addRetryableExceptionClass(entry.getKey(), entry.getValue(), map);
}
this.classified = map;
@@ -56,24 +53,23 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
*
* @see org.springframework.batch.support.ExceptionClassifierSupport#classify(java.lang.Throwable)
*/
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
if (throwable == null) {
return super.classify(throwable);
}
Class exceptionClass = throwable.getClass();
Class<?> exceptionClass = throwable.getClass();
if (classified.containsKey(exceptionClass)) {
return classified.get(exceptionClass);
}
// check for subclasses
Set classes = new TreeSet(new ClassComparator());
Set<Class<?>> classes = new TreeSet<Class<?>>(new ClassComparator());
classes.addAll(classified.keySet());
for (Iterator iterator = classes.iterator(); iterator.hasNext();) {
Class cls = (Class) iterator.next();
for (Class<?> cls : classes) {
if (cls.isAssignableFrom(exceptionClass)) {
Object value = classified.get(cls);
String value = classified.get(cls);
addRetryableExceptionClass(exceptionClass, value, this.classified);
return value;
}
@@ -82,9 +78,7 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
return super.classify(throwable);
}
private void addRetryableExceptionClass(Object candidateClass, Object classifiedAs, Map map) {
Assert.isAssignable(Class.class, candidateClass.getClass());
Class exceptionClass = (Class) candidateClass;
private void addRetryableExceptionClass(Class<?> exceptionClass, String classifiedAs, Map<Class<?>, String> map) {
Assert.isAssignable(Throwable.class, exceptionClass);
map.put(exceptionClass, classifiedAs);
}
@@ -95,15 +89,13 @@ public class SubclassExceptionClassifier extends ExceptionClassifierSupport {
* @author Dave Syer
*
*/
private class ClassComparator implements Comparator {
private class ClassComparator implements Comparator<Class<?>> {
/**
* @return 1 if arg0 is assignable from arg1, -1 otherwise
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object arg0, Object arg1) {
Class cls0 = (Class) arg0;
Class cls1 = (Class) arg1;
if (cls0.isAssignableFrom(cls1)) {
public int compare(Class<?> arg0, Class<?> arg1) {
if (arg0.isAssignableFrom(arg1)) {
return 1;
}
return -1;

View File

@@ -63,7 +63,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
public void testNotRethrownErrorLevel() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return LogOrRethrowExceptionHandler.ERROR;
}
});
@@ -74,7 +74,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
public void testNotRethrownWarnLevel() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return LogOrRethrowExceptionHandler.WARN;
}
});
@@ -85,7 +85,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
public void testNotRethrownDebugLevel() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return LogOrRethrowExceptionHandler.DEBUG;
}
});
@@ -96,7 +96,7 @@ public class LogOrRethrowExceptionHandlerTests extends TestCase {
public void testUnclassifiedException() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return "DEFAULT";
}
});

View File

@@ -51,7 +51,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
public void testNotRethrownWithThreshold() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return "RuntimeException";
}
});
@@ -65,7 +65,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
public void testRethrowOnThreshold() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return "RuntimeException";
}
});
@@ -84,7 +84,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
public void testNotUseParent() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return "RuntimeException";
}
});
@@ -103,7 +103,7 @@ public class RethrowOnThresholdExceptionHandlerTests extends TestCase {
public void testUseParent() throws Throwable {
handler.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
return "RuntimeException";
}
});

View File

@@ -80,7 +80,7 @@ public class ExceptionClassifierRetryPolicyTests extends TestCase {
assertTrue(policy.canRetry(context));
policy.setExceptionClassifier(new ExceptionClassifierSupport() {
public Object classify(Throwable throwable) {
public String classify(Throwable throwable) {
if (throwable != null) {
return "foo";
}

View File

@@ -16,11 +16,8 @@
package org.springframework.batch.support;
import java.util.Collections;
import java.util.LinkedHashMap;
import org.springframework.batch.support.SubclassExceptionClassifier;
import junit.framework.TestCase;
public class SubclassExceptionClassifierTests extends TestCase {
@@ -35,36 +32,24 @@ public class SubclassExceptionClassifierTests extends TestCase {
assertEquals(classifier.classify(new IllegalStateException("Foo")), classifier.getDefault());
}
public void testIllegalMapWithNonClass() {
try {
classifier.setTypeMap(Collections.singletonMap("bar", "foo"));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
public void testIllegalMapWithClass() {
try {
classifier.setTypeMap(Collections.singletonMap(String.class, "foo"));
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
}
public void testClassifyExactMatch() {
classifier.setTypeMap(Collections.singletonMap(IllegalStateException.class, "foo"));
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
put(IllegalStateException.class, "foo");
}});
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
public void testClassifySubclassMatch() {
classifier.setTypeMap(Collections.singletonMap(RuntimeException.class, "foo"));
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
put(RuntimeException.class, "foo");
}});
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
public void testClassifySuperclassDoesNotMatch() {
classifier.setTypeMap(Collections.singletonMap(IllegalStateException.class, "foo"));
classifier.setTypeMap(new LinkedHashMap<Class<?>, String>() {{
put(IllegalStateException.class, "foo");
}});
assertEquals(classifier.getDefault(), classifier.classify(new RuntimeException("Foo")));
}