Home > EJB, JAVA EE > EJB 3.1 – Asynchronous Methods

EJB 3.1 – Asynchronous Methods

One of the important features in EJB 3.1 is the ability to make asynchronous method calls. While the spec has always prevented spawning of user threads explicitly and still does so, at least now there is a way to achieve asynchronous behavior in a simple way.

Past workarounds

In the past, the most common workaround used to overcome the limitation imposed by the spec involved using  JMS. While JMS plays an important role in applications, it should be really used where messaging is the requirement – either publish/subscribe topics or point-to-point queues. If all you want to do is to make a method call asynchronous, there should be a straightforward way to do this and ejb 3.1 addresses precisely this gap in functionality.

@Asynchronous

It is fairly trivial to make a method asynchronous in ejb 3.1 using annotations. For example


@Asynchronous // This makes all business methods in the class async
public class AsyncProcessor{

    @Asynchronous //To make only this method async
    public void doAsyncStuff(){
    }

}

That’s it.  This is your typical “fire and forget” kind of async behavior where you are not waiting for the method to finish. When the asyncMethod is invoked by the container, it is able to use the @Asynchronous annotation and invoke the method in a different thread. To test this out, here is a test async bean and a caller servlet.


@Stateless
public class AsyncBean {

    @Asynchronous
    public void asyncMethod() {
        System.out.println("start running asyncMethod in thread " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {  e.printStackTrace();  }
        System.out.println("finished running asyncMethod in thread " + Thread.currentThread().getName());
    }
}

And here is the caller servlet

@WebServlet(name = "AsyncTestServlet",urlPatterns = {"/async"})
public class AsyncTestServlet extends HttpServlet {
    @Inject
    AsyncBean asyncBean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("caller method running in thread "+Thread.currentThread().getName());
        asyncBean.asyncMethod();
        System.out.println("caller method returned in thread "+Thread.currentThread().getName());
    }
    ...
}

And here is the output from the method calls showing that the async method is called in a different thread.

[#|2011-04-21 21:40:45.591 |INFO|glassfish3.0.1|caller method running in thread http-thread-pool-8080-(1)|#]
[#|2011-04-21 21:40:45.593 |INFO|glassfish3.0.1|caller method returned in thread http-thread-pool-8080-(1)|#] // Calling servlet is done
[#|2011-04-21 21:40:45.597 |INFO|glassfish3.0.1|start running asyncMethod in thread Ejb-Async-Thread-1|#]
[#|2011-04-21 21:40:48.598 |INFO|glassfish3.0.1|finished running asyncMethod in thread Ejb-Async-Thread-1|#]

Note that the ejb async method is called on a different thread “Ejb-Async-Thread-1″, and the simulated three second long method finishes long after the calling servlet thread has finished executing.

Mixing sync and async

There is another possible scenario, where the business method exposed has two parts, an async part and a synchronous part. For example, consider the OrderProcessor class below, which does an initial check to make sure the order is ready to submit, but after that check, it fires off an async method to call a web service (which may take a couple of seconds lets say). If we don’t want the customer to be waiting for the second call, we may try something like this -


@Stateless(name = "OrderProcessorEJB")
public class OrderProcessorBean {
    public void completeOrder(OrderDetails orderDetails){
        System.out.println("start running completeOrder Method in thread " + Thread.currentThread().getName());
        boolean okToProceed=validateOrderDetails (orderDetails);
        if(okToProceed){
            //We want this to happen async, since this may take a while
            submitDetailsToPartnerSite(orderDetails);
        }else{
            throw new InvalidOrderException("Order not valid for reason ...");
        }
        System.out.println("Done with completeOrder Method in thread "+ Thread.currentThread().getName());
    }

    //check here for validity of order before processing - check synchronously
    private boolean validateOrderDetails(String orderDetails){
        System.out.println("start running validateOrderDetails in thread " + Thread.currentThread().getName());
        return true;
    }

    // This may take a couple of seconds, can be done async
    @Asynchronous
    public void submitDetailsToPartnerSite(String orderDetails){
        System.out.println("start running submitDetailsToPartnerSite in thread " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000); // simulate delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Done with submitDetailsToPartnerSite in thread " + Thread.currentThread().getName());
    }

}

And here is the output for the above

[#|2011-04-22T 07:29:29.763|start running completeOrder Method in thread http-thread-pool-8080-(2) |#]
[#|2011-04-22T 07:29:29.763|start running validateOrderDetails in thread http-thread-pool-8080-(2) |#]
[#|2011-04-22T 07:29:29.764|start running submitDetailsToPartnerSite in thread http-thread-pool-8080-(2) |#]
[#|2011-04-22T 07:29:32.764 |Done with submitDetailsToPartnerSite in thread http-thread-pool-8080-(2) |#]
[#|2011-04-22T 07:29:32.764 |Finished completeOrder and returned to customer in thread http-thread-pool-8080-(2)|#] // We wanted this to finish earler !

But that isn’t what we expected! The submitDetailsToPartnerSite  method does not execute asynchronously in spite of the asynchronous annotation. Why does that happen ? When the second method is invoked from the first method, the container doesn’t get a chance to proxy the call to the second method – it is called directly on the same instance and hence executes synchronously. So what do we do to get our desired behavior?. We need to make a little change so that the submitDetailsToPartnerSite method is not directly called, but indirectly so that the container gets a chance to proxy the call. Here is the modified code – the only change is the way the submitDetailsToPartnerSite is invoked from the completeOrder method.


@Stateless(name = "OrderProcessorEJB")
public class OrderProcessorBean {

    @Inject
    SessionContext ctx;

    public void completeOrder(OrderDetails orderDetails){
        System.out.println("start running completeOrder Method in thread " + Thread.currentThread().getName());
        boolean okToProceed=validateOrderDetails (orderDetails);

        if(okToProceed){

            // NOTICE THE CHANGE HERE - INVOKED USING THE ctx INSTEAD of DIRECTLY
            ctx.getBusinessObject(OrderProcessorBean.class).submitDetailsToPartnerSite(orderDetails);

        }else{
            throw new InvalidOrderException("Order not valid for reason ...");
        }
        System.out.println("Done with completeOrder Method in thread "+ Thread.currentThread().getName());
    }

    //check here for validity of order before processing - check synchronously
    private boolean validateOrderDetails(String orderDetails){
        System.out.println("start running validateOrderDetails in thread " + Thread.currentThread().getName());
        return true;
    }

    // This may take a couple of seconds, can be done async
    @Asynchronous
    public void submitDetailsToPartnerSite(String orderDetails){
        System.out.println("start running submitDetailsToPartnerSite in thread " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000); // simulate delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Done with submitDetailsToPartnerSite in thread " + Thread.currentThread().getName());
    }

}

Note that the only code change consists of injecting the SessionContext ctx and using that to call the method as ctx.getBusinessObject(OrderProcessorBean.class).submitDetailsToPartnerSite(orderDetails) . Now this results in the first method getting executed synchronously while the second method getting executed asynchronously, just as we want. Here is the output

[#|2011-04-22T 18:30:59|start running completeOrder Method in thread http-thread-pool-8080-(2)|#]
[#|2011-04-22T 18:30:59|start running validateOrderDetails in thread http-thread-pool-8080-(2)|#]
[#|2011-04-22T 18:30:59|Done with completeOrder Method in thread http-thread-pool-8080-(2)|#]
[#|2011-04-22T 18:30:59|caller method returned in thread http-thread-pool-8080-(2)|#] // Customer Is Not Waiting !
[#|2011-04-22T 18:30:59|start running submitDetailsToPartnerSite in thread Ejb-Async-Thread-2|#]
[#|2011-04-22T 18:31:02|Done with submitDetailsToPartnerSite in thread Ejb-Async-Thread-2|#]

Future

In addition to “fire and forget”, there is also the scenario where we want to fire something off asynchronously, wait for a finite time and check the status, and then do something based on the status. EJB 3.1 takes care of that by providing the familiar Future, which is part of standard java concurrency. Consider the example below where the AsyncService has an asyncMethod which simulates a long running task ( 3 seconds) and returns the result of the task as a Future (Future<T> can be returned). The caller in this case is a servlet which is willing to wait for a second at the most for the async method to return.  The calling servlet simply fires off the asyncMethod, waits for a second and then if the async task is done by that time, uses the returned data. On the other hand if the async task is not done, it can then handle the situation accordingly based on the use case (either show old data, or default data etc). This would be a common use case where due to network issues the response times may vary and we want to take different actions based on a quick response vs slow response.


@Stateless
public class AsyncService {

    @Asynchronous
    public Future asyncMethod(){
    try{
        //simulate long task
        Thread.sleep(3000);
     }catch(Exception e){
         e.printStackTrace();
         return new AsyncResult("Error"); // handle exception

     }
     return new AsyncResult("Meaningful_Result_String");
 }
}

public class AsyncTestServlet extends HttpServlet {

 @Inject
 AsyncService asyncService;

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     Future<String> result=asyncService.asyncMethod();
     try {
         if(result.isDone()){
             //Handle successful fetch
             System.out.println("success - use the result " + result.get());
         }else{
             System.out.println("not yet done");
             //Handle task not finished yet - display cached data etc, based on the use case
         }
     } catch (Exception e) {
        e.printStackTrace();
     }
 }

or do this to wait UP TO 1 second

    try {
        String resultStr=result.get(1, TimeUnit.SECONDS);
        System.out.println(" async task done - resultStr is "+resultStr);
    } catch (TimeoutException e) {
        System.out.println("not yet done");
        //Handle task not finished yet - display cached data etc, based on the use case
    }

If using the first form, be sure to invoke the Future<T>.get() method only after making sure that Future<T>.isDone() is called. If the async task hasn’t finished then the get() method will block, which may not be what you want.! Otherwise use Future.get(timeout,TimeUnit) and catch the TimeoutException. The benefit of using that approach is that you can wait up to 1 second. So if the async method is done earlier, you don’t have to wait the whole 1 second.

Limits

Note that if there are multiple concurrent asynchronous invocations on a bean, there may be a limit to how many the container will allow concurrently. Consider the code sample below


public class AsyncTestServlet extends HttpServlet {
    @Inject
    AsyncService asyncService;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        for(int i=0;i<20;i++){
            asyncService.asyncMethod();
        }
    }
}

On glassfish, after about 16 threads, there was a pause and only after some of those 16 threads finished executing did the remaining 4 threads start. From the caller’s point of view the method calls are still asynchronous, but instead of the async call getting done after 3 seconds (assuming the async task takes 3 seconds), some of the async tasks may get done only 6 seconds after the async method is invoked. This is expected, since the thread pool is finite – so makes sense to experiment with your app server to understand and configure the behavior.

As always, the specs provide the most accurate and best information and it is always a good idea to refer to the specs before using the async behavior for something mission critical.

About these ads
Categories: EJB, JAVA EE
  1. rajublog
    July 9, 2011 at 7:37 am | #1

    Great!!! .i like it.its a good article.i would say from the core. :)

  2. August 14, 2011 at 4:20 pm | #2

    Satish,

    Thank you for this post. It is a very clear and helpful demonstration of EJB 3.1 Asynchronous method behavior. In Oracle Glassfish 3.1.1, according to the EjbAsyncInvocationManager class in com.sun.ejb.containers the 16 thread limit is hard-coded (refer to line 83).

    Thread pools are finite, however the 16 thread limit is a limitation currently imposed by that class.

    M

  3. December 6, 2011 at 8:06 am | #3

    thank your post help me a lot :d

  4. mk
    December 7, 2011 at 4:37 pm | #4

    Thanks Satish it helped me a lot to understand Asynch behaviour in EJB 3.1. I have a scenario

    web->Enterprise process hub -> business logic , I get one request from web to business logic, here I need to split one request into two requests and as soon as I get the first request response need to return the response back and call call send request asynch, could you please tell me how I can return the Future (second request response which to Enter Prise process hub?

  5. Jsh
    April 24, 2012 at 7:33 pm | #5

    Great blog! Just wanted to point out that instead of “@Inject SessionContext” one can also use a self referring EJB injection like,
    {{
    @EJB
    private OrderProcessorBean opbRef;
    }}

    And use it more directly as in

    {{
    opbRef.submitDetailsToPartnerSite(orderDetails);
    }}

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: