Reputation: 6079
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?
Upvotes: 5
Views: 1038
Reputation: 1215
As long as you pass the original exception to the new one as an argument, it creates the "caused by" chain and the stack trace is preserved. Your use cases seem a bit strange. To me, the exception when recovering or handling the error with more than some logging is another error and not really "caused by" the other error. I would just log the "foo" and throw the "bar".
In some cases I guess your way could make sense. To do this, you could pass the "foo" on as doSomethingElse(foo) and throw new BarException(foo) when something goes wrong handling this. Pretty much all standard exceptions support this constructor, and if you need to make your own just create a constructor that delegates to these.
I personally would not use them the way you seem to though. I use them to throw a different type of exception than the one I caught, if I want a different type for some reason. For example, to translate to exceptions of specific type to my application, or to translate checked to unchecked in cases where that makes sense. In such cases it is still good to keep the original exception and the full "caused by.." trace..
Upvotes: 1
Reputation: 48837
One solution is to use the Throwable#initCause(Throwable)
method:
bar.initCause(foo);
Upvotes: 4