Enterprise Integration Zone is brought to you in partnership with:

Jethro has posted 2 posts at DZone. View Full User Profile

Spring @Async and exception handling

01.30.2014
| 9716 views |
  • submit to reddit

Introduction

When using Spring to asynchronously execute pieces of your code you typically use the @Async annotation on your Spring @Components methods. When using the @Async annotation Spring will use AOP (Aspect Oriented Programming) to wrap your call in a Runnable. This Runnable will then be scheduled for execution on a TaskExecutor. More information about asynchronous execution and its transactional context can be found here.

Exception Handling

Spring comes with a number of pre-packaged TaskExecutors which are documented in the Spring documentation here. In practice you will likely use the ThreadPoolTaskExecutor or the SimpleAsyncTaskExecutor. When doing so you might at times wonder why a task has not been executed. This happens when an exception occurs inside the method you are trying to execute asynchronous. The aforementioned task executors do not handle the exceptions so the execution fails silently. This problem can be solved by implementing your own AsyncTaskExecutor which handles the exceptions by logging them (or in any other way you wish). Bellow you'll find an example implementation of such a AsyncTaskExecutor.

package nl.jborsje.blog.examples;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {
    private AsyncTaskExecutor executor;

    public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
        this.executor = executor;
    }

    public void execute(Runnable task) {
        executor.execute(createWrappedRunnable(task));
    }

    public void execute(Runnable task, long startTimeout) {
        executor.execute(createWrappedRunnable(task), startTimeout);
    }

    public Future submit(Runnable task) {
        return executor.submit(createWrappedRunnable(task));
    }

    public  Future submit(final Callable task) {
        return executor.submit(createCallable(task));
    }

    private  Callable createCallable(final Callable task) {
        return new Callable() {
            public T call() throws Exception {
                try {
                    return task.call();
                } catch (Exception ex) {
                    handle(ex);
                    throw ex;
                }
            }
        };
    }

    private Runnable createWrappedRunnable(final Runnable task) {
        return new Runnable() {
            public void run() {
                try {
                    task.run();
                } catch (Exception ex) {
                    handle(ex);
                }
            }
        };
    }

    private void handle(Exception ex) {
        System.err.println("Error during @Async execution: " + ex);
    }
}

This task executor decorates another task executor and wraps all Runnables and Callables in versions which handle the exception. In order to be able to use this task executor it has to be configured in the Spring application context as a managed bean and it has to be passed the real executor (the one that does the actual work) in its constructor. An example of such a configuration can be found bellow.

<task:annotation-driven executor="exceptionHandlingTaskExecutor" scheduler="defaultTaskScheduler" />
<bean id="exceptionHandlingTaskExecutor" class="nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor">
    <constructor-arg ref="defaultTaskExecutor" />
</bean>
<task:executor id="defaultTaskExecutor" pool-size="5" />
<task:scheduler id="defaultTaskScheduler" pool-size="1" />

JUnit testing

In the application context used for JUnit testing Spring beans the asynchronous execution of methods can be cumbersome. You typically want to validate the inner workings of your methods not Springs asynchronicity. To achieve this you can use the SyncTaskExecutor provided by Spring. This task executor runs the tasks it is given immediately on the thread that adds the task instead of using a thread pool and running the tasks asynchronous. This task executor is configured in the test scope application context as follows.

<task:annotation-driven executor="synchronousTaskExecutor" scheduler="defaultTaskScheduler" />
<bean id="synchronousTaskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
<task:scheduler id="defaultTaskScheduler" pool-size="1" />
Published at DZone with permission of its author, Jethro Borsje.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)