Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How safe is my safe rethrow?

Tags:

java

exception

(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.
like image 464
gustafc Avatar asked Oct 30 '09 16:10

gustafc


People also ask

Should I Rethrow exceptions?

Catch and rethrow exception Medium Catching and re-throwing an exception without further actions is redundant and wasteful. Instead, it is recommended to re-throw custom exception type and/or log trace for debugging.

What is Rethrow in exception handling?

Re-throwing an exception means calling the throw statement without an exception object, inside a catch block. It can only be used inside a catch block.


3 Answers

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.

like image 143
Peter Lawrey Avatar answered Oct 05 '22 23:10

Peter Lawrey


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.

like image 37
James Black Avatar answered Oct 06 '22 00:10

James Black


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;
      }
like image 28
Yishai Avatar answered Oct 05 '22 23:10

Yishai