Sunday 29 December 2013

How to use @Asynchronous in JEE

Since Java Enterprise Edition (JEE) 6 and EJB 3.1 (JSR 318) we can use the @Asynchronous annotation to call a method asynchronously.

The following example contains a Stateless Session Bean with three asynchronous methods, each one of them running for a different amount of time (1, 3 and 5 seconds) and returning a simple String value.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import java.util.concurrent.Future;

@Stateless
public class AsynchronousBean {

    private static final Logger LOG = LoggerFactory.getLogger(AsynchronousBean.class);

    @Asynchronous
    public Future<String> workFor5Seconds() throws Exception {
        LOG.info("==> workFor5Seconds");
        Thread.sleep(5000);
        LOG.info("<== workFor5Seconds");
        return new AsyncResult<String>("test1");
    }

    @Asynchronous
    public Future<String> workFor3Seconds() throws Exception {
        LOG.info("==> workFor3Seconds");
        Thread.sleep(3000);
        LOG.info("<== workFor3Seconds");
        return new AsyncResult<String>("test2");
    }

    @Asynchronous
    public Future<String> workFor1Second() throws Exception {
        LOG.info("==> workFor1Second");
        Thread.sleep(1000);
        LOG.info("<== workFor1Second");
        return new AsyncResult<String>("test3");
    }
}

We can test the AsynchronousBean with a small unit test using an EJBContainer to set up the environment. We call all three asynchronous methods in a row and use the Future type for the return result.

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class AsynchronousTest {

    private static final Logger LOG = LoggerFactory.getLogger(AsynchronousTest.class);

    public static EJBContainer container;

    AsynchronousBean asynchronousBean;

    @Before
    public void setUp() throws Exception {
        container = EJBContainer.createEJBContainer();
        container.getContext().bind("inject", this); 
        asynchronousBean = (AsynchronousBean) container.getContext().lookup("java:global/[module-name]/" + AsynchronousBean.class.getSimpleName());
    }

    @Test
    public void asynchronousTest() throws Exception {

        final Future<String> future1 = asynchronousBean.workFor5Seconds();
        final Future<String> future2 = asynchronousBean.workFor3Seconds();
        final Future<String> future3 = asynchronousBean.workFor1Second();

        LOG.info("Do something else..");
        Thread.sleep(1000);

        final String result1 = future1.get();
        final String result2 = future2.get();
        final String result3 = future3.get();

        LOG.info("Result from workFor5Seconds is: {}", result1);
        LOG.info("Result from workFor3Seconds is: {}", result2);
        LOG.info("Result from workFor1Second is: {}", result3);
    }

    @AfterClass
    public static void destroy() throws Exception {
        if(container != null) {
            container.close();
        }
    }
}

If we analyze the output of the logging we see characteristics of @Asynchronous. In a sequential processing we would be stuck in the first method waiting for 5 seconds to return and then call the next one. Using asynchronous calls we can enter all methods concurrently and get the final result if it is ready.

[INFO ] Do something else..
[INFO ] ==> workFor5Second
[INFO ] ==> workFor3Seconds
[INFO ] ==> workFor1Seconds
[INFO ] <== workFor1Second
[INFO ] <== workFor3Seconds
[INFO ] <== workFor5Seconds
[INFO ] Result from workFor5Seconds is: test1
[INFO ] Result from workFor3Seconds is: test2
[INFO ] Result from workFor1Second is: test3

See also