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