You can use the sample code within this topic to develop client code that calls EJB asynchronous methods.
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;
}
}
@EJB AcmeRemoteInterface myAsyncBean;
try {
myAsyncBean.fireAndForgetMethod();
} catch (EJBException ejbex) {
// Asynchronous method never dispatched, handle system error
}
myResult = myFutureResult.get(5, TimeUnit.SECONDS);
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();
@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.
}
@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
}
@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.
}
@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();
}
}
// ...
}
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;