RAP Project Lead at EclipseSource
To catch or not to catch... Throwable
August 8, 2012 | 2 min ReadMany 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 ofThrowable
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.