How to get correctly encoded stack trace for exceptions that occur when processing other exceptions?

Suppose I handle a FooException and a BarException . Suppose both of them are unchecked exceptions.

What I want to see in 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, the entire FooException entry will be deleted from stacktrace. For instance:

 // 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 constructor (message, cause) , then I can perform a rather crude process of "manual cloning" 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 will come to the following things:

 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 is of the wrong type and therefore may not be handled correctly by higher frames.

Is there a β€œbetter way” to handle this situation?

+5
source share
2 answers

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

 bar.initCause(foo); 
+4
source

As long as you pass the original exception from the new as an argument, it creates a chain called, and the stack trace is preserved. Your use cases seem a little strange. For me, an exception when repairing or processing an error with more than some logging is another error and is not "caused" by another error. I would just run foo and throw out the bar.

In some cases, I assume your path might make sense. To do this, you can pass "foo" as doSomethingElse (foo) and throw a new BarException (foo) when something goes wrong to handle it. Almost all standard exceptions support this constructor, and if you need to make your own, just create a constructor that delegates them.

I personally will not use them as you think. I use them to throw a different type of exception than the one I caught, if for some reason I need a different type. For example, to translate exceptions from a certain type to my application or to translate a checkmark checked to unchecked in cases where it makes sense. In such cases, it is useful to keep the original exception and the full "called .." trace.

+1
source

All Articles