Files
spring-retry/src/main/java/org/springframework/classify/BinaryExceptionClassifier.java
Gary Russell aad050ba47 AMQP-334 Fix Nested Cause Classification
Consider Foo caused by Bar caused by Baz.

If Bar is categorized TRUE and Baz categorized FALSE, classiy() should
return TRUE (hit on Bar), but it returned FALSE.

The early exit from the cause traversal was not taken because we
were always testing against the top level throwable (Bar).

Add a test to verify this scenario; test against the cause on
each iteration through the loop.
2013-10-02 11:21:22 -04:00

126 lines
3.7 KiB
Java

/*
* Copyright 2006-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.classify;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* A {@link Classifier} for exceptions that has only two classes (true and
* false). Classifies objects according to their inheritance relation with the
* supplied types. If the object to be classified is one of the provided types,
* or is a subclass of one of the types, then the non-default value is returned
* (usually true).
*
* @see SubclassClassifier
*
* @author Dave Syer
* @author Gary Russell
*
*/
public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boolean> {
private boolean traverseCauses;
/**
* Create a binary exception classifier with the provided default value.
*
* @param defaultValue defaults to false
*/
public BinaryExceptionClassifier(boolean defaultValue) {
super(defaultValue);
}
/**
* Create a binary exception classifier with the provided classes and their
* subclasses. The mapped value for these exceptions will be the one
* provided (which will be the opposite of the default).
*
* @param value
*/
public BinaryExceptionClassifier(Collection<Class<? extends Throwable>> exceptionClasses, boolean value) {
this(!value);
if (exceptionClasses != null) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> type : exceptionClasses) {
map.put(type, !getDefault());
}
setTypeMap(map);
}
}
/**
* Create a binary exception classifier with the default value false and
* value mapping true for the provided classes and their subclasses.
*/
public BinaryExceptionClassifier(Collection<Class<? extends Throwable>> exceptionClasses) {
this(exceptionClasses, true);
}
/**
* Create a binary exception classifier using the given classification map
* and a default classification of false.
*
* @param typeMap
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap) {
this(typeMap, false);
}
/**
* Create a binary exception classifier using the given classification map
* and a default classification of false.
*
* @param typeMap
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap, boolean defaultValue) {
super(typeMap, defaultValue);
}
public void setTraverseCauses(boolean traverseCauses) {
this.traverseCauses = traverseCauses;
}
@Override
public Boolean classify(Throwable classifiable) {
Boolean classified = super.classify(classifiable);
if (!this.traverseCauses) {
return classified;
}
/*
* If the result is the default, we need to find out if it was by default
* or so configured; if default, try the cause(es).
*/
if (classified.equals(this.getDefault())) {
Throwable cause = classifiable;
do {
if (this.getClassified().containsKey(cause.getClass())) {
return classified; // non-default classification
}
cause = cause.getCause();
classified = super.classify(cause);
}
while (cause != null && classified.equals(this.getDefault()));
}
return classified;
}
}