Developing client code that calls EJB asynchronous methods

You can use the sample code within this topic to develop client code that calls EJB asynchronous methods.

Before you begin

This task assumes that the following interface and bean implementation classes exist:
public interface AcmeRemoteInterface {
   void fireAndForgetMethod ();
   Future<Integer> methodWithResults() throws AcmeException1, AcmeException2;
}
@Stateless
@Remote(AcmeRemoteInterface.class)
@Asynchronous
public class AcmeAsyncBean {
   public void fireAndForgetMethod () {
      // do non-critical work
   }

   public Integer methodWithResults() {
      Integer result;
      // do work, and then return results
      return result;
   }
}

About this task

Procedure

  • Create client code that calls an asynchronous method where no results are returned, sometimes called a fire and forget method. This type of asynchronous method cannot result in application exceptions. However, system exceptions can occur that must be resolved.
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    try {
       myAsyncBean.fireAndForgetMethod();
    } catch (EJBException ejbex) {
       // Asynchronous method never dispatched, handle system error
    }
  • Create client code that calls an asynchronous method where results are returned.
    • Create client code that calls an asynchronous method where the client waits for up to 5 seconds (the client thread is blocked during this time window) to receive results. Exception handling requirements are the same as in the previous step. For example:
      myResult = myFutureResult.get(5, TimeUnit.SECONDS);
      
    • Create client code that calls an asynchronous method where results are not immediately obtained. After the method has executed, the client retrieves the results. This scheme prevents the client thread from blocking, and the client is free to execute other work while polling for results. Exception handling requirements are the same as in the previous steps. In this example, the client periodically polls the Future<V> object to determine when the asynchronous method has finished executing. For example:
      while (!myFutureResult.isDone()) {
      // Execute other work while waiting for the asynchronous method to complete.
      }
      
      // This call is guaranteed not to block because isDone returned true.
      myResult = myFutureResult.get(); 
  • Create client code to handle application exceptions.
    The client code calls an asynchronous method which returns an application exception in the Future<V> object. The following example demonstrates the exception handling code required to determine which application exception occurred.
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer>>myFutureResult = null;
    Integer myResult = null;
    
    try {
       myFutureResult = myAsyncBean.methodWithResults();
    } catch (EJBException ejbx) {
       // Asynchronous method never dispatched, handle exception
    }
    
    // Method is eventually dispatched.  Wait for results.
    
    try {
       myResult = myFutureResult.get();
    } catch (ExecutionException ex) {
       // Determine which application exception that occurred during the
       // asynchronous method call.
       Throwable theCause = ex.getCause();
       if (theCause instanceof AcmeException1) {
          // Handle AcmeException1
       } else if (theCause instanceof AcmeException2) {
          // Handle AcmeException2
       } else {
          // Handle other causes.
       }
    } catch ( ... ) {
       // Handle other exception.
    }
  • Create client code to identify system exceptions thrown by asynchronous method call during execution.
    The following example demonstrates the exception handling code required to determine if a system exception occurred.
    
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer>>myFutureResult = null;
    Integer myResult = null;
    
    try {
       myFutureResult = myAsyncBean.methodWithResults();
    } catch (EJBException ejbx) {
       // Asynchronous method was not dispatched; handle exception.
    }
    
    // Method will eventually be dispatched so block now and wait for results
    
    try {
       myResult = myFutureResult.get();
    } catch (ExecutionException ex) {
       // Find the exception class that occurred during the asynchronous method
       Throwable theCause = ex.getCause();
       if (theCause instanceof EJBException) {
          // Handle the EJBException that might be wrapping a system exception
          // which occurred during the asynchronous method execution.
          Throwable theRootCause = theCause.getCause();
          if (theRootCause != null) {
             // Handle the system exception
          }
       } else ... // handle other causes 
    } catch (...) {
       // handle other exceptions
    }
  • Optionally create client code to cancel an asynchronous method call. If this attempt is successful, then the Future.isCancelled method returns true and the Future.get methods result in the CancellationException.
    The following example demonstrates the code required to cancel an asynchronous method call.
    @EJB AcmeRemoteInterface myAsyncBean;
    
    Future<Integer> myFutureResult = myFutureResult.methodWithResults();
    Integer myResult;
    
    if (myFutureResult.cancel(true)) {
       // Asynchronous method was not dispatched.
    } else {
       // Asynchronous method already started executing.  The bean can still check
       // whether an attempt was made to cancel the call.
    }
    
    if (myFutureResult.isCancelled()) {
       // Asynchronous method call did not start executing because the cancel
       // method returned true in a previous line of the example.
    }
    
    try {
       myResult = myFutureResult.get();
    } catch (CancellationException ex) {
       // Handle the exception that occurs because the cancel method returned true
       // in a previous line of the example.
    }
  • Optionally create bean code to check whether a client attempted to cancel the asynchronous method call.
    The following example demonstrates the bean code required to check whether the client attempted to cancel the asynchronous method call. If the client attempted to cancel the work, then the SessionContext.wasCancelCalled method returns true, and the bean code can avoid unnecessary work.
    @Resource SessionContext myContext;
    
    public Future<Integer> methodWithResults() {
       for (int i = 0; i < 3; i++) {
          // Do one piece of long-running work.
    
          // Before continuing, check whether the client attempted to cancel this work.
          if (myContext.wasCancelCalled()) {
             throw new AcmeCancelCalledException();
          }
       }
    
       // ...
    }
  • Pass back multiple output values from the asynchronous method invocation.

    In some cases, a method must pass back multiple pieces of data.

    One way to accomplish this task is by using pass-by-reference semantics. In this approach, an object is passed into the method as a parameter, updated by the method, and then the updated value is available to the client. This approach does work for asynchronous methods, but it is not the optimal pattern.

    To return multiple pieces of data, create a wrapper inside the Future object that is returned by the method. In this approach, a wrapper object is defined that contains instance variables which hold the different pieces of data that must be returned. The asynchronous method sets these pieces of data into the wrapper object and returns it, and the client code then retrieves this data from the Future object.

    Embedding multiple pieces of data inside the wrapper object is a local or remote, transparent pattern, that identifies exactly when the results are available. In contrast, the traditional pass-by-reference technique does not give the client an easy way to determine when the results are available. The passed in object is not updated until the asynchronous method runs, and the client cannot determine when that has occurred, other than by interrogating the Future object using the Future.isDone() or Future.get() methods.

    
    // This is the result object that is returned from the asynchronous method.
    // This object is wrappered in a Future object, and it contains the two pieces of data
    // that must be returned from the method.
    class ResultObject {
         public Boolean myResult; 
         pubilc String myInfo;
    } 
                        
    
    // This is the asynchronous method code that gets the results and returns them.
    @Asynchronous
    public Future<ResultObject> asyncMethod1(Object someInputData) {
         boolean result = doSomeStuff();
         String info = doSomeMoreStuff();
         ResultObject theResult = new ResultObject();
         theResult.myResult = result;
         theResult.myInfo = info;
         return new javax.ejb.AsyncResult<ResultObject>(theResult);     
    }
                        
    
    // This is the client code that obtains the ResultObject, and then extracts the needed data from it.
    Future<ResultObject> myFutureResult = myBeanRef.asyncMethod1(someInputData);
    ResultObject theResult = myFutureResult.get();
    boolean didItWork = theResult.myResult;
    String explanation = theResult.myInfo;