RuntimeExceptions within @Asynchronous methods

Since the introduction of EJB 3.1 we can use the @Asynchronous annotation. It provides a simple way to create a new asynchronous process in your application.

If a method with the @Asynchronous is invoked it will spawn a new thread and return immediately. The invocation can return either a void or a [Future][Future_T]. When returning a **Future** it starts out completely empty, but the @Asynchronous can return something called **AsyncResult** to fill the **Future** with a value. The original method can, at a later moment in time, call .get() or .get(long timeout, TimeUnit unit) to see the result of the Future. If needed it will stop and wait for the result. But in the mean time it could have executed some other pieces of code in parallel.

To find out more about how to use @Asynchronous and Future, read this page from TomEE.

The problem I’ve found with this annotation is how it handles RuntimeException. If you have a void as return value, and inside the asynchronous method a RuntimeException occurs, it’ll be completely swallowed. Nothing will be send to your logs. This is something I couldn’t find in any documentation.

If you create a new Thread yourself (see example below) it will print the RuntimeException:

public static void main(final String[] args) {
    		final Runnable t = new Runnable() {
    			@Override
    			public void run() {
    				throw new RuntimeException("Log me!!");
    			}
    		};
    		new Thread(t).start();
    	}

When executed you’ll end up with the exception nicely printed in the console. But this isn’t the case for @Asynchronous. For example if you do:

@Asynchronous
public void test() {
    		throw new RuntimeException("Where am I?");
}

This exception is swallowed, never to be seen again. Because this wasn’t what I expected it took me much longer then needed to find the actual bug!

To fix this problem you can instead return a Future and call get(). When calling get() on a Future, and the asynchronous method ends with an exception, it’ll immediately throw an ExecutionException which wraps around the asynchronous exception (call getCause() to get it).

But calling get() basically makes the call synchronous again instead of the fire-and-forget I wanted to have. So instead I’ve now ended up with a big try/catch block around the entire @Asynchronous method. It feels a bit wrong… did I miss something? Is there a better method to log the RuntimeException from @Asynchronous methods?