2 Programming Model

The state-machine formulation of ECharts makes it well-suited for back-to-back user agent applications. These applications generally need to manage the dialog state of two or more SIP dialogs, in addition to application state. As such, it is recommended that ECharts machines be used for such applications. However it is often convenient to compose such applications with proxy applications for such applications as routing. In Section  3 we discuss use of these proxy applications; however for the remainder of the document, B2BUA-style applications are assumed.

SIP Servlet applications make heavy use of sessions. A SipSession is used to maintain state related to a particular SIP dialog. A SipApplicationSession groups together related SipSessions as well as other application-level data.

ECharts for SIP Servlets exposes a different object model. Ports are used as entry and exit points for SIP messages between the application and a remote SIP entity, and as such subsume the role of the SipSession. A FeatureBox contains one or more ports, as well as a Machine that specifies the actions that should occur based on events processed by the ports, and therefore maps to a SipApplicationSession.

A special SipServlet subclass is provided as a gateway to ECharts. This servlet, EChartsSipServlet, does not need to be modified or subclassed by the application developer. The only piece that needs to be supplied is a custom ECharts Machine that creates the required ports and specifies the application logic. All other components are defined and supplied by the adaptation layer.

Note that this software does not currently allow for distributable servlet applications.

2.1 FeatureBox

The FeatureBox is a container for all components necessary for an application instance. In this context, application instance refers to a specific instantation and execution of the application logic in reaction to the environment -- simplistically, a single “call.” This is in contrast to a servlet instance, which may serve a large number of “calls.” See Section 2.6 of JSR 289 for further details.

A simple though common FeatureBox configuration is shown in Figure  1. The box includes two ports for SIP messaging, as well as a custom ECharts Machine specified in MyMachine. For the simple case of a back-to-back user agent (B2BUA), port1 could process messages from and to the calling party, and port2 could process messages for the called party.

Figure 1: A sample FeatureBox with two ports

When an initial request is received by the EChartsSipServlet, a new instance of a FeatureBox is created and the request passed to the box on a special port called the BoxPort, of which there is exactly one per box. The reason for this is explained in Section  2.2.2.

2.2 Ports

Ports are the mechanism by which messages are received and sent. The bulk of the work in an application is done by processing SIP messages on SipPorts. However there are three other important types of ports, BoxPort, TimerPort, and NonSipPort. All are described below.

2.2.1 SipPort

A SipPort is associated with a single SIP dialog (and hence a single SipSession as defined in JSR 289). Messages received on a dialog are processed by defining message transitions on the corresponding SipPort. For example, the transition below will fire if a 180 Ringing message is received on a port named callee.

// absorb a 180 Ringing message from callee
transition INVITE_SENT - callee ? ProvisionalResponse180

Messages to be sent on a SipPort must be created by calling a method on that port. This will ensure that the message is created on the appropriate SipSession to satisfy the requirements of JSR 289. In addition, they must be sent via the ECharts syntax shown below, instead of the message.send() syntax of the SIP Servlet API. For instance, to create and send a BYE message on a port named caller, the following ECharts action could be used:

caller ! caller.createRequest("BYE")

Recall that this is the ECharts syntax for:

send the message returned by caller.createRequest("BYE")
on the caller port.

The processes of creation and sending are separated, so that the message may be manipulated (or saved) after creation and before sending.

2.2.2 BoxPort

The BoxPort is a special port (one per box) that is used to handle initial requests. This allows the application the flexibility to decide which SipPort to associate with the request at execution time. This is a mandatory step, though the step may be embedded in a child machine, as it is in the B2BUA sample. In the code snippet below, a received INVITE is bound to the caller port; all subsequent messages on the corresponding SipSession will be associated with the caller port.

boxPort = box.getBoxPort();

transition INIT - boxPort ? Invite /
    savedInvite = message;

2.2.3 TimerPort

In addition to SIP messages, the other environmental event that can fire transitions is a timeout event, resulting in a timed transition. These delays are fired on anonymous TimerPorts. The following transition will fire after a specified ring-no-answer timeout:

transition RINGING - delay(ringNoAnswerTimeout) -> NO_ANSWER;

The programmer does not explicitly declare and reference TimerPort objects; using the delay syntax above causes the appropriate code to be generated to create and manage the required ports and messages.

In order to use timed transitions, the SIP application’s deployment descriptor must be configured correctly. See Section  5.2 for details.

2.2.4 NonSipPort

This class is included to enable the possibility of receiving messages from sources other than SIP messages. For instance, this mechanism could be used to support converged (SIP + HTTP ) applications.

2.3 Messages

The SipServlet API defines a base message interface (SipServletMessage) and two subinterfaces (SipServletRequest and SipServletResponse). These interfaces may be used in message transitions. However, for programmer convenience and expressive power, a number of additional message classes have been defined. So instead of writing:

transition CONNECTED
    - callee ? SipServletRequest [ message.getMethod().equals("BYE") ]

the more concise (and readable) form shown below can be used:

transition CONNECTED - callee ? Bye -> TEARDOWN;

E4SS wraps an incoming message in the most appropriate message class but stores messages in their unwrapped form. The additional E4SS message classes are shown in Table  1, as well as the interface that each implements. The Response class has a subclass hierarchy which can be inferred from the class names.

Message Class Implemented Interface

Invite SipServletRequest

Response SipServletResponse

Table 1: Additional Message Classes

For messages that are sent rather than received, an appropriate createXXX method must be called on a SipPort to ensure that the message is part of the correct SipSession. The methods include createInvite, createRequest, and createResponse. See the javadoc for details.

2.4 Automatic Termination Handling

Telecommunications applications have widely divergent purposes, but all such applications must eventually terminate their calls. In SIP, the message used to terminate a call depends on the state of the SIP dialog. Depending on the state, a call might be properly terminated by sending BYE, sending CANCEL, or sending an error response to a received INVITE, and the resulting messages that are expected in response vary in each case. To support this universal need while reducing application complexity, we have used ECharts for SIP Servlets to create a framework to automatically terminate calls under certain conditions.

If an application uses a standard B2BUA pattern (one call in, one call out, and either party may end the call), then the B2BUA software supplied in the open-source distribution will handle termination just like any other mid-call request, by simply relaying a received BYE message to the other party, and then relaying the received response. However more complex applications may involve more than two parties or may need to terminate calls based on an application-specific trigger. To support these cases, we have created a framework for automatic termination handling. This behavior applies only to INVITE dialogs.

The termination handling framework tracks the INVITE dialog state of each SipPort in a FeatureBox. Updates to the state are made automatically based on SIP messages sent and received by the application. If a trigger condition occurs indicating the need to terminate the application, then a new termination machine is dynamically created for each port, all executing concurrently to tear down all the application’s calls according to the SIP protocol. This port termination machine can also be used explicitly in an application to selectively tear down the calls on one or more ports.

There are three conditions which trigger automatic termination behavior:

  1. Application-initiated teardown - This condition is used when the application wishes to initiate teardown on its own. This could occur, for example, when a prepaid calling card balance goes to zero. This condition is initiated when the application machine transitions to a terminal state, i.e., a simple state with no outgoing transitions.

    /** end call if timer expires
    transition IN_PROGRESS -
        delay( maxTime )
    -> END;

    /** terminal state
    state END;
    Tying termination behavior to a terminal state is reasonable, as the application has declared that it has no further actions to take.
  2. Unhandled teardown message - Another condition which results in automatic termination behavior is the receipt of a message that indicates that a call should be torn down (BYE or CANCEL), in the case where the application does not specify how to handle such a message. This frees the application developer from having to specify how to properly tear down each call at each point in the application. If the application should not terminate when it receives a teardown signal (such as in a sequence calling application), the developer is free to specify other (non-default) behavior.
  3. Exception handling - The final condition under which automatic termination behavior is initiated is in the event of a programming error (specifically an uncaught exception). We chose this because there is no general way to judge the programmer’s intent, and it is prudent in this case to clean up the calls to release resources.