The purpose of this document is to discuss the Unified REST RA framework Demonstration.

Audience

This document is for developers and solution architects working with REST APIs on the Metaswitch Rhino TAS or Sentinel platforms.

Contents

Introduction

An increasing number of services provide an HTTP-based REST API as their main interface to other network elements. The Rhino REST API Framework provide tools to allow Rhino developers to easily add support for REST APIs in their applications.

Objectives of the REST Demonstration

The focus of the Rhino REST API Framework Demonstration is to meet the following objectives:

  • Demonstrate how a REST API (generated with the REST RA framework) is used in a Rhino TAS based application

  • Demonstrate integration of REST APIs with Sentinel

  • Demonstrate how a Call Notification REST API can be used to influence call processing

The REST Demonstration emulates a real-world architecture with an application based on Sentinel-SIP that uses the prototype MetaSwitch Call Notification REST API during SIP call processing.

REST Demonstration Scenario (Real-World Architecture)

Architecture Description
Real World Scenario

The MMTel-AS receives SIP triggers from the S-CSCF. The MMTel-AS, acting as an MSW-CN client, sends notification requests to the Business Logic node.

The Business Logic node is an MSW-CN server that receives notifications from the MMTel-AS.

The Business Logic node will return instructions in response to Call Direction requests that the MMTel-AS will follow in subsequent processing of SIP signaling.

REST Demonstration Architecture that uses the Metaswitch Call Notification API

The following architecture for the REST Demonstration has been chosen because:

  • Sentinel-SIP implements the required SIP B2BUA behavior

  • Existing SIP features can be used

  • Demonstrates the integration of REST APIs with sentinel platforms

REST Demonstration Architecture Description
REST Demonstration Setup

A set of features and a session plan that uses sentinel-sip.

  • A session plan for the REST Demonstration

  • A feature that sets the session plan in the SentinelSelectionKey for all triggers

  • A feature that uses the Metaswitch Call Notification API

  • Feature execution scripts that are associated with the new session plan

  • Use existing sentinel-sip features (for example B2BUA)

Tip

See Sentinel Overview and Concepts for an overview of the sentinel platform used in the REST Demonstration. See Sentinel Express for sentinel platform documentation.

Rhino REST API Framework

Historically, applications running on Rhino TAS, including Sentinel VoLTE, have had to support these APIs by using the HTTP Resource Adaptor, and manually constructing the appropriate HTTP messages, which is time-consuming and prone to errors.

Alternatively, it is possible to write a custom Rhino resource adaptor (RA) that presents Rhino applications with a clean Java interface to a REST API. Writing a custom resource adaptor is a lot of work that must be repeated for each REST API that is to be supported. The result of this approach is a proliferation of RAs that are very similar, if each RA only supports one API.

The Rhino REST API Framework provides a better method for supporting REST APIs in Rhino by:

  • Providing a toolchain that can process a number of OpenAPI documents, and produce a fully functional REST Resource Adaptor supporting all of those APIs, ready for use in Rhino services.

  • Supporting OpenAPI v2 and OpenAPI v3 documents.

  • Providing a mechanism for supporting existing REST APIs that may not have an OpenAPI definition, such as OMA Call Notification.

  • Providing a mechanism for supporting REST APIs that use externally-defined types such as XML Schema.

Tip See: Rhino REST API Framework for an overview of the Unified REST RA Framework.

OpenAPI

The OpenAPI Specification has emerged as the industry standard for describing REST APIs, and this has enabled many tools for generating code and other resources. The problem of "how can my application talk to or implement this REST API" has largely been solved — tools such as openapi-generator can generate client and server code for many languages and frameworks, so getting up and running with a new REST API is a simpler task.

Tip See: Introduction to REST APIs and OpenAPI for an overview of OpenAPI.

Demo Architecture and Implementation

The REST Demonstration includes the components described below.

Metaswitch Call Notification REST API

The Rhino REST API Framework was used to create the MSW-CN REST API implementation (Creating a REST API). The generated module structure is:

Generated MSW-CN module structure
msw-call-notification-rest-api 1
msw-call-notification-rest-api/msw-call-notification-api/src/api.yaml 2
msw-call-notification-rest-api/oma-call-notification-api/src/api.properties 3
msw-call-notification-rest-api/msw-call-notification-api 4
msw-call-notification-rest-api/msw-call-notification-api-ratype 5
msw-call-notification-rest-api/msw-call-notification-api-plugin 6
msw-call-notification-rest-api/msw-call-notification-api-client-sbbpart 7
  1. The MSW-CN group module

  2. The MSW-call-notification-api sub-module holds the API specification and a properties file. The other sub-modules have an ivy dependency on this module.

  3. An OpenAPI formatted description of the MSW-CN API

  4. A properties file that defines java packages, and the names of deployable units

  5. Generates a RA type that includes events, provider interfaces and so on for a MSW-CN RA Type that can be deployed in Rhino

  6. Generates a library component the Unified REST RA uses to load and drive the MSW-CN API

  7. Generates a Jar containing a super-class that developers extend to create an sbb-part, for the client MSW-CN role, in their application. The generated super-class includes all event handlers and slee-annotations required to deploy such an sbb-part in Rhino

Tip Developers may create multiple REST API modules. A REST RA can support one or more such REST APIs.

Call Notification Resource Adaptor

The Rhino REST API Framework was used to create the Call Notification Resource Adaptor (Creating a REST Resource Adaptor). The REST Demonstration REST resource adaptor module is: call-notification-ra

The call-notification-ra is an Ant/Ivy build module that depends on the Unified REST RA Core and the Metaswitch Call Notification REST API. The call-notification-ra produces a Unified REST RA, that supports MSW-CN, used in Sentinel-SIP.

Tip Developers may create multiple REST RA modules, each with different sets of APIs to suit different applications.

Call Notification Client Event Handler

The MSW-CN event handler sbbpart (rest-demo-feature-modules/msw-call-notification-client-event-handler) contains an sbb-part that extends the class generated in msw-call-notification-rest-api/msw-call-notification-api-client-sbbpart. The MswCallNotificationClientEventHandlerSbbpart class extends the MswCallNotificationApiClientSbbpart class and implements behaviour on receipt of call direction and call event notification response events.

The super class includes the required SBBPart, RATypeBinding and EventMethod slee annotations.

The MswCallNotificationClientEventHandlerSbbpart event handler sbb-part
package com.opencloud.openapi.demo;

// msw call notification API
import com.opencloud.openapi.demo.msw_cn.api.*;

// super-class for an sbb-part to process
// msw call notification related response events
import com.opencloud.openapi.demo.msw_cn.impl.*;

// to bind the event handler sbbpart to the sentinel-sip service
@BinderTargets(services="sip")
public class MswCallNotificationClientEventHandlerSbbpart extends MswCallNotificationClientSbbpart {

    public MswCallNotificationClientEventHandlerSbbpart(SbbPartContext context) { super(context); }

    @Override 1
    protected void handleCallDirectionNotification_200_SuccessResponse(
            CallDirectionNotification_200_SuccessResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallDirectionNotification", response, response.getStatusCode(),
            "SuccessResponse", aci, eventContext);
    }

    @Override 2
    protected void handleCallDirectionNotificationDefaultResponse(
            CallDirectionNotificationDefaultResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallDirectionNotification", response, response.getStatusCode(),
            "DefaultResponse", aci, eventContext);
    }

    @Override 3
    protected void handleCallEventNotification_200_SuccessResponse(
            CallEventNotification_200_SuccessResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallEventNotification", response, response.getStatusCode(),
            "SuccessResponse", aci, eventContext);
    }

    @Override 4
    protected void handleCallEventNotificationDefaultResponse(
            CallEventNotificationDefaultResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallEventNotification", response, response.getStatusCode(),
            "DefaultResponse", aci, eventContext);
    }

    // Submit response event to sentinel runtime
    // for delivery to a feature for processing
    private void submitResponseEventToSentinel(
            String operation, Object response, int statusCode,
            String description,
            ActivityContextInterface aci, EventContext eventContext)
    {
        final SentinelActivityContextInterface sentinelAci =
            getSbbPartContext().asSbbPartActivityContextInterface(aci);

        final String target = sentinelAci.getTargetFeature();  5
        if (target == null || target.isEmpty()) {
            getTracer().fine("No target feature for response, ignore");
            return;
        }

        getTracer().finer("Pass {} {} {} to target feature \"{}\"",
            operation, statusCode, description, target);
        try {
            SentinelEndpoint sentinelEndpoint =
                (SentinelEndpoint) getSbbPartContext().getSbbLocalObject();
            sentinelEndpoint.processEvent(
                    target, response, false, true, aci, eventContext); 6
        }
        catch (SentinelFireEventException | IllegalArgumentException | NullPointerException e)
        {
            getTracer().severe("Exception processing {} {} {} event",
                operation, statusCode, description, e);
        }
    }
}
  1. override method in MswCallNotificationClientSbbpart to handle a CallDirectionNotification 200 Success response

  2. override method in MswCallNotificationClientSbbpart to handle a CallDirectionNotification default response (i.e all other CallDirectionNotification responses)

  3. override method in MswCallNotificationClientSbbpart to handle a CallEventNotification 200 Success response

  4. override method in MswCallNotificationClientSbbpart to handle a CallEventNotification default response (i.e all other CallEventNotification responses)

  5. find the feature the response event should be processed by

  6. submit the event to the Sentinel-SIP runtime for processing by the target feature

Set Session Plan Feature

The RestSetSessionPlan feature (rest-demo-feature-modules/msw-set-session-plan-feature) updates the sentinel selection key PlanID field with the REST Demonstration session plan ID.

Tip Learn about the Sentinel Selection Key and read Features for an introduction to Sentinel features.
The RestSetSessionPlan feature
package com.opencloud.openapi.demo;

@SentinelFeature(
    featureName = RestSetSessionPlan.NAME,
    componentName = "@component.name@",
    featureVendor = "@component.vendor@",
    featureVersion = "@component.version@",
    featureGroup = SentinelFeature.SIP_FEATURE_GROUP,
    executionPhases = ExecutionPhase.SipSessionPhase,
    usageStatistics = RestSetSessionPlanStats.class
)
// to bind the feature to the sentinel-sip service
@BinderTargets(services = "sip")
public class RestSetSessionPlan
        extends BaseFeature<SentinelSipSessionState, SentinelSipMultiLegFeatureEndpoint>
        implements InjectFeatureStats<RestSetSessionPlanStats>
{
    public static final String NAME = "RestSetSessionPlan";

    // ...

    @Override
    public void startFeature(Object trigger, Object activity, ActivityContextInterface aci) {
        final SentinelSelectionKey selectionKey = getSessionState().getSentinelSelectionKey();
        // Set the plan ID in the selectionKey to 'rest_demo'
        selectionKey.setPlanId(REST_DEMO_SESSION_PLAN);
        getTracer().finest("SentinelSelectionKey set: {}", selectionKey);
        getSessionState().setSentinelSelectionKey(selectionKey);
        getCaller().featureHasFinished();
    }

    private static final String REST_DEMO_SESSION_PLAN = "rest_demo";
}

The value of the SentinelSelectionKey is used by the Sentinel run-time to resolve feature configuration, and the feature execution scripts that run.

Note There are many alternate approaches to deriving a plan ID. For example the plan ID could be derived by analysing headers in the SIP INVITE.
Tip Session plans, the Sentinel-SIP session plan and feature execution scripts are explained here: Session Plans, SIP Sessions Initiated via SIP INVITE and Feature Execution Scripts.

Send Call Notification Feature

The RestSendCallNotification feature (rest-demo-feature-modules/msw-send-call-notification) sends CallDirection and CallEvent notification requests and processes the corresponding responses. The RestSendCallNotification feature:

  • determines the type of notification event to send, if appropriate, based on the point-in-session and the triggering SIP event.

  • will influence the current SIP session based on CallDirection response events. For example the session may be ended, routed to an alternate destination or allowed to continue.

startFeature method of the RestSendCallNotification feature
@Override
public void startFeature(Object trigger,
                         Object activity,
                         ActivityContextInterface aci)
{
    // Find the active point-in-session 1
    final SipFeatureScriptExecutionPoint executionPoint =
        getSessionState().getCurrentSipFeatureExecutionPoint();

    if (trigger instanceof IncomingSipRequest
        || trigger instanceof IncomingSipResponse)
    {
        // Determine the notification type and call event 2
        final CallNotificationTypes notificationType;
        final CallEvents callEvent;
        if (trigger instanceof IncomingSipRequest) {
            final IncomingSipRequest request = (IncomingSipRequest) trigger;
            notificationType = determineNotificationType(
                                    executionPoint, request);
            callEvent = determineCallEvent(executionPoint, request);
        }
        else {
            final IncomingSipResponse response  = (IncomingSipResponse) trigger;
            notificationType = determineNotificationType(
                                    executionPoint, response);
            callEvent = determineCallEvent(executionPoint, response);
        }

        if (null == notificationType || null == callEvent) {
            getCaller().featureHasFinished();
            return;
        }

        final LegManager lm = getCaller().getLegManager();
        final Leg calledPartyLeg = lm.getCallingPartyLeg().getLinkedLeg();

        // if there is no linked leg, then this response will have been received
        // on a leg associated with an MRF (for an announcement).
        // A notification is not required in this case.
        if (null == calledPartyLeg) {
            final Leg leg = getCaller().getLegManager().getLeg(aci);
            getTracer().finest("A notification is not required at: {} for response on leg {}",
                                    executionPoint, leg.getLegName());
            getCaller().featureHasFinished();
            return;
        }

        // send a notification 3
        // if the leg is not active, a sip session has not been created yet
        // if there is no INVITE request, then an INVITE has not
        // been sent on the cld leg yet
        if (calledPartyLeg.isActive()
                && null != calledPartyLeg.getInviteRequest())
        {
            sendNotification(calledPartyLeg, notificationType, callEvent);
        }
        else {
            sendNotification(aci, notificationType, callEvent);
        }
    }
    else {
        // Process a notification response event 4
        if (processNotificationResponse(
                executionPoint, trigger, activity, aci))
        {
            getCaller().featureHasFinished();
        }
        getCaller().detach(aci);
    }
}
  1. Find the currently execution feature script execution point.

  2. Determine the notification type and call event based on the triggering SIP message, and the point-in-session

  3. Send a notification request message

  4. Process a CallDirection or CallEvent notification response event.

Determining the MSW-CN notification event to send

// Determine the notification type from the triggering SIP request 1
private CallNotificationTypes determineNotificationType(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipRequest request)
{
    switch (executionPoint) {
        // the initial trigger
        case SipAccess_SessionStart:
            return CallNotificationTypes.CALLDIRECTION;
        // one of the participants hangs up
        case SipMidSession_PartyRequest:
            return "BYE".equals(request.getMethod())
                        ? CallNotificationTypes.CALLDIRECTION : null;
        // a notification is not required
        default: return null;
    }
}

// Determine the notification type from the triggering SIP response 2
private CallNotificationTypes determineNotificationType(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipResponse response)
{
    switch (executionPoint) {
        case SipAccess_PartyResponse:
            if (! "INVITE".equals(response.getRequest().getMethod()))
                return null;
            switch (response.getStatus()) {
                case SipResponse.SC_OK:
                    return CallNotificationTypes.CALLEVENT;
                // 486 - called party busy
                case SipResponse.SC_BUSY_HERE:
                // 404 - no route to destination
                case SipResponse.SC_NOT_FOUND:
                // 408 - no answer
                case SipResponse.SC_REQUEST_TIMEOUT:
                // 480 - no answer
                case SipResponse.SC_TEMPORARLY_UNAVAILABLE:
                    return CallNotificationTypes.CALLDIRECTION;
                // a notification is not required
                default: return null;
            }

        case SipMidSession_PartyResponse:
            if (SipResponse.SC_OK == response.getStatus()
                && "BYE".equals(response.getRequest().getMethod()))
                return CallNotificationTypes.CALLEVENT;
            // otherwise a notification is not required
            return null;

        // a notification is not required
        default: return null;
    }
}
  1. At SipAccess_SessionStart Sentinel-SIP is processing a triggering INVITE request, so send a CallNotificationTypes.CALLDIRECTION request. At SipMidSession_PartyRequest we are interested in BYE requests and send a CallNotificationTypes.CALLDIRECTION request.

  2. At SipAccess_PartyResponse we are interested in responses to the triggering INVITE request. On answer, send a CallNotificationTypes.CALLEVENT request, otherwise send a CallNotificationTypes.CALLDIRECTION request. At SipMidSession_PartyResponse we are interested in responses to BYE requests and send a CallNotificationTypes.CALLEVENT request.

// Determine the CallEvent from the triggering SIP request 1
private CallEvents determineCallEvent(
                        SipFeatureScriptExecutionPoint executionPoint,
                        IncomingSipRequest request)
{
    switch (executionPoint) {
        // the initial trigger
        case SipAccess_SessionStart:
            return CallEvents.CALLEDNUMBER;
        // one of the participants hangs up
        case SipMidSession_PartyRequest:
            return "BYE".equals(request.getMethod())
                        ? CallEvents.DISCONNECTED : null;
        // a notification is not required
        default: return null;
    }
}

// Determine the CallEvent from the triggering SIP response 2
private CallEvents determineCallEvent(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipResponse response)
{
    switch (executionPoint) {
        case SipAccess_PartyResponse:
            if (! "INVITE".equals(response.getRequest().getMethod()))
                return null;
            switch (response.getStatus()) {
                case SipResponse.SC_OK:
                    return CallEvents.ANSWER;
                // 486 - called party busy
                case SipResponse.SC_BUSY_HERE:
                    return CallEvents.BUSY;
                // 404 - no route to destination
                case SipResponse.SC_NOT_FOUND:
                    return CallEvents.NOTREACHABLE;
                // 408 - no answer
                case SipResponse.SC_REQUEST_TIMEOUT:
                // 480 - no answer
                case SipResponse.SC_TEMPORARLY_UNAVAILABLE:
                    return CallEvents.NOANSWER;
                // a notification is not required
                default: return null;
            }
        case SipMidSession_PartyResponse:
            if (SipResponse.SC_OK == response.getStatus()
                && "BYE".equals(response.getRequest().getMethod()))
                return CallEvents.DISCONNECTED;
            // otherwise a notification is not required
            return null;

        // a notification is not required
        default: return null;
    }
}
  1. At SipAccess_SessionStart Sentinel-SIP is processing a triggering INVITE request (a CallEvents.CALLEDNUMBER event). At SipMidSession_PartyRequest we are interested in BYE requests (CallEvents.DISCONNECTED events)

  2. At SipAccess_PartyResponse we are interested in responses to the triggering INVITE request (CallEvents.ANSWER, CallEvents.BUSY and so on). At SipMidSession_PartyResponse we are interested in responses to BYE requests (CallEvents.DISCONNECTED events).

Sending CallDirection and CallEvent notification requests

The RestSendCallNotification feature sends CallDirection and CallEvent notification requests in the following way.

sendNotification private method of the RestSendCallNotification feature
// Send a call event notification
private void sendNotification(
        String from, String fromDisplayName, String to,
        CallNotificationTypes notificationType, CallEvents callEvent)
{
    final SendCallNotificationConfig configuration =
            configurationReader.getConfiguration(
                    getSessionState().getSentinelSelectionKey());

    final ApiConfiguration apiConfiguration =
            ApiConfiguration.standardConfiguration();
    apiConfiguration.set(ApiOptions.destinationUrl,
                         configuration.getDestinationUrl());
    final MswCallNotificationRaTypeApiClient client =
            cnProvider.getApiClient(apiConfiguration);

    // Create a new CallEventNotification object 1
    final CallEventNotification notification = new CallEventNotification();
    try {
        notification.notificationType(notificationType)
            .eventDescription(new EventDescription().callEvent(callEvent))
            .calledParticipant(new URI(to))
            .callingParticipant(new URI(from))
            .callingParticipantName(fromDisplayName);
    }
    catch (URISyntaxException e) {
        getTracer().severe(
            "Failed to extract participants from: {}, to: {}", from, to, e);
    }

    // Create a REST request to send 2
    final NotificationApi nApi = client.getNotificationApi();
    final RestRequestBuilder notificationRequest =
            notificationType == CallNotificationTypes.CALLEVENT
            ? nApi.createCallEventNotificationRequest(notification)
            : nApi.createCallDirectionNotificationRequest(notification);

    try {
        // Send the REST request 3
        final OutgoingRestActivity outgoingRestActivity =
            notificationApi.sendRequest(notificationRequest);
        final ActivityContextInterface newAci =
            cnACIFactory.getActivityContextInterface(outgoingRestActivity);
        final SentinelActivityContextInterface outgoingAci =
            getCaller().asSentinelActivityContextInterface(newAci);
        stats.incrementSendCallNotification(1);

        // Set the target feature to 'RestSendCallNotification' 4
        outgoingAci.setTargetFeature(NAME);
        // Attach to the new activity, wait for the response 5
        outgoingAci.attach(getFacilities().getSbbLocalObject());
        getTracer().finest("Notification sent. Waiting for response");
        getCaller().featureWaiting(outgoingAci);
    }
    catch (IOException e) {
        getCaller().featureFailedToExecute(
                new FeatureError(FeatureError.Cause.unclassified,
                    "Failed to send notification", e));
    }
}
  1. The CallEventNotification has:

    • a notification type (CallNotificationTypes.CALLEVENT or CallNotificationTypes.CALLDIRECTION)

    • a call event (CallEvents.CALLEDNUMBER, CallEvents.ANSWER and so on)

    • called participant (the To address), calling participant (the From address) and the calling participant display name

  2. Create an appropriate REST request message by using the MSW-CN NotificationApi.

  3. Send the REST request. The Unified REST RA returns an OutgoingRestActivity related to the sent REST request.

  4. Set TargetFeature on the ActivityContextInterface to RestSendCallNotification so Sentinel-SIP will route the corresponding response to the currently executing feature.

  5. Attach to the activity and tell Sentinel-SIP that we are waiting for a response. Sentinel-SIP pauses executing the active feature execution script until a response event arrives.

Processing CallDirection 200 Success response events

A CallDirectionNotification success response includes instructions that must be followed to effect ongoing call processing.

private boolean processDirectionNotificationSuccessResponse(
            SipFeatureScriptExecutionPoint executionPoint,
            CallDirectionNotification_200_SuccessResponse trigger,
            Object activity)
{
    // Determine the action to perform 1
    final Action actionToTake = trigger.getContent();
    if (null == actionToTake || null == actionToTake.getActionToPerform()) {
        stats.incrementCallDirectionResponseWithNoActionToTake(1);
        return true;
    }

    // Apply the requested Action 2
    switch(actionToTake.getActionToPerform()) {
        case ROUTE:
            // Get the new destination 3
            final URI newDestination  = actionToTake.getRoutingAddress();
            if (null == newDestination) {
                stats.incrementCallDirectionNoRoutingAddress(1);
                return true;
            }
            final org.jainslee.resources.sip.URI sipDestination =
                getSipUri(newDestination);
            if (null == sipDestination) {
                stats.incrementCallDirectionNonSipRoutingAddress(1);
                return true;
            }
            stats.incrementCallDirectionRoute(1);
            // route the call to the new destination 4
            routeToNewDestination(executionPoint, sipDestination);
            return true;

        case CONTINUE:
            // Allow the call to continue without interruption 5
            stats.incrementCallDirectionContinue(1);
            return true;

        case ENDCALL:
            // End the call 6
            stats.incrementCallDirectionEndCall(1);

            // Check to see if an annoucement should be played 7
            final String announcementIdStr = actionToTake.getAnnouncementId();
            if (null == announcementIdStr) {
                getTracer().fine("Action is to ENDCALL");
                // defer to the leg manager to end the session
                final LegManager lm = getCaller().getLegManager();
                lm.endSession(SipResponse.SC_FORBIDDEN);
            }
            else {
                // Schedule an announcement 8
                try {
                    // announcement ID should be a Long.
                    final long announcementId = Long.parseLong(announcementIdStr);
                    getTracer().fine("Action is to ENDCALL after playing announcement: {}",
                                        announcementIdStr);
                    // tell sentinel-sip about the announcement to be played
                    final List<SipAnnouncementInformation> announcementQueue =
                                getSessionState().getEarlyMediaAnnouncementInfoQueue();
                    announcementQueue.add(SipAnnouncementInformation.getBuilder()
                                .setAnnouncementId(announcementId).build());
                    getSessionState().setEarlyMediaAnnouncementInfoQueue(announcementQueue);
                    // tell sentinel-sip to end the session after the announcement
                    getSessionState().setEndSessionAfterAnnouncement(SipResponse.SC_FORBIDDEN);
                    // used in rest-demo feature execution scripts
                    getSessionState().setPlayAnnouncementThenEndSession(true);
                }
                catch (NumberFormatException nfe) {
                    getTracer().fine("Announcement id: {} is not a valid. Ending the call.", announcementIdStr);
                    // defer to the leg manager to end the session
                    final LegManager lm = getCaller().getLegManager();
                    lm.endSession(SipResponse.SC_FORBIDDEN);
                }
            }
            // generate a DISCONNECTED notification 9
            sendNotification(
                    lm.getCallingPartyLeg(),
                    CallNotificationTypes.CALLEVENT,
                    CallEvents.DISCONNECTED);
            return false; // sent a notification, feature has not finished
    }
    return true;
}
  1. The CallDirectionNotification 200 success response body contains an Action object that dictates how the active SIP session should be processed.

  2. The ActionToPerform is one of ROUTE, CONTINUE or ENDCALL

  3. If the action to perform is ROUTE the actionToTake will also include a new destination address.

  4. Route the call to the new adddress.

  5. If the action to perform is CONTINUE allow the call to continue uninterrupted.

  6. If the action to perform is ENDCALL delegate to Sentinel-SIP.

  7. The actionToTake may, optionally, include an announcement ID

  8. If actionToTake includes an announcement ID, then update session state, so the PlayAnnouncement feature can play the announcement

  9. Generate a DISCONNECTED notification

Tip Learn more about the SIP Play Announcement Feature.

Feature Execution Scripts

The RestSetSessionPlan feature (rest-demo-feature-modules/msw-set-session-plan-feature) updates the sentinel selection key to with the REST Demonstration session plan. Sentinel-SIP uses the sentinel-selection-key when resolving the feature execution scripts to run at each point in session.

Tip For more details on the sentinel selection key, feature execution scripts and Sentinel-SIP feature script execution points see: Sentinel Selection Key Feature Execution Scripts and Sentinel SIP Feature Execution Points

The following feature execution scripts have been added for the REST Demonstration.

Point-in-Session feature Execution Script

SipAccess_SessionStart:::

featurescript RestSessionStart {
    if not LegManager.endSession
       and not LegManager.detachAll
       and not LegManager.currentLeg.releaseLeg
    {
        run AcceptSip
        run RestSetSessionPlan
        run DoNotChargeSipSession
    }
}

post:SipAccess_SessionStart:sipcall:rest_demo:

featurescript DemoPostSessionStart {
    if not LegManager.endSession {
        run RecordTimestamps mode "outbound"
        run RestSendCallNotification
        if session.PlayAnnouncementThenEndSession {
            run SipPlayAnnouncement
        }
    }
    run DetermineCauseCode
    run DiameterServiceInfo
    run DiameterPerLegInfo mode "outbound"
}

SipAccess_SubscriberCheck:sipcall:rest_demo:

featurescript RestDemoSessionCheck {
    if not session.PlayAnnouncementThenEndSession {
        run SipSubscriberDetermination
        run CallPartiesAddressDetermination
        run SessionTracing
    }
}

SipAccess_PartyResponse:sipcall:rest_demo:

featurescript RestDemoSubscriberCheck {
    if not session.PlayAnnouncementThenEndSession {
        run SubscriberDataLookup
        run SubscriberValidity
    }
}

SipAccess_PartyRequest:sipcall:rest_demo:

featurescript RestSipAccessPartyRequest {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipAccess_PartyResponse:sipcall:rest_demo:

featurescript RestSipAccessPartyResponse {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipMidSession_PartyRequest:sipcall:rest_demo:

featurescript DemoMidSessionPartyRequest {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipMidSession_PartyResponse:sipcall:rest_demo:

featurescript DemoMidSessionPartyResponse {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

Introduction to Metaswitch Call Notification

The Metaswitch Call Notification API is a prototype call notification API based on a subset of the OMA Call Notification API. In particular the Metaswitch Call Notification API implements call event and call direction notifications with a minor addition to support annoucements.

Tip See: {introduction-to-rest-and-openapi} for an introduction to REST (REpresentational State Transfer) APIs.

Capabilities of the Call Notification API

The SIP AS (MSW-CN client), in the following diagram, receives a SIP message from the S-CSCF and determines if a call event or call direction should be generated (in terms of call leg, point-in-call and so on), The MSW-CN server sends a notification request to an MSW-CN server if a call event or call direction is required.

MSW-CN client
Implementation of the client role of the MSW-CN API

MSW-CN server
Implementation of the server role of the MSW-CN API

Notification
An API request, made from the MSW-CN API client to an MSW-CN API server, containing a notification of a call/session related event (for a particular call/session)

MSW-CN API

There are two types of notification made by an MSW-CN client to an MSW-CN server.

  • Call Event: For call event notifications

    • An MSW-CN client notifies an MSW-CN server of events on a call/session.
      Call processing continues.
      This is analogous to an SSF sending event-reports, armed with notify-&-continue, to an SCF

  • Call Direction: For call direction notifications, where the client waits for instructions in the notification response.

    • An MSW-CN client notifies an MSW-CN server of events on a call/session.
      The MSW-CN client blocks ongoing call processing and waits for instructions from the MSW-CN server (in the response to the notification).
      This is analogous to an SSF sending event-reports, that had been armed with suspend, to an SCF where the BCSM blocks waiting-for-instructions.

The {arch-rest-api-framework } generates an OpenAPI specification description of MSW-CN into Resource Adaptor Type that Rhino applications use.

Tip The API definition is: msw-call-notification-rest-api/msw-call-notification-api/src/api.yaml.

The following sections illustrate aspects of the MSW-CN API and how it is realized in Java va the REST API framework.

Call Event and Call Direction Notifications

The YAML description below shows that an HTTP POST request, with a request URL including /callevent/notification corresponds to a call event notification. The body of the request is a CallEventNotification JSON object. A 200 OK response indicates the request was received. Any other response corresponds to an error.

YAML for Call Event Notification
/callevent/notification:
post:
  summary: A new Call Event notification
  operationId: callEventNotification
  tags:
    - notification
  requestBody:
    required: true
    description: call event notification
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CallEventNotification"
  responses:
    '204':
      description: | The 204 No Content HTTP status code is returned if the data was received successfully
    default:
      description: unexpected error

The YAML description below shows that an HTTP POST request, with a request URL including /calldirection/notification corresponds to a call direction notification. The body of the request is a CallEventNotification JSON object. A 200 OK response indicates the request was received successfully. The body of the response is a Action JSON object. Any other response corresponds to an error.

YAML for Call Direction Notification
/calldirection/notification:
post:
  summary: A new Call Direction notification
  operationId: callDirectionNotification
  tags:
    - notification
  requestBody:
    required: true
    description: call direction notification
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/CallEventNotification"
  responses:
    '200':
      description: | The 200 OK HTTP status code is returned if the data was received successfully. The response body contains the request 'Action to take'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Action'
    default:
      description: unexpected error

The REST API framework generates a number of Java interfaces, enums and classes from the YAML specification. For example, an application uses the NotificationApi below to create Call Direction and Call Event notification requests.

Code Generated NotificationApi
package com.opencloud.openapi.demo.msw_cn.api;

import com.opencloud.slee.rest.common.*;
import com.opencloud.openapi.demo.msw_cn.model.*;

public interface NotificationApi {
    // Create a RestRequestBuilder for request: CallDirectionNotification
    RestRequestBuilder createCallDirectionNotificationRequest(
          CallEventNotification callEventNotification);

    // Create a RestRequestBuilder for request: CallEventNotification
    RestRequestBuilder createCallEventNotificationRequest(
          CallEventNotification callEventNotification);

    // Send a request
    OutgoingRestActivity sendRequest(
          RestRequestBuilder requestBuilder) throws IOException;
}
Tip

A concrete example showing how the MSW-CN API can be used can be seen in rest-demo-feature-modules/msw-send-call-notification/

See: Sending CallDirection and CallEvent notification requests for a code excerpt showing sending a Call Direction request

When Notifications can be Sent

Notifications are sent at certain points in the basic call model. These are:

Point-in-Call Calling/Called Notification Type Description

CALLEDNUMBER

Calling

Call Direction and Call Event

A call session between two parties, a calling participant and a called participant (called number) is being attempted

BUSY

Called

Call Direction and Call Event

Called party is busy

NOTREACHABLE

Called

Call Direction and Call Event

Called party is not reachable

NOANSWER

Called

Call Direction and Call Event

Called party doesn’t answer

ANSWER

Called

Call Event

Called Participant has confirmed (answered) the call

DISCONNECTED

Calling + Called

Call Direction and Call Event

Called (or calling) party disconnected

YAML CallEvents Enum
CallEvents:
  type: string
  enum:
    - Busy
    - NotReachable
    - NoAnswer
    - CalledNumber
    - Answer
    - Disconnected
YAML CallNotificationTypes Enum
CallNotificationTypes:
  type: string
  enum:
    - CallEvent
    - CallDirection
YAML AddressDirection Enum
AddressDirection:
  type: string
  enum:
    - Calling
    - Called

Actions Available in a Call Direction Response

A Call Direction response contains an Action object in the response body. The action describes the actionToPerform.

Action Description

ROUTE

Request to (re-)route the call to the address indicated with routingAddress

CONTINUE

Request to continue the call without any changes. This will result in normal handling of the event in the network

ENDCALL

Request to end the call. This will result in termination of the call. An action may optionally include an annoucementId. Sentinel will play the announcement to the calling party before ending the call.

YAML Action Component
Action:
  required:
    - actionToPerform
  properties:
    actionToPerform:
      $ref: '#/components/schemas/ActionValues'
    routingAddress:
      type: string
      format: uri
    announcementId:
      type: string
YAML ActionValues Enum
ActionValues:
  type: string
  enum:
    - Route
    - Continue
    - EndCall

Demo Installation and Setup

Prerequisites

The REST Demo requires a Linux system with OpenJDK 11. The JAVA_HOME shell environment variable should be set to the base directory of the OpenJDK installation.

The REST Demo is deployed on Rhino TAS. The REST Demo installer can install Rhino automatically (recommended), or you can install it manually. If installing Rhino manually, use Rhino SDK 3.0.0.0 or later.

Important The REST Demo requires access to Metaswitch Artifactory servers. Please contact Metaswitch for a username and password.

Licensing

A license file is required to run Rhino, the Sentinel SIP components and the Metaswitch Call Notification API used in the demo.

The REST demo package includes a license file (rest-demo-license.license) that is valid for two months from the release of the REST demo.

Important Contact Metaswitch if you require a new license file.

If you allow the installer to install Rhino automatically, it will prompt you for the location of the license file.

If you prefer to set up Rhino manually, then you need to install the license file prior to installing the demo.

Installation

Prepare the REST Demo package

Important The REST Demo requires access to Metaswitch Artifactory servers. Please contact Metaswitch for a username and password.

The REST demo package contains an installer program that guides you through the installation process, and downloads the other components needed to run the demo.

After downloading, extract the REST Demo zip file. This will create the rest-demo-sdk directory. In this directory there is an installer that guides you through the following steps:

  1. You may optionally take the demo offline. The installer will download all additional components so that installation can continue later without accessing the network.

  2. You can choose to install the Rhino TAS SDK, if you don’t have it already.

  3. Source code for the REST Demo modules is extracted and built.

  4. Finally the REST Demo components are deployed and configured in the Rhino TAS instance specified in (2).

Tip The installer captures full logging from the various tools that it uses, and writes these logs into the rest-demo-sdk/build/target/log directory. This can be helpful when debugging issues.
Note Before installing, if the host requires a proxy to access Artifactory then it must be configured in sdk.properties. sdk.properties can be found in the top-level directory of the unzipped package. Find the section marked with # Proxy settings and change it to the following:
# Proxy settings
#
sdk.http.proxyHost=<proxy hostname here>
sdk.http.proxyPort=<proxy port here>
sdk.https.proxyHost<proxy hostname here>
sdk.https.proxyPort=<proxy port here>
#
# These properties are used for both http and https.
sdk.http.nonProxyHosts=localhost|127.0.0.1

Install the REST Demo modules

1

Start the installer

Run the installer command in the build/bin directory of the extracted REST Demo SDK.

$ cd rest-demo-sdk
$ build/bin/installer

The installer first initialises its environment, showing output similar to the following:

Initialising the SDK ...
Retrieving Installer dependencies ... done.

The installer may prompt for Artifactory credentials, which should have been supplied to you by Metaswitch.

2

Prompting for information

The installer will prompt the user for some configuration values. A value inside square brackets, if present, is the default answer for that question. Pressing the Enter key without entering any value will use the default value. If the default isn’t present, the prompt will be repeated. In subsequent runs of the installer, the defaults will reflect the values that the user has previously entered.

Explanations of all of the questions the installer will ask are laid out over the next few steps. Note that some of the questions will only appear under certain circumstances, so not all of them will be seen in a given installer run.

3

Taking the SDK offline

The installer asks if the SDK should be taken offline.

You can optionally take the SDK offline by creating a local repository. This will take several minutes depending on connection speed, but will make subsequent retrievals much faster and remove the need for an internet connection.
Do you want to take the SDK offline? y/[N] >

If the user presses the Enter key then the default of N is applied by the installer. This means that the SDK remains online, and will connect to the Metaswitch Ivy repositories on an as-needs basis. Answering yes will create a local Ivy repository that includes all of the remote artifacts required to build the SDK.

The user is then presented with progress information related to the downloading of artifacts necessary to take the SDK offline. This process can take around 10 minutes.

4

Basic SDK Questions

The values provided in this step are used in SLEE component identifiers and Ivy artifacts created when the REST Demo modules are built below.

Your organization's name, e.g. Rocket Communications Inc.
sdk.component.vendor [UNSET] >

This value will be used for the vendor portion of the SLEE Component ID for all SLEE components published by the SDK.

sdk.component.version [1.0] >

This value will be used for the version portion of the SLEE Component ID for all SLEE components published by the SDK.

The name of the platform operator, e.g. Rocket.
sdk.platform.operator.name [UNSET] >

The name of the platform operator for the system. It is used extensively throughout configuration profiles.

An Ivy organization field, recommended lower case with no whitespace e.g. "rocket".
sdk.ivy.org [UNSET] >

This value is used as the org value for all Ivy artifacts created by the SDK.

sdk.ivy.publish.revision [1.0.0] >

This value is used as the base of the revision value for all Ivy artifacts created by the SDK. Additional letters and numbers will be appended to it to identify specific releases, snapshots and milestones when an artifact is actually published.

5

Installing Rhino

You can either have the installer set up a Rhino SDK for you or point it at an existing Rhino installation, SDK or production.
Note: If you want to use an existing Rhino installation it has to be running and a proper license has to be installed when finishing the installation after the configuration. Also make sure that you have adjusted the memory settings and created a tcapsim-gt-table.txt file as detailed in the documentation.
Set up a Rhino SDK installation automatically? y/[N] >

If the installer needs to set up a new Rhino SDK installation, it will prompt for a license file.

Enter the path to your Rhino license file > /home/restdemo/rest-demo-sdk/opencloud.license

The Rhino TAS SDK will be installed and started in the background.

If the user instructs the installer to use an existing Rhino TAS, the path to the Rhino client directory must be entered.

Enter the path to your Rhino client directory > /home/restdemo/RhinoSDK

If the Rhino TAS installation is a production version, then additional information is required to complete configuration.

You can either have the installer deploy against Rhino SDK or production.
Does the specified client point to a production installation? y/[N] >

If Yes is selected, the installer prompts for details of the cluster nodes and hosts. Otherwise, the user enters the host that Rhino TAS SDK is installed on.

The host your Rhino SDK is installed on.
Rhino host [localhost] >

6

Review settings

Once the basic SDK configuration questions have been answered, the user is provided the opportunity to review, and if happy, accept the settings.

Tip Settings are saved to disk, so that they can be read later.
Review settings
***************


Basic SDK properties
====================

  sdk.component.vendor: Rocket Communications Inc
  sdk.component.version: 1.0
  sdk.platform.operator.name: Rocket
  sdk.ivy.org: rocket
  sdk.ivy.publish.revision: 1.0.0


Rhino settings
==============

  Deploy Rhino: Yes
  License location: /home/restdemo/rest-demo-sdk/opencloud.license

Accept these values? [Y]/n > y

Updating file sdk.properties
Updating file sdk.properties

Configuration changes written.

If the user presses the n key then the questions are asked again. Note that the list of configuration files that have been modified are printed out by the configuration portion.

7

Create deployment modules

Next, the installer prompts the user to create and build the REST demo modules. These include Sentinel-SIP features and REST API RA Types used by those features to communicate with an external API server.

This creates the rest-demo-modules directory and builds the demo modules, ready for deployment in Rhino. This must be done the first time the installer runs. If this step has already been done in a previous installer run, it can be safely skipped.

Create REST Demo Modules
========================


You can optionally create & build the demo REST RA and Call Notification API feature modules. This will create the "rest-demo-modules" directory and build the modules it contains. These modules will be deployed automatically by the installer. If the "rest-demo-modules" directory already exists, it will be deleted before being recreated and rebuilt.
Delete & rebuild REST demo modules? [Y]/n >

The user is then asked to confirm:

Review settings
***************


Create REST Demo Modules
========================

  Create & build REST demo modules: Yes

Accept these values? [Y]/n > y

Building the modules will take a few minutes.

Creating module rest-demo-modules ...done.
Building REST Demo modules; this is going to take a while ...
done.

Configuration changes written.
Creating deployment module deploy-rest-demo ...done.

8

Deployment

The installer can now deploy the REST Demo features into Rhino. It will prompt the user to confirm whether to proceed with the installation:

Install now? [Y]/n >

Installation can be deferred to a later time by entering n here. The installer will exit, saving the settings that were entered. These settings will be used automatically on the next installer run.

Otherwise, the installer proceeds with deploying the REST Demo modules into Rhino. This may take around 10-15 minutes.

Installing Rhino ...done.
Starting Rhino in the background ...done.


Installing List CDRs tool ...done.
Publishing deployment module deploy-rest-demo ...done.
Deploying; this is going to take a while ...done.
Binding; this is going to take a while ...done.
Configuring; this is going to take a while ...done.

Installation completed successfully in 11 minutes and 14 seconds. Rhino has been left running to finish applying configuration changes.

The configuration has been saved to the file /home/restdemo/rest-demo-sdk/install.properties. This file can be used to re-run the installation non-interactively with the same settings.

The installation is now complete.

When the installer completes, the Rhino TAS will be running with Sentinel SIP and the call notification features described in Demo Architecture and Implementation.

Directory Structure

The contents of the rest-demo-sdk directory are as follows:

Name Description
build/

Directory containing the installer and Ant scripts for building and deploying SDK components.

rest-demo-modules/

Directory containing:

  • API modules for generating SLEE RA Types based on an OpenAPI specification of an example "Call Notification" API (based on the OMA Call Notification API)

  • An RA module that generates the implementation of a SLEE Resource Adaptor supporting the demo APIs

  • Sentinel-SIP feature modules that make use of the demo Call Notification API to direct SIP calls

test-suite/

Directory containing a test suite that simulates some example SIP call scenarios, showing the demo features making REST calls to a simulated HTTP server. See [running-the-test-suite] below.

run-tests

A script that runs the tests in the test-suite directory.

rhino-sdk/

Directory containing the Rhino TAS SDK that was created by the installer (if requested).

build.xml, sdk.properties, deps.properties

Ant build and properties files for building the project. Should not need to be modified.

ivy.properties, release.properties

Dynamically-generated properties files for resolving the various components deployed by the demo. Automatically recreated when building or deploying REST demo modules.

Running the Test Suite

The test-suite directory contains integration tests that demonstrate various scenarios using the demo Call Notification API. The test scenarios simulate a SIP node (e.g. an S-CSCF) sending calls to Rhino, and an HTTP server that responds to the REST requests sent by the Sentinel-SIP features running in Rhino.

To run the tests, first run the installer as above, then run the run-tests script. This runs all scenarios in the test suite, and logs the call flows as they are performed.

See Demo Call Flow Scenarios for more information on the test scenarios.

Demo Call Flow Scenarios

This section describes the call flow scenarios provided with the REST API Framework demonstration, and provides instructions for running the scenarios and analysing the results.

Rest Demo Test Suite

The REST Demonstration includes a test-suite at: test-suite.

Test suite structure

The most important files and directories in the test suite are:

test-suite/build.xml 1
test-suite/config 2
test-suite/log4j.properties 3
test-suite/scenarios 4
test-suite/target 5
  1. apache ant build file for running integration tests

  2. directory containing configuration files for interfaces and scenario simulators used in the test suite

  3. log4j properties files that dictates the logging generated by the test suite and scenario simulators. This does not have any impact on the tracing levels of Rhino (i.e the system under test)

  4. directory containing a sub-directory per test scenario

  5. directory that contains sub-directories such as results, which the output from each scenario is collated

Anatomy of a call flow scenario

The scenarios in the test suite are all contained in sub-directories of scenarios.

scenarios/call-notification/cn01001-successful-call-no-cn
scenarios/call-notification/cn01003-successful-call-direction-continue
scenarios/call-notification/cn01004-successful-call-direction-endcall
...

Each scenario directory contains up to three files:

  • scscf.scen — A scenario simulator script that defines the expected signalling between all the roles in the scenario. In the case of the REST demo, the roles include the SCSCF, Sentinel/Rhino TAS and the Call Notification server.

  • sas.yaml — the expected Service Assurance Server (SAS) event messages during the scenario

  • usage.yaml — the expected statistics that will be raised during the call flow

Note Learn more about the Scenario Simulator and Scenario Editor.

How to execute scenarios

There three methods you may follow to run scenarios:

  1. Run all tests by using the run-tests script from the root of the rest-demo installation:

    rest-demo$ ./run-tests
  2. Run all tests by using apache ant in the testsuite directory:

    rest-demo/test-suite $ ../build/bin/ant auto run-integration-tests
  3. Run a single test by using apache ant in the testsuite directory:

    rest-demo/test-suite $ ../build/bin/ant auto choose

    A file-chooser will open, which allows you to select one of the tests in the test suite to execute.
    choose test a
    choose test b

Reviewing the test logs

The outcome of each scenario appears in complementary directories in targets/results. Each scenario results directory contains up to three files:

  • stu.log — records the output from the test runner, including the messages sent/to from protocol simulators, statistics collected and sas events analysed.

  • localhost-101-rhino.log — records the output from the Rhino TAS as the scenario executes. The log will include output from the SIP SIS, Rhino, the call notification resource adaptor and the Sentinel-SIP service.

  • sas-sim.log — records output from the SAS simulator, which includes details of all marker and event messages received from Rhino whilst the scenario executes.

Scenarios

Each scenario may include the following roles:

  • SCSCF — SIP signalling between the SCSCF and a SIP AS, which in this case is Sentinel SIP on the Rhino TAS

  • MRF — SIP signalling between the Rhino TAS and a MRF (for announcements)

  • Sentinel — Sentinel SIP running on the Rhino TAS, including the REST Demo features and the REST Demo call notification resource adaptor.

  • CN-Server — HTTP signalling between Sentinel and an external Call Notification server. The CN server responds to call notification direction and event REST requests and answers with call notification direction and event REST responses.

Note

Some SIP signalling, such as reliable provisional responses, have been omitted from the following call flow diagrams for the sake of brevity.

Baseline scenario with no call notification

cn01001-successful-call-no-cn

This scenario is a simple SIP call with no interaction with the Call Notification server. The scenario demonstrates the standard SIP call flow for a successful call between two parties.

cn01001 successful call no cn 1
cn01001 successful call no cn 2
cn01001 successful call no cn 3

Action during call setup

The focus of these scenarios is the call notification direction request generated on the initial SIP INVITE. In each scenario, the Call Notification server responds with instructions to influence subsequent processing of the call.

cn01003-successful-call-direction-continue
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

On the CN server response

…​ sends a Call Notification Event request to the Call Notification server.

cn01003 successful call direction continue 1
cn01003 successful call direction continue 2
cn01003 successful call direction continue 3

cn01004-successful-call-direction-endcall
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ENDCALL.

On the CN server response

…​ rejects the call by sending a 403 error response to the calling party and sends a Call Notification Event - Disconnected request to the Call Notification server.

cn01004 successful call direction endcall
cn01005-successful-call-direction-route
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ROUTE and routingAddress of tel:34600009901 so Sentinel directs the call to the alternate destination.

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

On the CN server response

…​ sends a Call Notification Event request to the Call Notification server.

cn01005 successful call direction route 1
cn01005 successful call direction route 2
cn01005 successful call direction route 3

cn01009-successful-call-direction-number-translation
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ROUTE and routingAddress of tel:34600009901 so Sentinel directs the call to the alternate destination.

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

On the CN server response

…​ sends a Call Notification Event request to the Call Notification server.

cn01009 successful call direction number translation 1
cn01009 successful call direction number translation 2
cn01009 successful call direction number translation 3

cn01010-successful-call-direction-endcall-with-announcement
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ENDCALL and announcementId of 15.

On the CN server response

…​ plays announcement (id=15) to the calling party by creating an outgoing dialog to the MRF

The announcement ends

…​ rejects the call by sending a 403 error response to the calling party and sends a Call Notification Event - Disconnected request to the Call Notification server.

cn01010 successful call direction endcall with announcement

Action based on response of the called party

The focus of these scenarios is the call notification direction request generated when the called party fails to respond. In each scenario, the Call Notification server responds with instructions to ROUTE the call to an alternate destination.

cn01006-successful-call-forward-on-busy
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party is busy

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ROUTE and routingAddress of tel:34600009901.

On the CN server response

…​ directs the call to the alternate destination (tel:34600009901)

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

On the CN server response

…​ sends a Call Notification Event request to the Call Notification server.

cn01006 successful call forward on busy 1
cn01006 successful call forward on busy 2
cn01006 successful call forward on busy 3
cn01006 successful call forward on busy 4
cn01006 successful call forward on busy 5

cn01007-successful-call-forward-on-no-answer
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party does not answer

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ROUTE and routingAddress of tel:34600009901.

On the CN server response

…​ directs the call to the alternate destination (tel:34600009901)

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party is notified the call is ended

…​ sends a Call Notification Event request to the Call Notification server.

cn01007 successful call forward on no answer 1
cn01007 successful call forward on no answer 2
cn01007 successful call forward on no answer 3
cn01007 successful call forward on no answer 4
cn01007 successful call forward on no answer 5

cn01008-successful-call-forward-on-rsf
Point in session Sentinel …​

On the initial INVITE

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party cannot be reached

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of ROUTE and routingAddress of tel:34600009901.

On the CN server response

…​ directs the call to the alternate destination (tel:34600009901)

Called party answers

…​ sends a Call Notification Event request to the Call Notification server.

Calling party ends the call

…​ sends a Call Notification Direction request to the Call Notification server.

The CN server responds with an actionToPerform of CONTINUE.

Called party is notified the call is ended

…​ sends a Call Notification Event request to the Call Notification server.

cn01008 successful call forward on rsf 1
cn01008 successful call forward on rsf 2
cn01008 successful call forward on rsf 3
cn01008 successful call forward on rsf 4
cn01008 successful call forward on rsf 5