Going Async with EJBs

EJBs provide support for asynchronous execution of business logic wherein the calling thread (invoking the EJB business method) is released and the actual processing takes place in a background thread which is managed by the EJB container itself.

The EJB specification provided us the following options

  • Asynchronous Beans

  • Message Driven Beans

@Asynchronous

Using MDBs (along with a full fledged JMS infrastructure setup) is not something which you would want to commit to, if all you need is a few methods to be invoked asynchronously. In case you do not need strict decoupling b/w sender and receiver and the rich features of enterprise messaging offered by JMS, an easier way of adopting the asynchronous processing paradigm is via the @Asynchronous annotation.

package ejbap.async;

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;

@Stateless
@Asynchronous
public class EmailService {

    public void sendMail() {

        System.out.println("Sending email......");
        try {
            //for sample purposes ONLY
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
    }

}

In the above example, the annotated method returns void. One also can return a parameterized java.util.concurrent.Future object i.e. Future<T>. Think of it like a tracking ID for your order - you can invoke methods on it to introspect the status of the asynchronous invocation or even cancel your it !

package ejbap.async;

import java.util.UUID;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;

@Stateless
public class DataService {

    @Asynchronous
    public Future<String> fetch() {
        System.out.println("Invocation thread -- " + Thread.currentThread().getName());
        return new AsyncResult<>(longRunningMethod());
    }

    private String longRunningMethod() {
        System.out.println("Async execution thread -- " 
                + Thread.currentThread().getName());

        try {
            //for sample purposes ONLY
            Thread.sleep(5000);

        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }

        return UUID.randomUUID().toString();
    }
}

@Asynchronous is applicable on individual methods as well as classes. In case it is used on a class, all the methods of that EJB would execute asynchronously

@ActivationConfigProperty

This annotation is used to declaratively configure a Message driven bean. It accepts name value pairs where the name represents an attribute

Message Driven Beans are powered by the @MessageDriven annotation. Please refer to the EJB Core chapter for a detailed discussion on this.

package ejbap.async;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationLookup",
            propertyValue = "java:comp/env/orderQueue"),
    @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue")
})
public class MDBWithActivationConfig implements MessageListener{

    @Override
    public void onMessage(Message msg) {
        try {
            emailOrderDetails(msg);
        } catch (JMSException ex) {
            throw new RuntimeException(ex);
        }
    }

    private void emailOrderDetails(Message msg) throws JMSException{
        //logic to send order details...
    }

}

As you noticed, the destinationType and destinationLookup attributes were used in the above example. There are a set of standard attributes which are applicable for Message driven beans which listen to JMS destinations (topics and queues)

  • messageSelector: specifies selectors to be used for filtration

  • connectionFactoryLookup: connection factory of the JMS provider

  • clientId: used while connecting to JMS provider

  • subscriptionName: name of the durable subscription in case the MDB is configured to listen to a Topic

  • subscriptionDurability: type of subscription, either Durable or NonDurable

  • acknowledgeMode: acknowledgement mode to be used, either Auto_acknowledge or Dups_ok_acknowledge

Looking ahead ...

We'll dive into the eager initilization feature provided by Singleton EJBs.

Last updated