Search This Blog

Monday, December 19, 2011

EJB asynchronous processing


In J2EE world JMS was often used for asynchronous execution of synchronous methods. There was no other way to invoke session beans in a background thread. EJB 3.1 introduced an easy way to solve background execution without using messaging.
 It is sufficient to declare methods as @Asynchronous and they will be invoked in background thread. The result value of an asynchronous invocation is available to the client by returning the Future object and even allows canceling of background tasks.
 The built-in support for asynchronous processing is easier and cleaner compared to the JMS way and it also allows bidirectional communication. Implementing request-response communication with JMS messaging is harder and you would need input queue and output queue. JMS on the other hand allows looser coupling.

So, say for example that you want to send email asynchronously (example assumes that glassfish application server is used).
Using JMS you can do it in combination with message driven beans (components designed to consume the asynchronous messages). As soon as a new message reaches the jms destination, an message driven bean instance is retrieved from the pool to handle the message.

To use JMS and message driven beans you have to first configure JMS connection factory and destination queue through glassfish administration console. For example:





 
Calling client (JSF bean or another EJB for example) then can inject these resources and use them to send message (queue asynchronous task).
//inject connection factory and destination  
      @Resource(name="connFactory", mappedName="mailConnFactory")  
      private QueueConnectionFactory qFactory;  
      @Resource(name="jmsQueue", mappedName="mailQueue")  
      private Queue queue;  

And asynchronous call is done by sending message that will be asynchronously processed by message driven bean.

QueueConnection qConn = (QueueConnection)qFactory.  
                   createConnection();  
           // Get session  
           Session session = qConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);  
           // Create the JMS message  
           ObjectMessage msg = session.createObjectMessage();  
           // set payload  
           MailData data = new MailData("reciever@mail.com","message text");  
           msg.setObject(data);  
           // Send JMS message  
           session.createProducer(queue).send(msg);  
           //release resources  
           session.close();  
           qConn.close();  


Once message is queued you need message driven bean that will process message. In this case this message driven bean will send actual email. Mail session resource in glassfish administration console(under resources/java mail sessions node) must be configured to send actual email.
@MessageDriven(mappedName="mailQueue")  
 public class MailSenderBean implements MessageListener {  
      @Resource(name = "mail" )  
      private Session mailSession;  
      @Override  
      public void onMessage(Message message) {  
            if (message instanceof ObjectMessage) {  
                 try{  
                      ObjectMessage msg = (ObjectMessage)message;  
                      MailData data = (MailData)msg.getObject();  
                      MimeMessage msg = new MimeMessage(mailSession);  
                      msg.setSubject("subject");  
                      msg.setRecipient(RecipientType.TO,  
                               new InternetAddress(data.getRecipient()));  
                      msg.setContent(data.getText(), "text/html");
                     Transport.send(msg);  
                 }catch(Exception e) {  
                      // handle exception  
                 }  
            }  
      }  
 }  

And to accomplish same thing with built in asynchronous support you just have to annotate session bean method with @Asynchronous annotation and container will execute method in background thread.

@Stateless  
 public class MailSenderBean implements MailSender {  
      @Resource(name = "mail" )  
      private Session mailSession;  
      @Asynchronous  
      @Override  
      public void sendEmail(MailData data){  
           try {  
                MimeMessage msg = new MimeMessage(mailSession);  
                msg.setSubject("subject");  
                msg.setRecipient(RecipientType.TO,  
                     new InternetAddress(data.getRecipient())); 
                msg.setContent(data.getText(), "text/html"); 
                Transport.send(msg);  
           } catch (Exception e) {  
                //handle error  
           }  
      }  
 }  

This method could also return Future object. Calling client could then check with future.isDone() if processing thread has finished with execution and fetch result of execution with future.get().