XINS - Asynchronous calls (by Anthony Goubard)

Introduction

XINS has primarily been designed to execute calls synchronously and most of the time it's what you need. But there are some situations where you want the call to be asynchronous. For example:
  1. You have to call several APIs or several functions at the same time.
  2. You do not expect any answer from the call or the answer is not important and you ignore errors.
  3. You don't want to wait for the answer because the call can take a lot of time.
  4. You want to call several functions and handle the results as you receive them.
  5. You want to call an API from a graphical user interface but you don't want the user interface to freeze during the call.

Parallel calls (Solution for 1. and 2.)

The first case is when an action calls several independent XINS functions. Then it would be more efficient if it was done in parallel.
The solution is to execute the calls in separated threads and then to call the Thread.join() method on all started threads in order to wait for the threads to finish before continuing the program.

Let's take the example where we want the details of a customer, for which, we need to call 2 functions, one to get the address details and another one to ask the account details.

// Create the Client API. The CAPI will be used to call the API.
CAPI capi = new CAPI(descriptor);

int customerId = 12345654;

// Create and start the thread that calls the AccountDetails function
AccountDetailsThread callThread = new AccountDetailsThread(capi, customerId);
callAccountDetails.start();

// You don't need to create another thread to call the address details,
// just use the current thread
AddressDetailsResult addressDetails = capi.callAddressDetails(customerId);

// Wait for the AccountDetails function to finish (if it has not finished yet)
// If you are not interested in the response of the call,
// you can remove the call to the join() method
callAccountDetails.join();

AccountDetailsResult accountDetails = null;
if (callAccountDetails.getException() != null) {
   throw callAccountDetails.getException();
} else {
   accountDetails = callAccountDetails.getResult();
}

// Congratulation! At this point, you have the address and account details.


/**
 * Thread that calls the AccountDetails function.
 */
class AccountDetailsThread extends Thread {

   private CAPI _capi;
   private int _id;
   private Exception _exception;
   private AccountDetailsResult _result;

   AccountDetailsThread(CAPI capi, int id {
      _capi = capi;
      _id = id;
   }

   public void run() {
      try {
         _result = _capi.callAccountDetails(_id);
      } catch (Exception ex) {
         _exception = ex;
      }
   }

   public Exception getException() {
      return _exception;
   }

   public AccountDetailsResult getResult() {
      return _result;
   }
}

If you needed to make a third call then you would need to create another thread class.

If you have a lot of parallel calls to execute, you may consider using the more generic CallCAPIThread class (javadoc).

Get notified of the result (Solution for 3. and 4.)

Another case of asynchronous calls is when you want to call an API and get notified when the answer is received.
This can be done with listeners. The listeners will get invoked when the result is received.
This function is provided by a new org.xins.client.async package. This package is part of XINS as of XINS 1.4.

The package contains the following classes:

  • AsynchronousCall: Class where you register the listener and perform the call.
  • CallListener: Interface for a listener that is notified when a call finishes (either successfully or not).
  • CallSucceededEvent: Event received when the call succeeded.
  • CallFailedEvent: Event received when the call failed.
Let's see an example on how to use these classes:
/**
 * Adds credits to a customer.
 */
class AddCreditsClient implements CallListener {

   public AddCreditsClient(CAPI capi, int credits, int customerID) {
      AsynchronousCall asyncCall = new AsynchronousCall();

      // Register this call in order to be notified when the result arrives
      asyncCall.addCallListener(this);
      AddCreditsRequest request = new AddCreditsRequest();
      request.setCustomerID(customerID);
      request.setCreditsToAdd(credits);

      // Start the call in a separate thread
      asyncCall.call(capi, request);
   }

   public void callSucceeded(CallSucceededEvent event) {
      AddCreditsResult result = (AddCreditsResult) event.getResult();
      AddCreditsRequest request = (AddCreditsRequest) event.getRequest();

      // Here we just show the result, but we could have logged the information
      // or stored it in a database.
      System.out.println("Customer N\u00b0 " + request.getCustomerID() +
            " has been credited. His new credit is $" +
            result.getCustomerCredit() + ".");
   }

   public void callFailed(CallFailedEvent event) {
      AddCreditsRequest request = (AddCreditsRequest) event.getRequest();

      // The reason why the call failed
      Exception exception = event.getException();

      // Depending on the error, we show a different message
      if (exception instanceof UnsuccessfulXINSCallException) {
         System.err.println("Failed to credit the customer N\u00b0 " +
               request.getCustomerID() + ". Error on server side: " +
               ((UnsuccessfulXINSCallException) exception).getErrorCode());
      } else {
         System.err.println("Failed to credit the customer N\u00b0 " +
               request.getCustomerID() + ". Reason: " +
               event.getException().getMessage());
      }
   }
}

Call in user interface (Solution for 5.)

Another case where asynchronous call is needed is when you want to call a function after an action on a Swing user interface (like clicking on a button).
Because Swing is single threaded the user interface will freeze (typically showing a grey rectangle) until the event finishes. This can take up to several seconds, depending on the network and the API function.

If you don't need to update the user interface with the result, you could use one of the two previous solutions. But if you want to update the user interface with the result, you will need to do it on the event dispatch thread.

Sun has developed a class named SwingWorker to deal with this issue. This class will be available in the upcoming Java SE 6 and is already available for download for older Java versions.

More information and examples on how to use it are available at: http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html#SwingWorker