With the introduction of asynchronous methods in EJB 3.1, the clients can now call session bean methods asynchronously using the no-interface, remote or local business views. Prior to EJB 3.1, JMS or Message Driven Beans(MDBs) were used for performing asynchronous tasks. Now with the inclusion of asynchronous methods in session beans in EJB 3.1, the MDB need not be used unless there is really a need for point-to-point or publish-subscribe messaging.
This feature of EJB 3.1 will have a significant impact on the design of enterprise applications in future. The existing enterprise applications may even get refactored to eliminate the MDB and introduce the asynchronous methods in the session beans. This will not only make the design cleaner but also reduce or eliminate the effort for maintainance of messaging solutions.
However, asynchronous methods are not supported by EJB 3.1 Lite.
There are two types of asynchronous tasks – a) fire and forget and b) retrieve result later. In fire and forget type of task, the client does not have to get any return value from the called method and also it does not bother whether the called method throws an application exception. In retrieve result later type of task, the client can perform other tasks when the called method is being executed and need not wait for the called method to complete execution. When the called method has completed execution, its result will be returned to the client.
When an asynchronous method is called on a session bean the EJB container dispatches the method call to a separate thread for execution. Thus, the client thread can perform its own execution after the asynchronous method is called.
To declare asynchronous methods in a session bean, the @Asynchronous
annotation is used on a class or a method. If used on a class declaration, all methods of the session bean become asynchronous methods. Alternatively, the <asynch-methodType> element can be used to define an asynchronous method in deployment descriptor. Listing 1 demonstrates an example of asynchronous method declaration.
The return type of an asynchronous method can be void or Future<T>
where T is the type of object returned. A concrete implementation of java.util.concurrent.Future<T>
interface is returned to the client if Future<T>
is the return type. The javax.ejb.AsychResult<T>
constructor is used within the asynchronous method to set the return value. The AsyncResult<Result>(result)
constructor is used in Listing 1 to set the return value of the method whose return type is Future<Result>
.
The client can cancel the method execution by calling the cancel(boolean mayInterruptIfRunning)
of Future
. However, the cancellation may not be done if the method execution is already started. Whether the thread executing asynchronous method can be interrupted is specified by the boolean argument passed to this method. The cancel
method of Future
uses thread interruption under the hood if true is passed to this method. The wasCancelledCalled()
method of SessionContext
from which the asynchronous method is called returns true if mayInterruptIfRunning
is true and returns false if mayInterruptIfRunning
is false.
Whether the asynchronous method has completed execution can be found out by calling the isDone()
method of Future
. This method can be called before calling cancel()
to ensure that the method execution is not yet completed.
The return value of asynchronous method can be obtained by client by calling the get()
method of Future
. There are two get
methods provided by Future
. The no-arg get()
method is a blocking method and waits until the asynchronous method has completed execution with or without exception or the execution of asynchronous method is cancelled by cancel
method. There is one get
method which allows to define the maximum waiting time used in Listing 2. This method returns the return value of asynchronous method until the time limit specified is elapsed else throws a TimeoutException
. The better approach to avoid the get method to block the client thread is to call it after the isDone()
returns true, if the client thread can perform other task parallely.
The asynchronous method should always be called from a business view. Simply calling it from other method won’t execute it asynchronously. The ctx.getBusinessObject()
method is used in Listing 1. to enable the EJB container to call the processRequest()
method asynchronously.
The client calling asynchronous method with Future<T>
return type can receive the exceptions thrown during its execution. The exception is embedded within java.util.concurrent.ExecutionException
and propogated to the client. The client can receive the exception by calling getCause()
on the exception. The asynchronous methods with Future<T>
return type only can throw application exceptions. The asynchronous methods with void return type cannot propogate the exceptionsto the client.
The transaction attributes of NOT_SUPPORTED, REQUIRED
and REQUIRES_NEW
can be used by asynchronous methods. However, the client’s transaction context is not propogated to the asynchronous method.
The client’s security principal is propogated to the asynchronous method.
If you are using JSF 2 as the client for stateful session bean with asynchronous methods, ensure that you are using @ConversationScoped
instead of @SessionScoped
as using the latter sometimes results in ConcurrentModificationException
.
Business use case
We had a request processing application implemented using JMS which had to perform processing of the request asynchronously. The request was sent as a message. This solution required us to maintain message queues. With EJB 3.1, we decided to get rid of the message queues and refactored the design to use asynchronous method of a session bean. The requests were persisted before processing them asynchronously. The Request entity had a status property which was set to COMPLETE when its processing was completed successfully. The Session Façade layer which called the session bean to save a new request and further process it, implemented a timeout of 10 seconds. Any request which was not completed within 10 seconds or which threw exception during execution was not set with COMPLETE status. The Session Façade layer provided a facility to update the requests and then start the processing of a request. The outcome of request processing was returned as Result object. The application also had a facility to re-process the incomplete requests.
@Stateless
public class RequestProcessorBean {
@PersistenceContext
private EntityManager entityManager;
@Resource
private SessionContext ctx;
@Asynchronous
public Future processRequest(Request request)
throws ProcessException {
Result result = new Result();
...
return new AsyncResult(result);
}
public Future saveRequest(Request request) {
entityManager.persist(request);
return ctx.
getBusinessObject(RequestProcessorBean.class).
processRequest(request);
}
public void updateRequest(Request request) {
entityManager.merge(request);
}
}
Listing 1. Declaring asynchronous method
@Stateless
public class RequestFacadeBean {
@EJB
private RequestProcessorBean requestProcessor;
public Result saveRequest(Request request)
throws ProcessException {
Future future=
requestProcessor.saveRequest(request);
Result result = null;
try {
result = future.get(10,TimeUnit.SECONDS);
}
catch(TimeoutException exp) {
//request is not yet processed
}
catch(ExecutionException exp) {
if(exp.getCause() instanceof ProcessException) {
throw exp.getCause();
}
}
}
public Result updateRequest(Request request) throws
ProcessException{
requestProcessor.updateRequest(request);
Future future =
requestProcessor.processRequest(request);
...
}
}
Listing 2. Asynchronous method client
This new feature of EJB 3.1 should be used only when asynchrony is really a need. Overuse of asynchronous methods would degrade the application performance.