| :index-group: EJB | |
| :jbake-type: page | |
| :jbake-status: status=published | |
| = @Asynchronous @PostConstruct | |
| Placing `@Asynchronous` on the `@PostConstruct` of an EJB is not a | |
| supported part of Java EE, but this example shows a pattern which works | |
| just as well with little effort. | |
| The heart of this pattern is to: | |
| * pass the construction ``logic`` to an `@Asynchronous` method via a | |
| `java.util.concurrent.Callable` | |
| * ensure the bean does not process invocations till construction is | |
| complete via an `@AroundInvoke` method on the bean and the | |
| `java.util.concurrent.Future` | |
| Simple and effective. The result is a faster starting application that | |
| is still thread-safe. | |
| [source,java] | |
| ---- | |
| package org.superbiz.asyncpost; | |
| import jakarta.annotation.PostConstruct; | |
| import jakarta.ejb.EJB; | |
| import jakarta.ejb.Lock; | |
| import jakarta.ejb.LockType; | |
| import jakarta.ejb.Singleton; | |
| import jakarta.interceptor.AroundInvoke; | |
| import jakarta.interceptor.InvocationContext; | |
| import java.util.concurrent.Callable; | |
| import java.util.concurrent.Future; | |
| import static java.util.concurrent.TimeUnit.SECONDS; | |
| @Singleton | |
| @Lock(LockType.READ) | |
| public class SlowStarter { | |
| @EJB | |
| private Executor executor; | |
| private Future construct; | |
| private String color; | |
| private String shape; | |
| @PostConstruct | |
| private void construct() throws Exception { | |
| construct = executor.submit(new Callable() { | |
| @Override | |
| public Object call() throws Exception { | |
| Thread.sleep(SECONDS.toMillis(10)); | |
| SlowStarter.this.color = "orange"; | |
| SlowStarter.this.shape = "circle"; | |
| return null; | |
| } | |
| }); | |
| } | |
| @AroundInvoke | |
| private Object guaranteeConstructionComplete(InvocationContext context) throws Exception { | |
| construct.get(); | |
| return context.proceed(); | |
| } | |
| public String getColor() { | |
| return color; | |
| } | |
| public String getShape() { | |
| return shape; | |
| } | |
| } | |
| ---- | |
| The `Executor` is a simple pattern, useful for many things, which | |
| exposes an interface functionally equivalent to | |
| `java.util.concurrent.ExecutorService`, but with the underlying thread | |
| pool controlled by the container. | |
| [source,java] | |
| ---- | |
| package org.superbiz.asyncpost; | |
| import jakarta.ejb.AsyncResult; | |
| import jakarta.ejb.Asynchronous; | |
| import jakarta.ejb.Lock; | |
| import jakarta.ejb.LockType; | |
| import jakarta.ejb.Singleton; | |
| import java.util.concurrent.Callable; | |
| import java.util.concurrent.Future; | |
| @Singleton | |
| @Lock(LockType.READ) | |
| public class Executor { | |
| @Asynchronous | |
| public <T> Future<T> submit(Callable<T> task) throws Exception { | |
| return new AsyncResult<T>(task.call()); | |
| } | |
| } | |
| ---- | |
| Finally a test case shows the usefulness of `@AroundInvoke` call in our | |
| bean that calls `construct.get()` | |
| [source,java] | |
| ---- | |
| package org.superbiz.asyncpost; | |
| import junit.framework.Assert; | |
| import org.junit.Test; | |
| import jakarta.ejb.EJB; | |
| import jakarta.ejb.embeddable.EJBContainer; | |
| public class SlowStarterTest { | |
| @EJB | |
| private SlowStarter slowStarter; | |
| @Test | |
| public void test() throws Exception { | |
| // Start the Container | |
| EJBContainer.createEJBContainer().getContext().bind("inject", this); | |
| // Immediately access the fields initialized in the PostConstruct | |
| // This will fail without the @AroundInvoke call to construct.get() | |
| Assert.assertEquals("orange", slowStarter.getColor()); | |
| Assert.assertEquals("circle", slowStarter.getShape()); | |
| } | |
| } | |
| ---- |