Best way to check whether a certain exception type was the cause (of a cause, etc …) in a nested exception?

守給你的承諾、 提交于 2019-12-02 22:06:37

Why would you want to avoid getCause. You can, of course, write yourself a method to perform the task, something like:

public static boolean isCause(
    Class<? extends Throwable> expected,
    Throwable exc
) {
   return expected.isInstance(exc) || (
       exc != null && isCause(expected, exc.getCause())
   );
}
Patrick Boos

If you are using Apache Commons Lang, then you can use the following:

(1) When the cause should be exactly of the specified type

if (ExceptionUtils.indexOfThrowable(exception, ExpectedException.class) != -1) {
    // exception is or has a cause of type ExpectedException.class
}

(2) When the cause should be either of the specified type or its subclass type

if (ExceptionUtils.indexOfType(exception, ExpectedException.class) != -1) {
    // exception is or has a cause of type ExpectedException.class or its subclass
}

I don't think you have any choice but to call through the layers of getCause. If you look at the source code for the Spring NestedRuntimeException that you mention that is how it is implemented.

Imitation is the sincerest form of flattery. Based on a quick inspection of the source, this is exactly what NestedRuntimeException does:

/**
 * Check whether this exception contains an exception of the given type:
 * either it is of the given class itself or it contains a nested cause
 * of the given type.
 * @param exType the exception type to look for
 * @return whether there is a nested exception of the specified type
 */
public boolean contains(Class exType) {
    if (exType == null) {
        return false;
    }
    if (exType.isInstance(this)) {
        return true;
    }
    Throwable cause = getCause();
    if (cause == this) {
        return false;
    }
    if (cause instanceof NestedRuntimeException) {
        return ((NestedRuntimeException) cause).contains(exType);
    }
    else {
        while (cause != null) {
            if (exType.isInstance(cause)) {
                return true;
            }
            if (cause.getCause() == cause) {
                break;
            }
            cause = cause.getCause();
        }
        return false;
    }
}

CAVEAT: The above is the code as of 4 March 2009 so, if you really want to know what Spring is doing right now, you should research the code as it exists today (whenever that is).

Well, I think there's no way to do this without calling getCause(). It you think it's ugly implement a utility class for doing this:

public class ExceptionUtils {
     public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) {
         // call getCause() until it returns null or finds the exception
     }
}

You can do this using guava:

FluentIterable.from(Throwables.getCausalChain(e))
                        .filter(Predicates.instanceOf(ConstraintViolationException.class))
                        .first()
                        .isPresent();

Based on Patrick Boos answer: If you using Apache Commons Lang 3 you can check:

indexOfThrowable: Returns the (zero based) index of the first Throwable that matches the specified class (exactly) in the exception chain. Subclasses of the specified class do not match

if (ExceptionUtils.indexOfThrowable(e, clazz) != -1) {
    // your code
}

or

indexOfType: Returns the (zero based) index of the first Throwable that matches the specified class or subclass in the exception chain. Subclasses of the specified class do match

if (ExceptionUtils.indexOfType(e, clazz) != -1) {
    // your code
}

Example for multiple types with Java 8:

Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
            .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!