Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a correctly-chained stack trace for exceptions thrown when handling other exceptions?

Suppose I am handling FooException and BarException occurs. Let's assume they are both unchecked exceptions.

What I want to see in the stacktrace is:

com.bar.BarException: Bar Message
    at com.baz.BazCode(BazCode.java:123)
    ...
Caused by: com.foo.FooException: Foo Message
    at com.baz.BazCode(BazCode.java:321)
    ....
Caused by: ...

However, by default, all record of FooException will be erased from the stacktrace. For example:

// In a class written by me
/**
  * ...
  * @throws FooException if foo happens
  * @throws BarException if bar happens
  */
public void upperFrame() {
    try {
        foo.doSomething();
    } catch (FooException foo) {
        bar.doSomethingElse();
    }
}

// In class Bar (not written by me)
public void doSomethingElse() {
    if (someConditionWhichHappensToBeTrueInThisScenario()) {
        throw new BarException("Hello Bar World"); // At this point, FooException gets erased from the stack trace
    }
}

If BarException has a (message, cause) constructor then I can follow a rather crude kind of "manual cloning" process to achieve my goal:

try {
    foo.doSomething();
} catch (FooException foo) {
    try {
        bar.doSomethingElse();
    } catch (BarException bar) {
        BarException bar2 = new BarException(bar.getMessage(), foo);
        bar2.setStackTrace(bar.getStackTrace());
        throw bar2;
    }
}

However, if BarException does not have such a constructor (for example ClassCastException) then I am reduced to doing things like this:

try {
    foo.doSomething();
} catch (FooException foo) {
    try {
        bar.doSomethingElse();
    } catch (BarException bar) {
        RuntimeException e = new RuntimeException("com.bar.BarException: " + bar.getMessage(), foo);
        e.setStackTrace(bar.getStackTrace());
        throw e;
    }
}

This is dangerous because e has the wrong type and so may not be correctly handled by higher frames.

Is there a "best-practice" way to handle this situation?

like image 530
Adam Burley Avatar asked May 27 '15 09:05

Adam Burley


1 Answers

One solution is to use the Throwable#initCause(Throwable) method:

bar.initCause(foo);
like image 79
sp00m Avatar answered Oct 18 '22 09:10

sp00m