Pages

Sunday, March 25, 2012

JEE example - part 2: Asynchronous EJB call


In this entry I will discuss the use of EJB asynchronous call in JEE applications.

By default, session bean invocations through the Remote, Local, and no-interface views are synchronous. The client that made the call blocks for the duration of the invocation and is returned control only after all invocation processing has completed. Since EJB 3.1 clients can achieve asynchronous invocation behavior (for instance on long time processing tasks). A session bean class or a method can be marked with the annotation javax.ejb.Asynchronous. When using such designated methods, control returns to the client before the container dispatches the invocation to a bean instance; the container continues processing the invocation on a separate thread of execution. It is to be noted that asynchronous method invocation semantics only apply to the no-interface, Local business, and Remote business client views. As stated in the EJB 3.1 specification, an asynchronous method must have return type void or java.util.concurrent.Future<V>, where V is the result value type. Here is an example that completes the interface from previous post:

package org.example.jee.service;
import java.util.concurrent.Future;

/**
 * Service Interface
 * 
 * @author fracaru
 * 
 */
public interface IService {

 /**
  * Greetings 
  * @return greetings
  */
 String sayHello();
           
        /**
  * Asynchronous Greetings 
  * @return greetings
  */
 Future sayHelloAsynchronously();
}


And the EJB implementation:

package org.example.jee.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Future;

import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Remote;
import org.example.jee.service.IService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author fracaru
 * 
 */
@Remote(IService.class)
@Stateless
public class HelloServiceBean implements IService {

 @Override
 public String sayHello() {
  return "Hello World";
 }

 @Override
 @Asynchronous
 public Future sayHelloAsynchronously() {
  // do some processing stuff here
  if (ctx.wasCancelCalled()) {
   return new AsyncResult(
     "sayHello Asynchronous Call Cancelled");
  }
  // continue long processing stuff here
  for (int i = 0; i < 10000; i++) {
   try {
    File f = File.createTempFile("process", "tmp");
    FileOutputStream fos = new FileOutputStream(f);
    fos.write(i);
    fos.flush();
    fos.close();
    if (i % 1000 == 0) {
     System.out.println("Processing asynchronous call ...");
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return new AsyncResult("Asynchronous Hello World");
 }

}

What is the processing on the client side? When using the Future return type, the client can retrieve the invocation result value, discover any invocation exception, or attempt to cancel the asynchronous invocation. When a client calls the get method, it blocks, waiting the processing to complete, and then retrieves the result, if available. It is to remark that the JEE specification recommends to clients to process the result of an asynchronous EJB call as the server will try to return this result.

@Test
 public void testService() {
 IService ejb = (IService) ctx.lookup(remoteBean);
 Future result = ejb.sayHelloAsynchronously();
 
 System.out.println("Return from asynchronous call ...");
 // process result call
 String message = result.get();
    Assert.assertEquals("Asynchronous Hello World", message);
 }

Before EJB 3.1, the only possibility to build asynchronous JEE architecture was to use JMS and MDB. JMS is a Message Oriented Middleware (MOM) that allows application JEE applications to create, send, receive, and read messages. You have to create and manage JMS queues where a client publishes messages. The processing is handled in the MDB that consumes the message. It is to be noted that JMS provides a more robust and reliable mechanism to achieve asynchronous calls than asynchronous session bean methods. In a JMS / MDB architecture, messages are persisted, and only on successful processing in the MDB they are removed from queue only on successful processing in the MDB. Moreover, using JMS allows components or applications to be to be loosely coupled and provides allows delivery guarantees. In conclusion, the use of javax.ejb.Asynchronous annotation is recommended when oneyou just wants to call a method asynchronously, with no need of complex mechanisms.

2 comments:

/* */