tags:

views:

234

answers:

3

(Late edit: This question will hopefully be obsolete when Java 7 comes, because of the "final rethrow" feature which seems like it will be added.)


Quite often, I find myself in situations looking like this:

    do some initialization
    try {
        do some work 
    } catch any exception {
        undo initialization
        rethrow exception
    }

In C# you can do it like this:

InitializeStuff();
try
{
    DoSomeWork();
}
catch 
{
    UndoInitialize();
    throw;
}

For Java, there's no good substitution, and since the proposal for improved exception handling was cut from Java 7, it looks like it'll take at best several years until we get something like it. Thus, I decided to roll my own:

(Edit: Half a year later, final rethrow is back, or so it seems.)

public final class Rethrow {

    private Rethrow() { throw new AssertionError("uninstantiable"); }

    /** Rethrows t if it is an unchecked exception. */
    public static void unchecked(Throwable t) {
        if (t instanceof Error)
            throw (Error) t;
        if (t instanceof RuntimeException)
            throw (RuntimeException) t;
    }

    /** Rethrows t if it is an unchecked exception or an instance of E. */
    public static <E extends Exception> void instanceOrUnchecked(
            Class<E> exceptionClass, Throwable t) throws E, Error,
            RuntimeException {
        Rethrow.unchecked(t);
        if (exceptionClass.isInstance(t))
            throw exceptionClass.cast(t);
    }

}

Typical usage:

public void doStuff() throws SomeException {
    initializeStuff();
    try {
        doSomeWork();
    } catch (Throwable t) {
        undoInitialize();
        Rethrow.instanceOrUnchecked(SomeException.class, t);
        // We shouldn't get past the above line as only unchecked or 
        // SomeException exceptions are thrown in the try block, but
        // we don't want to risk swallowing an error, so:
        throw new SomeException("Unexpected exception", t); 
    }
    private void doSomeWork() throws SomeException { ... }
}

It's a bit wordy, catching Throwable is usually frowned upon, I'm not really happy at using reflection just to rethrow an exception, and I always feel a bit uneasy writing "this will not happen" comments, but in practice it works well (or seems to, at least). What I wonder is:

  1. Do I have any flaws in my rethrow helper methods? Some corner cases I've missed? (I know that the Throwable may have been caused by something so severe that my undoInitialize will fail, but that's OK.)
    • Has someone already invented this? I looked at Commons Lang's ExceptionUtils but that does other things.

Edit:

  • finally is not the droid I'm looking for. I'm only interested to do stuff when an exception is thrown.
  • Yes, I know catching Throwable is a big no-no, but I think it's the lesser evil here compared to having three catch clauses (for Error, RuntimeException and SomeException, respectively) with identical code.
  • Note that I'm not trying to suppress any errors - the idea is that any exceptions thrown in the try block will continue to bubble up through the call stack as soon as I've rewinded a few things.
+1  A: 

If you are that concerned about getting your uninitialization to happen then you may want to just put that code into a finally block, as, if it should be called at some point, then you perhaps should always clean up.

I am leery of catching Throwable as some of the exceptions I want to handle, and some I just log, as, there is no use passing exceptions that the user can't do anything about, such as a NullPointerException.

But, you didn't show what SomeException is defined as, but if an OutOfMemoryException is thrown, your throwable will catch it, but it may not be the same type as SomeException so your wrapper will be needed in your sample function, at least when I look at the instanceOrUnchecked method.

You may want to write a unit test, try different classes of Exceptions and see what does or doesn't work as expected, so you can document the expected behavior.

James Black
`SomeException extends Exception`. As for uninitializing in `finally` - I only want the uninitialization to happen if an exception is thrown. I could of course have a boolean flag initially set to false and then set it to true as the last thing in the `try` block, but that's a bit cumbersome (and what if someone adds code after it is set to true without thinking about the exception handling?).
gustafc
Also, I have tested it, and it works very well... under all circumstances I've been able to think of, which only proves that I've managed to write code I'm not smart enough to break myself =)
gustafc
Have you tested with a class that extends Throwable, not one that extends Exception?
James Black
@JB, I haven't. Clever! I should probably change the type constraint on `instanceOrUnchecked` from `extends Exception` to `extends Throwable` to allow clients to rethrow such abominations.
gustafc
@gustafc - Just looking for cases you probably didn't expect. This one is an issue for people that wrap their calls in catch(Exception) as the more critical errors are Throwable not Exception.
James Black
@gustafc, instead of checking a boolean flag, try checking the reference to the object you were trying to create. If it's null then an exception was probably caught. And if someone adds code without updating the check, they will either have a valid object or a NullPointerException
finnw
+1  A: 

An alternative is to have a factory which creates SomeException only if the cause is a checked exception:

   public static SomeException throwException(String message, Throwable cause) throws SomeException {
      unchecked(cause); //calls the method you defined in the question.
      throw new SomeException(message, cause);
   }

The reason why I put in the return value in the method is so that the client can do something like this:

     catch (Throwable e) {
         undoInitialize();
         throw SomeException.throwException("message", e);
     }

so that the compiler is fooled into not requiring a return after the catch statement if the method has a return type, but it still throws the exception if the client forgot to put the throw before the call to the factory method.

The disadvantage of this over your code is that it is less portable (it works for SomeException, but not for SomeOtherException), but that may be ok, because it won't be for every exception type that you need to have an undo initialize.

If it fits your use case you could put the unchecked call in the constructor of SomeException and have the logic available to all subclasses, but that would have to fit your specific project - it would not be a good idea in the general case as it would prevent wrapping runtime exceptions.

      public SomeException(message, cause) {
            super(message, unchecked(cause));
      }

      private static Throwable unchecked(Throwable cause) {
          if (cause instanceof Error) throw (Error) cause;
          if (cause instanceof RuntimeException) throw (RuntimeException) cause;
          return cause;
      }
Yishai
The factory function is nice, but since I have an exception hierarchy (several classes extending `SomeException`), I need something more flexible. I like the cleverness of the second alternative, but I think maybe it's too clever - the WTF potential for maintenance programmers seems high.
gustafc
+2  A: 

There are a couple of way to handle this. The first is my preference if you don't need to know what the exception was.

boolean okay = false;
try {
  // do some work which might throw an exception
  okay = true;
} finally {
  if (!okay) // do some clean up.
}

In some cases you can do the same without an extra variable, depending on what the try block does.

A second option is a hack but also works.

try {
    // do some work which might throw an exception
} catch (Throwable t) {
    // do something with t.
    Thread.currentThread().stop(t);
}

The stop(Throwable t) method doesn't stop the thread, instead it causes the thread to throw the exception provided in an unchecked way.

You can use Unsafe.throwException() with a bit of fiddling and there is a way to do this with Generics which I have forgotten.

Peter Lawrey