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:
-
CallerToVMS
: Connects the caller to the MRF to play a greeting message and record the caller's name. -
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. -
CallerCalleeTransfer
: Connects the caller to the callee. -
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. The
assistant.sip.tpcc
package (click the image for a full-size screen shot)