Writing UAS Applications

A UAS application is an application that is loaded into and run by a UAS. It is simply a Java class packaged in a jar file that references com.aculab.uasapi and adheres to a few rules.

Each time a UAS application is run it instantiates a class that handles a single primary call and optionally a small number of secondary extra call channels. It is presented with the call channel objects that control these calls.

While a call is answered various speech processing features can be performed on it. Once the call is hung up the application should return. The overall result of the application run is returned to the UAS and logged.

See Class Diagram.

Note: Applications currently need to be built using JDK 1.7.

Application Rules

A UAS application is a Java class that adheres to certain programming rules and is packaged in a jar file together with a manifest.

UAS Application File Specification:
  • The name of the jar file (without extension) is the application name.
  • The jar file MUST include a manifest file containing headers: Implementation-Title and Implementation-Version
  • The jar file MUST include an application class with the same name as the jar file
  • The application class MUST inherit from either UASInboundApplication or UASOutboundApplication.
  • The application class MUST override the abstract run method that returns an integer indicating the overall success of the application invocation.
  • The application class may provide a default constructor.
  • The application class may provide implementations for the static methods onLoad and onUnload methods.
The entry point for application is the run method. Its prototype is:

Inbound:
@Override
public int run(UASCallChannel channel, String applicationParameters)


Outbound:
@Override
public int run(UASCallChannel channel, 
                        String applicationParameters, String outboundParameters)


Where:
  • channel - the primary call channel.
  • applicationParameters - a string containing parameters associated with the application (defined in the Inbound or Outbound Service Registration pages on cloud.aculab.com).
  • outboundParameters - a string containing parameters related to a specific invocation of the application. Typically this includes the outgoing call destination address.

Return Code:

The return code should adhere to the following restrictions:
  • 0 or any positive integer: the application passed. The exact meaning is application specific
  • -1 to -99: the application failed, reserved for UAS use
    • -1: the application instance has thrown an exception
    • -2: the incoming call was hung up before the application instance was invoked
    • -3: the specified application is not currently loaded
    • -4: the specified application cannot be run
    • -5: the application instance returned a reserved value
    • -6: the specified application is Inbound, but the call is outbound (or vice versa)
    • -7: the UAS is not currently accepting new application requests
    • -9: UAS error while processing application request
  • any other negative integer: the application failed. The exact meaning is application specific

Creating a UAS Application

The install package contains two java source files that can be used as a starting point for developing new UAS applications (see [UASInstallDir]/Templates/). Also included are example manifest files and Ant build script files.

Here are step-by-step instructions for creating UAS applications using Eclipse and NetBeans.

Using Eclipse to Create UAS Applications

This explains how to create a Java UAS Application project in the Eclipse IDE and packaging it in a jar file using Eclipse or Ant.

Creating a UAS Application Project in Eclipse

  • Create an empty project:
    Select New - Java Project and in the Create a Java Project dialog
    create a folder for the project and a project name.
    (note: JRE must be 1.7) Select Next.

    In the Libraries tab: Select Add External JARs...
    Browse to [UASInstallDir]/Repository/ and select uasapi-release.jar.

    Select Finish.
  • Add the class and manifest files:
    In Package Explorer right-click on your new project
    Select Import - General - File System and browse to [UASInstallDir]/Templates/NewInboundApp (or NewOutboundApp). Enable the folder's tick box. Select Finish.

    Your project should now also contain NewInboundApp.java (or NewOutboundApp.java), MANIFEST.MF and build.xml files.
  • Refactor for your application name:
    Refactor - rename the NewInboundApp or NewOutboundApp class to the name of your application. (switch on "Update fully qualified names in non-java text files") to ensure the manifest file is also updated.

    You can also change the containing package name (default: com.customer.uas.app).

Using Eclipse to build the application jar file

  • Specify the Jar file name and contents:
    In Package Explorer right-click on your application project
    Select Export - Java - JAR file, untick the .classpath, .project, build.xml and MANIFEST.MF files and set the export destination to your application name with ".jar" extension in the bin folder of your project.
    Select Next until the JAR Manifest Specification dialog is displayed.
  • Include the Manifest file in the Jar file:
    Specify Use existing manifest from workspace and browse to the MANIFEST.MF file in your project.
    Select Finish.

Using Ant to build the application jar file

  • Create an Ant Builder:
    In Package Explorer right-click on your application project
    Select Properties - Builders - New - Ant Builder and give the builder a name
    Set the Buildfile - Browse Workspace to your project and select the build.xml file
  • Selecting Build Project on your application will now also create a jar file in the bin folder.

Using NetBeans to Create UAS Applications

This explains how to create a Java UAS Application project in the NetBeans IDE.

  • Create an empty project:
    Select New Project and select Java Class Library.
    Create a location for the project and a project name.
    Finish.
  • Add the api library:
    Right-click on the Libraries folder of the newly-created project, select Add JAR/Folder and browse to [UASInstallDir]/Repository/ and select uasapi-release.jar.
  • Add a source package, the java source file and manifest:
    Right-click on the Source Packages folder and select New - Java Package.
    Enter the package name com.customer.uas.app and select Finish.
    From File Explorer copy one of the two template app java files (NewInboundApp.java or NewOutboundApp.java) and paste it into your new source package in NetBeans.
    From File Explorer copy the MANIFEST.MF file and paste it into the root folder of your project in NetBeans.
    Edit the /nbproject/project.properties file of your project and add the line manifest.file=MANIFEST.MF
  • Refactor for your application name:
    Refactor - rename the NewInboundApp or NewOutboundApp class to the name of your application. (you will need to edit the manifest file manually to reflect this change)

    You can also change the containing package name (default: com.customer.uas.app).

The Manifest File

The MANIFEST.MF file is included in the UAS application jar file to allow the UAS to control whether/how the application is loaded.
The UAS expects two standard headers to be set:

e.g.
Implementation-Title: com.acompany.uas.app.AUASInboundApp
Implementation-Version: 1.0.1

If these are not set in the manifest file or the Implementation-Title name does not match the class name the UAS will not load the application.

UASApplication Base Class

All UAS applications inherit from UASApplication. This class returns a property via method getApplicationInstanceId() that uniquely identifies a particular application instance within the Aculab Cloud (this id is included in diagnostic trace for each instance).

It returns properties from the methods getFileManager() and getTrace() that provide media file and diagnostic tracing.

It also returns fax documents via getFaxToSend() and getFaxToReceive() that are used when sending or receiving faxes. Note: These last two properties will be null unless Fax Sending or Fax Receiving has been enabled within the service invoking a particular application instance.


Inbound/Outbound Applications

A UAS application is either inbound or outbound. This refers to the direction of the primary call to be managed by the application.

An inbound application inherits from UASInboundApplication and is passed a call channel on which an incoming call has been detected and is waiting to be answered (or rejected).

An outbound application inherits from UASOutboundApplication and is passed a call channel on which no call is yet active. The application is expected to make an outbound call attempt within a reasonably short time.


com.aculab.uasapi

The UAS API library is based on the AMS Client API which provides powerful media processing and call control features via a high-level client API. It allows incoming and outgoing telephone calls to be controlled and connected together, wav file playback, wav file record, DTMF playback and detection.

The UAS API library (uasapi-x.x.x-release.jar) is installed in the <UAS install folder>\Repository folder. Applications require this to be specified on their classpath in their manifest file. The supplied sample UAS applications expect the global Classpath Variable UASAPI_LIB to be configured to point to this jar file.

UAS applications need to import classes from com.aculab.uasapi and com.aculab.amsapi (the underlying layer).

These assemblies contain classes that control individual telephone calls and associated media processes that interact with the call. For example, to play a wav file to the call or record the call to file.

This is a synchronous API. Methods that interract with the Aculab Cloud send a message and wait for a response before returning control to the application.

Application Initialisation

The Java UAS supports one-time initialisation of an application so that any static resources that an application may use can be initialised. To achieve this a UAS application may optionally implement one or both of the following methods in its application class:
  • public static int onLoad();
  • public static void onUnload();
The onLoad method is called while an application is being loaded. It should return zero or a positive value to indicate success or a negative value to cancel the loading operation.
The onUnload method is called by the UAS when an application is unloaded.

Default Constructor

When a UAS is requested to run a loaded UAS application it instantiates the application's class and then calls the run method. A default constuctor can optionally be implemented. This will be called immediately before run is called.

Diagnostic Trace

The property returned by getTrace() of the UASApplication class provides the ability to log diagnostic trace to file. The amount of trace is determined by the TraceLevel configuration setting for the UAS (the default level is Error). This can be altered via the UAS Management Console, from where log files can also be downloaded for viewing.

The log files are written to a location specified by the TraceLocation configuration UAS setting. This defaults to Logs under the installation directory. The TraceFileMaxKilobytes and TraceFileCount configuration settings also affect how large the log files are and how many are written.
(see Using The UAS for information on configuration)

By default, up to 50 log files are written of 2MB maximum size. e.g. UAS_0000.log - UAS_0049.log

In addition to the trace files, Application Data Records (ADRs) are written to the same location as the diagnostic trace files. These have the form UAS_ADR_0000.log.


Debugging

TODO!!!



Extra Channels

An application is always supplied with a single primary channel. It is possible to configure a service to use additional secondary (extra) channels to make outgoing calls which can, for instance, then be connected to the primary call.

When registering an application via Inbound Services or Outbound Services, the number of extra channels required can be defined. An application can access the resultant call channels via the getExtraChannels() method of the UASCallChannel class.

For example
UASCallChannel otherChannel = channel.getExtraChannels()[0];
if (otherChannel.call(otherDestinationAddress) == CallState.Answered)
{
    // Connect the calls together
    channel.connect(otherChannel);
}


Text To Speech (TTS)

The Aculab Cloud supports Text To Speech (TTS), allowing quick and easy application prototyping and, more generally, the ability for your applications to read text to their users.

SSML
The TTS methods support the embedding of Speech Synthesis Markup Language (SSML). This is a very flexible way of expressing how you would like your text to be spoken. Further details on the SSML features and voices supported are available in the Aculab Cloud TTS documentation.

In brief, SSML allows you to do things like:

channel.getFilePlayer().say(
    "<prosody rate='x-slow'>
        This is me speaking slowly.
     </prosody>");

channel.getFilePlayer().say(
    "<prosody pitch='+50%'>
        This is me speaking with a very high pitch.
     </prosody>");


Media Files

Media files to be played to a call or recorded from a call reside on the cloud.

An application can perform some simple manipulation of these files via getFileManager() of UASApplication.

This class enables the application to rename and delete files and check for their existence.

For example:
if (this.getFileManager().exists(recordFileName))
{
    this.getFileManager().deleteFile(recordFileName);
}

Additionally, the Aculab Cloud provides Web Services access to Media Files that can be used to download, list upload, rename or delete files. See the ManageMediaFiles in Web Service API Application Samples

The Aculab Cloud accesses media files via a highly reliable, distributed storage system. The way it works will generally be transparent to you as an application writer, but there are a few important things to bear in mind:
  • When recording a new file within a telephone call, it will be available for use immediately in that call, and within 60 seconds in other calls, from cloud.aculab.com and from the Web Services API.
  • When writing a new file via cloud.aculab.com or Web Services API, it will be available for access within 60 seconds from the completion of the write.
  • When deleting or overwriting an existing file, the change will take up to 15 minutes to become available across the distributed storage system. Until this has been accomplished some telephone calls may continue to access the old version.
  • While these levels of delay may be fine when you've just pushed a new version of your 'Hello caller, what would you like to do?' wav file onto the Cloud via the Cloud Web Portal or Web Services API, it may not be so helpful when you've just recorded a voicemail and the intended recipient dials into the system soon after to receive it. So, for cases where you need to access new media files quickly, please make them new files by using new filenames.
  • It's generally a good idea to organise your media files into directories. The directory delimiter is "/", as in "intro/hello.wav". Directories are created and destroyed on demand by the storage system so, assuming that the 'intro' directory doesn't exist, channel.FileRecorder.record("intro/hello.wav") would create that directory and file_man.delete_file("intro/hello.wav") would remove it along with the file.

Call Transfer

The ability to transfer a call is provided in the form of retrievable transfer. This re-routes the audio to another destination but maintains control over the call, allowing it to be retrieved.

This kind of transfer requires an Extra Channel in the Idle state to be supplied.

The Transfer method makes a call to a supplied target address and transfers the audio connection from the call making the transfer to that target. However it retains control of the lifetime of the call (this is known as Third Party Call Control in the SIP world).

This means that the audio connection can be retrieved at a later time. Additionally, the audio connection returns to the original call automatically, if the target of the transfer hangs up.

Retrievable transfer is often used in scenarios where an operator creates a call that connects two participants together and in conferencing.

Conferencing

The Aculab Cloud supports conferencing together two or more calls (parties) within an entity called a conference room for which you provide an identifying name. Within each conference room a conference entity controls the audio mix that each party hears. A number of options can be set for each party as they enter the conference room (these are shown here in green).


Conference Room

A conference room is Created when the first party enters, either by transferring an existing call or by placing a new call to that room. See: UASCallChannel.

For example:
channel.transferToConference(
    "BobsConferenceRoom",
    channel.getExtraChannels()[0]);

A conference room is Destroyed if a party exits that is configured to Destroy On Exit or if the last remaining party exits. When a conference room is destroyed any remaining parties are automatically hung up. A party exits a conference room either by hanging up or by retrieving its call. See: UASCallChannel.

For example:
channel.hangUp();
or
CallState state = channel.retrieveTransferredCall();
if (state == CallState.Answered)
{
    // Call has been retrieved from conference
    ...
}
  • Although the name chosen for a conference room is unique to your user account, you are strongly advised to provide a different name for each one. (This avoids various potential issues, for example, where a caller stays in one conference longer than expected and is suddenly introduced into a supposedly new conference as new callers ring in)
  • When transferring into a room, a Ringback Tone can be configured to play while the call is ringing
  • When a call is hung up it exits the conference room
  • A party can be configured to exit the room when they hit a specified keypad digit (Exit On DTMF Digit)
  • A party may be a human or an application, such as one that records a conference room

Conference

Each conference room contains a single conference that has two states: Started and Stopped.
It is initially in the Stopped state.

While Started all parties in the conference room can either listen or talk and listen.

While Stopped the parties cannot hear each other. Each party may have an On Conference Stopped File configured to play in this state. An example would be holding music.

A conference Starts:
When a conference room contains at least two parties, at least one talker and at least one party configured to Start On Entry.

A conference Stops:
When the number of parties goes below two or if there are no talkers left in the room. Any parties remaining in the Stopped conference will hear their On Conference Stopped File if configured


Conference Party Options

When entering a conference room, a number of options determine how the party affects the conference and how the conference interacts with the party:

Party Type The type of participation this party can take in the conference - TalkerAndListener or Listener
Lifetime Control The control that this party has on the lifetime of the conference and conference room. One of:
None The party does not directly affect the lifetime of the conference
Start On Entry The conference may start once this party has entered the conference room
Destroy On Exit The conference stops and the conference room is destroyed when this party exits
Full Control The conference may start once this party has entered the conference room and the conference room will be destroyed when this party exits
Ringback Tone The ring tone, if any, that's heard by this party while the call to the room is ringing
On Conference Stopped File An optional name of a media file that is played to the party (looping) while the conference is stopped
Beep On Entry Whether a beep is played to the room when this party enters a started conference
Exit On DTMF Digit A DTMF digit that, if provided, allows this party to exit the conference room by pressing the specified digit (e.g. '*')
Mute On DTMF Digits An optional pair of DTMF digits that allow the party's audio contribution to the conference to be muted and unmuted when one of the specified digits is hit
On Entry Media An optional name of a media file or some TTS that is played to a started conference immediately before the party enters it (and after the Beep On Entry, if configured)
On Exit Media An optional name of a media file or some TTS that is played to a started conference immediately after the party exits it
Prefix Media An optional name of a media file or some TTS that is played to a started conference immediately before the On Entry Media or On Exit Media

For example:
// Create media settings to specify all options
ConferencePartyMediaSettings settings = 
    new ConferencePartyMediaSettings(
        channel.getPredefinedToneSequences().get(PredefinedTones.RingbackUnitedKingdom),
        "onHoldMusic.wav",                        // file to play when stopped
        true,                                     // beep enabled
        '#',                                      // exit on a #
        new ConferencePartyMuteDigits('1', '2'),  // Mute on 1, unmute on 2
        PlayableMedia.textToSay("Bob "),          // Prefix
        PlayableMedia.textToSay("has entered the conference"),
        PlayableMedia.textToSay("has exited the conference"));

See: ConferencePartyType, ConferenceLifetimeControl, ConferencePartyMediaSettings, ConferencePartyMuteDigits.

Fax

Sending and receiving faxes on a call is supported on the Aculab Cloud and needs to be enabled via the Enable fax send and/or Enable fax receive options in the Fax tab of the invoking Inbound or Outbound service. Typically faxes are only sent on outgoing calls and received on incoming calls. Note that enabling either of these will result in the application being charged at a different platform rate.

Sending a Fax

Enabling fax send in a service creates an empty FaxToSend fax document object as a property of the invoked UASApplication. The content of this object can be built up from TIFF files that have been stored on the cloud and text can be added to selected pages. It can be sent using the FaxSender property of a call channel.

For example:
 this.getFaxToSend().setContent("myFaxTxDocument.tif");
 channel.getFaxSender().send(this.getFaxToSend());


Also see the OutboundSendFax application sample.

Receiving a Fax

Enabling fax receive in a service creates a FaxToReceive fax document object as a property of the invoked UASApplication. The filename of this object must be set, then a fax can be received using the FaxReceiver property of a call channel.

For example:
 this.getFaxToReceive().setFileName("receivedFax1.tif");
 channel.getFaxReceiver().receive(this.getFaxToReceive());


Also see the InboundReceiveFax application sample.

Advanced Fax Options

Typically faxes can be sent and received without recourse to changing the Fax Options. However, where circumstances require it, the default fax options for a service can be altered from the Fax tab of Inbound Services and Outbound Services.
These defaults may be overriden per application instance via the FaxOptions class that can be passed to the FaxSender or FaxReceiver. Option names and value ranges must match those available on the Service Fax tab.

See Sending & Receiving Faxes for more information.

High Level API

This release contains a BETA release of a high level api that provides additional high level functionality on top of the standard UASCallChannel.

The CallChannelHelper class has methods that implement typical operations such as capturing a number from the caller, playing a menu to the caller and making an outgoing call and connecting it immediately to an existing call.

For example:
 CallChannelHelper channelHelper = new CallChannelHelper(this, channel);
 channel.getFaxReceiver().receive(this.getFaxToReceive());
 
 // Establish a target address
 CallTarget callTarget = CallTarget.sipAddressToCall(
    "sip:bob@acompany.com", "sip:bill@anothercompany.com");
 channelHelper.callAndConnect(callTarget,
    channel.getExtraChannels()[0], 120);