To catch or not to catch… Throwable

To catch or not to catch… Throwable

Many developers still catch Throwable in their try/catch statements. Is this a good idea? I don’t think so. As all of you know, Throwable is a generic superclass for all exception and errors in Java. As exceptions are meant to be caught, errors in most cases are not. If we take a look at the Error class JavaDoc we will read the following:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.

Catching some fatal VM errors like OutOfMemoryError or StackOverflowError is a nonsense. Оn the other hand, if you load a native libraries, you have to deal with UnsatisfiedLinkError error. That’s way it’s better to catch the exact error you need instead of a generic Throwable object.
In the RAP world, when you run an application in SWT_COMPATIBILITY operational mode, there is one error which must be propagated in any case. This is the ThreadDeath error. Catching ThreadDeath will lead to the situation where it is impossible to restart the RAP session. Sometimes this is easy to see when the Throwable is caught, but sometimes it’s well hidden.
Two years ago, Benjamin Muskalla wrote a great blog post about Equinox Security integration in RAP. Recently, a bug report states that when you try to restart RAP sessions (press F5 in a browser) with the login dialog opened, the application freezes. My first reaction was that this is another Throwable catch in the user code that prevents the propagation of the ThreadDeath error. Unfortunately, I was wrong. The javax.security.auth.login.LoginContext class, responsible for authentication, uses reflection to execute the actual login method. When ThreadDeath error occurs inside your login method, it’s transformed into a InvocationTargetException exception and the RAP session doesn’t restart. The solution is to manually catch the ThreadDeath error in your login module, wrap it into a LoginException

try {
  callbackHandler.handle( new Callback[] { label, nameCallback, passwordCallback } );
} catch( ThreadDeath death ) {
  LoginException loginException = new LoginException();
  loginException.initCause( death );
  throw loginException;
} catch( Exception exception ) {
  ...
}

and re-throw it afterwards from your application code:

try {
  secureContext.login();
} catch( LoginException exception ) {
  Throwable cause = exception.getCause();
  if( cause != null && cause.getCause() instanceof ThreadDeath ) {
    throw ( ThreadDeath )cause.getCause();
  }
  ...
}

Kudos for this workaround goes to Daniele Pirola. Demo bundles have been updated in „RAP/Equinox Security Integration“ WiKi page.