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