Cycles in chained exceptions
I'll first quickly motivate the question with my use case. My library needs to expose a Java exception classifier to a framework which it plugs in to. For example:
enum Classification { FATAL, TRANSIENT, UNKNOWN }
Classification classify(Throwable t) {
if (t instanceof MyTransientException)
return Classification.TRANSIENT;
else if (t instanceof MyFatalException)
return Classification.FATAL;
else
return Classification.UNKNOWN;
}
Sometimes, and for reasons out of my control, the passed exception is a wrapper around the one I'm interested in so I want to search the cause chain for it. My initial idea was:
Classification classify(Throwable t) {
if (t == null)
return Classification.UNKNOWN;
if (t instanceof MyTransientException)
return Classification.TRANSIENT;
else if (t instanceof MyFatalException)
return Classification.FATAL;
else
return classify(t.getCause());
}
Unfortunately, this could result in infinite recursion if the passed exception has a cycle in its causa开发者_JS百科l chain. It's highly unlikely that such an exception will be passed, and an argument could be made that it's an error elsewhere in the system if such an exception is created, but I'm very uncomfortable with having the possibility of my library being responsible for a production outage if it happens. Throwable's API and javadoc do not explicitly forbid this possibility beyond the idea that cycles are inherently nonsensical in a causal chain.
I noticed that Guava has a @Beta method to extract the causal chain, Throwables.getCausalChain, but its implementation is susceptible to the same issue -- it will wind up throwing an OOME.
I'm planning to use an identity hash set to detect the cycle and mitigate the risk, but I wanted to hear how others view this issue. Do you think I'm being overly defensive? What would you do?
It's great that you are being so conscientious. But you don't want to get into the business of manufacturing kevlar boots.
If a user does something that would make even Throwable.printStackTrace
go into infinite recursion, that user is beyond help. Don't even worry about this.
I think it is on the defensive side to do in your own code, but because these days we use many libraries, you never know which will actually do something as unexpected as create a causechain loop.
Apache Commons does handle this in it's ExceptionUtils
. For instance in ExceptionUtils.getThrowableList
. It does this in a fairly simple way:
public static List<Throwable> getThrowableList(Throwable throwable) {
final List<Throwable> list = new ArrayList<>();
while (throwable != null && !list.contains(throwable)) {
list.add(throwable);
throwable = throwable.getCause();
}
return list;
}
By using helper libraries instead of building your own you can prevent mistakes on things you would never think of yourself.
精彩评论