views:

62

answers:

2

I was not able to find any advice on catch blocks in Java that involve some cleanup operations which themselves could throw exceptions.

The classic example is that of stream.close() which we usually call in the finally clause and if that throws an exception, we either ignore it by calling it in a try-catch block or declare it to be rethrown.

But in general, how do I handle cases like:

public void doIt() throws ApiException { //ApiException is my "higher level" exception
  try {
    doLower();
  } catch(Exception le) {
    doCleanup(); //this throws exception too which I can't communicate to caller
    throw new ApiException(le);
  }
}

I could do:

catch(Exception le) {
  try {
    doCleanup();
  } catch(Exception le1) {
  //ignore?
  //log?
  }
  throw new ApiException(le); //I must throw le
}

But that means I will have to do some log analysis to understand why cleanup failed.

If I did:

catch(Exception le) {
  try {
    doCleanup();
  } catch(Exception le1) {
    throw new ApiException(le1);
  }

It results in losing the le that got me here in the catch block in the fist place.

What are some of the idioms people use here?

  • Declare the lower level exceptions in throws clause?
  • Ignore the exceptions during cleanup operation?
A: 

Use a finally block. If you are worried about swallowing the stack trace do the following:

Exception ex = new Exception(le.stackTrace());
Woot4Moo
I am not sure. Firstly, there are two sources of exceptions and hence I am not sure where to place finally and secondly, I guess you meant setStackTrace() method on Throwable. We don't have Exception constructor that accepts a stack trace.
Kedar Mhaswade
+3  A: 

First, decide if you actually need to throw from the finally block - think carefully on that - in the case of a failed close() call... well, logging it is fine - but what can a higher layer of your API really do about the problem? So for 99% of the cases, you will log the secondary then re-throw the primary.

Next, if you do need to throw the secondary, decide if the various causes of the secondary exception are important. It will be rare that they are. So set the cause of the secondary to be the primary (either using an appropriate constructor, or initCause() ).

Finally, if you've must throw the secondary, and retain the full stack trace of the secondary and the primary - then you are in to creating a custom Exception to handle the situation. It's ugly, because you will probably want to derive from different parent classes. If this does arise, I suggest creating a helper class that is capable of filling the stack trace of a target exception in such a way that it produces a meaningful trace based on both exceptions (Be sure to use indentation for the secondary so nested exceptions are easy to pull apart).

But mostly, I suggest that you use the log-and-rethrow-primary paradigm. Focus on fixing the primary, and the secondary issues generally take care of themselves (the classic example here is an IO exception that munges things up so badly that the call to close() can't succeed either).

Kevin Day