# Asynchronous Scaleout Application Interface

## Asynchronous Client API Overview

Async Client API

The Asynchronous Client API provides Java applications the capability to connect to and make requests to UIMA-AS services. ProcessCas and CollectionProcessingComplete requests are supported.

It provides four kinds of capabilities:

- sending requests, receiving replies, asynchronously (or synchronously)

- setting timeouts and limits on the number of simultaneous requests in process (via setting the CAS Pool size)

- using an optionally provided collection reader to obtain items to process

- deploying services as part of the startup process

An application can use this API to prepare and send each CAS to a service one at a time, or alternately can use a UIMA collection reader to prepare the CASes to be delivered.

The application normally provides a listener class to receive asynchronous replies. For individual CAS requests a synchronous sendAndReceive call is available. As an alternative for this synchronous call, instead of using this client API, the standard UIMA Analysis Engine APIs can be used with an analysis engine instantiated from a JMS Service Descriptor. See .

As a convenience, the Asynchronous Client API can also be used to deploy (i.e., "start") services. Java services deployed by the API are instantiated in the same JVM. Logging for all UIMA components in the same JVM are merged; class names and thread IDs can be used to distinguish log entries from different services. All services in the JVM can be monitored by a single JMX console. Native C++ UIMA services can be called from the JVM via the JNI or optionally be launched as separate processes on the same machine. In either case logging and JMX monitoring for native services are integrated with the other UIMA components in the JVM.

## The UimaAsynchronousEngine Interface

An application developer's starting point for accessing UIMA-AS services is the UimaAsynchronousEngine Interface. For each service an application wants to use, it must instantiate a client object:

    UimaAsynchronousEngine uimaAsEngine =
            new BaseUIMAAsynchronousEngine_impl();

The following is a short introduction to some important methods on this class.

- `void initialize(Map anApplicationContext)`: Initializes an asynchronous client. Using configuration provided in the given Map object, this method creates a connection to the UIMA-AS Service queue, creates a response queue, and retrieves the service metadata. This method blocks until a reply is received from the service or a timeout occurs. If a collection reader has been specified, its typesystem is merged with that from the service. The combined typesystem is used to create a Cas pool. On success the application is notified via the listener's initializationComplete() method, which is called prior to the original call unblocking. Asynchronous errors are delivered to the listener's entityProcessComplete() method. See for more about the ApplicationContext map.

- `void addStatusCallbackListener(UimaASStatusCallbackListener aListener)`: Plugs in an application-specific listener. The application receives callbacks via methods in this listener class. More than one listener can be added.

- `CAS getCAS()`: Requests a new CAS instance from a CAS pool. This method blocks until a free instance of CAS is available in the CAS pool. Applications that use synchronous `sendAndReceive()` and `getCAS()` need to call `CAS.reset()` before reusing the CAS, or `CAS.release()` to return it to the CAS pool. Applications that use asynchronous `sendCAS()` and `getCAS()` must not call `CAS.release()` nor `CAS.reset()` unless `sendCAS()` throws an exception. If `sendCAS()` call is successful, the UIMA AS framework code releases each CAS automatically when a reply is received. The framework releases a CAS right after a callback listener `entityProcessComplete()` completes.

- `void sendCAS(CAS aCAS)`: Sends a given CAS for analysis to the UIMA-AS Service. The application is notified of responses or timeouts via `entityProcessComplete()` method.

- `void setCollectionReader(CollectionReader aCollectionReader)`: Plugs in an instantiated CollectionReader instance to use. This method must be called before `initialize`.

- `void process()`: Starts processing a collection using a collection reader. The method will block until the CollectionReader finishes processing the entire collection. Throws ResourceProcessException if a CollectionReader has not been provided or initialize has not been called.

- `void collectionProcessingComplete()`: Sends a Collection Processing Complete request to the UIMA-AS Analysis Service. This call is cascaded down to all delegates; however, if a particular delegate is scaled-out, only one of the instances of the delegate will get this call. The method blocks until all of the components that received this call have returned, or a timeout occurs. On success or failure, the application is notified via the statusCallbackListener's collectionProcessComplete() method.

- `void sendAndReceiveCAS(CAS aCAS)`: Send a CAS, wait for response. On success aCAS contains the analysis results. Throws an exception on error. Note that this interface cannot be used to interface to a CAS Multiplier service, because it will block until the parent comes back, and any child CASes will be ignored.

- `String aHandle deploy( String aDeploymentDescriptor, Map anApplicationContext)`: Deploys the UIMA-AS service specified by the given deployment descriptor in this JVM, and returns a handle for this service. The application context map must contain DD2SpringXsltFilePath and SaxonClasspath entries. This call blocks until the service is ready to process requests, or an exception occurs during deployment. If an exception occurs, the callback listener's

- `void undeploy(String aHandle)`: Tells the specified service to terminate. The handle is the same handle that is returned by the corresponding `deploy(...)` method.

- `void stop()`: Stops the asynchronous client. Removes the Cas pool, drops the connection to the UIMA-AS service queue and stops listening on its response queue. Terminates and undeploys any services which have been started with this client.

  This is an asynchronous call, and can be called at any time.

- `void stopProducingCases()`: Send stop signals for all CASes that are currently in process (where the API is expecting responses). If a CAS is a parent of child CASes being produced by a CAS Multiplier, this operation will also signal the CAS Multiplier to stop producing new CASes.

- `void stopProducingCases(String aCasReferenceId)`: send a stop request to a UIMA-AS Service for a particular CAS-id. If that CAS is a parent of child CASes being produced by a CAS Multiplier, this operation will also signal the CAS Multiplier to stop producing new CASes.

## Application Context Map

The application context map is used to pass initialization parameters. These parameters are itemized below.

- DD2SpringXsltFilePath: Required for deploying services.

- SaxonClasspath: Required for deploying services.

- ServerUri: Broker connector for service. Required for initialize.

- Endpoint: Service queue name. Required for initialize.

- Resource Manager: (Optional) a UIMA ResourceManager to use for the client.

- CasPoolSize: Size of Cas pool to create to send to specified service. Default = 1.

- CAS_INITIAL_HEAPSIZE: (Optional) the initial CAS heapsize, in 4-byte words. Default = 500,000.

- Application Name: optional name of the application using this API, for logging.

- Timeout: Process CAS timeout in ms. Default = no timeout.

- GetMetaTimeout: Initialize timeout in ms. Default = 60 seconds.

- CpcTimeout: Collection process complete timeout. Default = no timeout.

- SerializationStrategy:(Optional) xmi or binary serialization. Default = xmi

## Status Callback Listener

Asynchronous events are returned to applications via methods in classes registered to the Client API object with addStatusCallbackListener(). These classes must extend the class org.apache.uima.aae.client.UimaAsBaseCallbackListener.

- `initializationComplete(EntityProcessStatus aStatus)`: The callback used to inform the application that the initialization request has completed. On success aStatus will be null; on failure use the UimaASProcessStatus class to get the details.

- `entityProcessComplete(CAS aCas, EntityProcessStatus aStatus)`: The callback used to inform the application that a processCas request has completed. On success aCAS object will contain result of analysis; on failure the CAS will be in the same state as before it was sent to a service and aStatus will contain the cause of failure. When calling this method, the UIMA AS passes an object of type `UimaASProcessStatus` as a second argument. It extends `EntityProcessStatus` and provides `getCasReferenceId()` method to retrieve a unique id assigned to a CAS. To access this method the user code should implement the following

                     if ( aStatus instanceof UimaASProcessStatus ) {
      	              casReferenceId =
      	                ((UimaASProcessStatus)aStatus).getCasReferenceId();
         	           }

- `collectionProcessComplete(EntityProcessStatus aStatus)`: The callback used to inform the application that the CPC request has completed. On success aStatus will be null; on failure use the `UimaASProcessStatus` class to get the details.

- `onBeforeMessageSend(UimaASProcessStatus status)`: The callback used to inform the application that a CAS is about to be sent to a service. The status object has `getCasReferenceId()` method that returns a unique CAS id assigned by UIMA AS. This reference id may be used to associate arbirary information with a CAS, and is also returned in the callback listener as part of the status object.

## Error Results

Errors are delivered to the callback listeners as an `EntityProcessStatus` or `UimaASProcessStatus` object. These objects provide the methods:

- `isException()`: Indicates the error returned is in the form of exception messages.

- `getExceptions()`: Returns a List of exceptions.

## Asynchronous Client API Usage Scenarios

### Instantiating a Client API Object

A client API object must be instantiated for each remote service an application will directly connect with, and a listener class registered in order to process asynchronous events:

    //create Asynchronous Client API
    uimaAsEngine = new BaseUIMAAsynchronousEngine_impl();
    uimaAsEngine.addStatusCallbackListener(new MyStatusCallbackListener());

### Calling an Existing Service

The following code shows how to establish connection to an existing service:

    //create Map to pass server URI and Endpoint parameters
    Map<String,Object> appCtx = new HashMap<String,Object>();
    // Add Broker URI on local machine
    appCtx.put(UimaAsynchronousEngine.ServerUri, "tcp://localhost:61616");
    // Add Queue Name
    appCtx.put(UimaAsynchronousEngine.Endpoint, "RoomNumberAnnotatorQueue");
    // Add the Cas Pool Size
    appCtx.put(UimaAsynchronousEngine.CasPoolSize, 2);

    //initialize
    uimaAsEngine.initialize(appCtx);

Prepare a Cas and send it to the service:

    //get an empty CAS from the Cas pool
    CAS cas = uimaAsEngine.getCAS();
    // Initialize it with input data
    cas.setDocumentText("Some text to pass to this service.");
    // Send Cas to service for processing
    uimaAsEngine.sendCAS(cas);

### Retrieving Asynchronous Results

Asynchronous events resulting from the process Cas request are passed to the registered listener.

    // Callback Listener. Receives event notifications from UIMA-AS.
    class MyStatusCallbackListener implements UimaASStatusCallbackListener {

    // Method called when the processing of a Document is completed.
    public void entityProcessComplete(CAS aCas, EntityProcessStatus aStatus) {
      if (aStatus != null && aStatus.isException()) {
        List exceptions = aStatus.getExceptions();
        for (int i = 0; i < exceptions.size(); i++) {
          ((Throwable) exceptions.get(i)).printStackTrace();
          }
        uimaAsEngine.stop();
        return;
        }

    // Process the retrieved Cas here
    // ...
      }

    // Add other required callback methods below...
    }

### Deploying a Service with the Client API

Services can be deployed from a client object independently of any service connection. The main motivation for this feature is to be able to deploy a service, connect to it, and then remove the service when the application is done using it.

    // create Map to hold required parameters
    Map<String,Object> appCtx = new HashMap<String,Object>();
    appCtx.put(UimaAsynchronousEngine.DD2SpringXsltFilePath,
               System.getenv("UIMA_HOME") + "/bin/dd2spring.xsl");
    appCtx.put(UimaAsynchronousEngine.SaxonClasspath,
               "file:" + System.getenv("UIMA_HOME") + "/saxon/saxon8.jar");
    uimaAsEngine.deploy(service, appCtx);

## Undeploying a Service with the Client API

Services can be undeployed from a client object as follows:

    // create Map to hold required parameters
    Map<String,Object> appCtx = new HashMap<String,Object>();
    appCtx.put(UimaAsynchronousEngine.DD2SpringXsltFilePath,
               System.getenv("UIMA_HOME") + "/bin/dd2spring.xsl");
    appCtx.put(UimaAsynchronousEngine.SaxonClasspath,
               "file:" + System.getenv("UIMA_HOME") + "/saxon/saxon8.jar");
    String id = uimaAsEngine.deploy(service, appCtx);
    uimaAsEngine.undeploy(id);

## Recovering from broker failure

The Client API has a built in recovery strategy to handle cases where a broker fails or becomes unreachable, and then, later becomes available again.

Before sending a new request to a broker, the client checks the state of its connection. If the connection has failed, the client enters a loop where it will attemp to reconnect every 5 seconds. One message is logged to notify this is happening. The recovery attempt stops when the the connection is recovered, or when all UIMA AS clients that are sharing this failed connection, terminate.

During the recovery attempt, any CASes that are submitted via the client APIs will fail or timeout. If the application uses the sendAndReceive() synchronous API, the failure will be delivered by an exception. If the application client uses the sendCAS() asynchronous API, the failure will be delivered via the normal callback listener that the application registered with the UIMA AS client.

## Sample Code

See $UIMA_HOME/examples/src/org/apache/uima/examples/as/RunRemoteAsyncAE.java