An IMS Application Example Based on SIP Servlets and VoiceXML

AssistedCall

This is the central class for the application and contains the controlling logic and methods needed to complete the application's main flow. AssistedCall objects are stored in the SipApplicationSession. The application manages pairs of call-legs (where the application server is behaving as a B2BUA). Management of the call-leg pairs are encapsulated in implementations of the ITPCC interface, for example, CallerToVMSCall, CalleeToVMSCall, CallerCalleeTransfer, and CallerValedictionTransfer. The AssistedCall class implements the ITPCCEventListener interface, which allows it to receive callback notifications of the ongoing progress of call-leg pairs. If a caller or callee prematurely ends a call session, or for some reason a call cannot be connected, the AssistedCall implementation will receive these notifications and end the other calls gracefully.

AssistantService

This utility class provides methods for managing the instances of AssistedCall objects, network utility methods, and a SIP message header logging service. The AssistantService object can also be used to create a timer so calls can receive timeout notifications. This is achieved using the servlet container's TimerService. The AssistantTimerListener class is declared as the application timer listener in the sip.xml file.

ContactStoreDAO

This data access object wraps access to an SQL database for storing mappings from a person's public address (address of record) to their many device addresses (for example, their phone at home or at work). The ContactStoreDAO returns a value object called ContactStoreVO. A PointBase database is used with two tables called Greetings and Contacts. The Greetings table has two fields, aor_uri, which is the public address for a user with a configured Personal Assistant, and greeting, which is a blob containing the user's audio greeting. The Contacts table has a key aor_uri which it relates to its second field device_uri. The Contacts table holds a URI for each of the user's devices.

MRF control

Today, there are a number of different protocols all leveraging SIP for controlling media servers that have been published as IETF Internet Drafts. Popular examples, include the Media Sessions Markup Language (MSML), the Media Server Control Markup Language (MSCML), and the SIP Interface to VoiceXML Media Services (draft-burke-vxml). To make the implementation as general as possible, we have chosen to abstract the interface using the IMediaController and IMediaListener classes, part of the com.voxpilot.mediacontrol package. The IMediaController interface provides methods for starting and terminating a VoiceXML session. The IMediaListener interface is used to report events such as the VoiceXML session starting or terminating back to the SIP servlet application. Both of these interfaces should be considered "skeletons" that can be extended in the future to provide access to other functions on the media server, such as low-level commands to play announcements, collect input from the user, start conferences, and perform transcoding.

We have supplied a concrete implementation of IMediaController called SipVxmlMediaController, which encapsulates the standard approach for triggering VoiceXML application sessions described in RFC 4240 and draft-burke-vxml. Briefly, the MRF is invoked by sending a SIP INVITE to the MRF with a specially formatted SIP URI:

sip:dialog@192.168.1.1:5060;voicexml=http://example.com/start.vxml;aai=123

The user part is fixed at dialog to indicate a VoiceXML dialog service. The host and port refer to the host and port of the MRF. The voicexml parameter specifies the first VoiceXML page to execute and, in our case, will reference the application server. The aai parameter is used for application-to-application information that can be retrieved within the VoiceXML application from the session variable session.connection.aai. The aai parameter is useful for passing in extra information to the VoiceXML application. For the Personal Assistant application, we will pass in the SipApplicationSession ID and pass this ID back in each HTTP request for correlation purposes (more on this below).

The following code snippet shows how to create a media controller and create and connect a VoiceXML session:

// Create a media controller
IMediaController mediaController = MediaControllerFactory.create(
                 IMediaController.SIP_VXML_MEDIA_CONTROLLER,
                 sipFactory, sipAppSession);

// Register for events
mediaController.setMediaListener(this);

// Create a VoiceXML session
VxmlParam [] params = new VxmlParam[2];
params[0] = new VxmlParam(VxmlParam.VOICEXML, vxmlURL);
params[1] = new VxmlParam(VxmlParam.AAI, sipAppSession.getId());

mediaController.createVxmlSession(sipMrfUri, fromUri, toUri,
          sdpObject, sdpContentType,
          params);
// Connect
mediaController.connectVxmlSession();

/**
 The VoiceXML session can be started after the
 IMediaListener.vxmlSessionReady() event is received by calling
 the IMediaController.startVxmlSession() method.
**/

The IMediaController interface receives SIP messages (e.g. such as a 200 OK response from the MRF) via its doSipServletMessage(). In the Personal Assistant application, the AssistantSipServlet dispatches SIP messages to objects implementing the ISipDialog interface. We employ an adapter pattern using a class called SipMediaControllerAdapter which implements the ISipDialog interface and aggregates a reference to an IMediaController. This way, SIP messages are passed through to the IMediaController via its adapter.

Maintaining application state

On the SIP side, application state is maintained via the SipApplicationSession object, part of the standard SIP servlet package. A new SipApplicationSession object is created each time a new INVITE request is placed to the Personal Assistant application. The SipApplicationSession object in turn contains a reference to one or more SipSession objects - one for each SIP dialog. For the Personal Assistant application, we place the central AssistedCall object into the SipApplicationSession.

A key function required by a container supporting converged applications (such as one involving both the HTTP and SIP protocols) is the ability to provide some sort of correlation between a HTTP request and its corresponding SIP dialog. While the SIP servlet specification does mention that the SipApplicationSession object can aggregate HttpSession objects, the mechanism to create this relationship is not expanded upon. The upcoming WebLogic SIP Server 2.2 extends the SIP servlet API by introducing some useful methods for correlating HTTP and SIP sessions.

In the Personal Assistant application, we provide two static methods on the AssistantService class called addSipApplicationSessionReference() and findAssistedCall(). The former stores the SipApplicationSession into the servlet context keyed by its ID (obtained from SipApplicationSession.getId()). The latter retrieves the SipApplicationSession object given the ID.

Each time the MRF is invoked, the SipApplicationSession ID is passed into the VoiceXML application via the aai parameter. Each HTTP request back to the application server from the MRF includes this ID, therefore allowing the HTTP servlet to retrieve the corresponding SipApplicationSession and associated application state.

Call control

To decompose the application neatly, the Personal Assistant code encapsulates the call flow patterns between pairs of call legs in a specialized sub-class of ITPCC. There are a total of four classes:

  1. CallerToVMS: Connects the caller to the MRF to play a greeting message and record the caller's name.
  2. CalleeToVMS: Places an outbound call to the callee and connects the callee to the MRF to inform the callee a caller wishes to speak to him or her.
  3. CallerCalleeTransfer: Connects the caller to the callee.
  4. CallerValedictionTransfer: Connects the caller to the MRF to play a valediction message if the callee refuses to speak to the caller.

(VMS stands for VoiceXML Media Server and is another name for MRF).

The ITPCC interface is straightforward: The startCall() method commences the call flow pattern, and the endCall() method terminates the call-legs. The disconnectA() and disconnectB() methods can be used to tear down a particular call-leg.

Figure 6 illustrates the assistant.sip.tpcc package.

Figure 6

Figure 6. The assistant.sip.tpcc package (click the image for a full-size screen shot)

Oracle Chatbot
Disconnected