JBoss.orgCommunity Documentation

Chapter 4. Source Code Overview

4.1. Service Descriptor
4.2. The Root SBB
4.2.1. The Root SBB Abstract Class
4.2.2. Root SBB XML Descriptor

The example application is defined by a service descriptor, which refers the Root SBB. The Root SBB does not defines child relations, which means the application is a single SBB.

Important

To obtain the example's complete source code please refer to Section 2.2, “Mobicents JAIN SLEE SIP B2BUA Example Source Code”.

The service descriptor is plain simple, it just defines the service ID, the ID of the root SBB and its default priority. The complete XML is:



<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE service-xml PUBLIC 
    "-//Sun Microsystems, Inc.//DTD JAIN SLEE Service 1.1//EN"
    "http://java.sun.com/dtd/slee-service-xml_1_1.dtd">
<service-xml>
    <service>
        <service-name>SimpleSip11B2BTestService</service-name>
        <service-vendor>org.mobicents</service-vendor>
        <service-version>1.0</service-version>
        <root-sbb>
            <sbb-name>SimpleSip11B2BTestSbb</sbb-name>
            <sbb-vendor>org.mobicents</sbb-vendor>
            <sbb-version>1.0</sbb-version>
        </root-sbb>
        <default-priority>0</default-priority>
    </service>
</service-xml>
        
        

The SIP B2BUA Example's Root SBB is composed by the abstract class and the XML descriptor.

The class org.mobicents.slee.example.sip11.b2b.SimpleSip11B2BTestSbb includes all the service logic for the example.

The SIP INVITE is the starting point of each instance of the service logic, its responsibility is:

The event handler code:



        // Initial request
    public void onInviteEvent(RequestEvent event, ActivityContextInterface aci) {
        // ACI is the server transaction activity
        final ServerTransaction st = event.getServerTransaction();
        try {
            // Create the dialogs representing the incoming and outgoing call
            // legs.
            final DialogActivity incomingDialog = (DialogActivity) sipProvider
                    .getNewDialog(st);
            final DialogActivity outgoingDialog = sipProvider.getNewDialog(
                    incomingDialog, true);
            // Obtain the dialog activity contexts and attach to them
            final ActivityContextInterface outgoingDialogACI = sipActivityContextInterfaceFactory
                    .getActivityContextInterface(outgoingDialog);
            final ActivityContextInterface incomingDialogACI = sipActivityContextInterfaceFactory
                    .getActivityContextInterface(incomingDialog);
            final SbbLocalObject sbbLocalObject = sbbContext
                    .getSbbLocalObject();
            incomingDialogACI.attach(sbbLocalObject);
            outgoingDialogACI.attach(sbbLocalObject);
            // Record which dialog is which, so we can find the peer dialog
            // when forwarding messages between dialogs.
            setIncomingDialog(incomingDialogACI);
            setOutgoingDialog(outgoingDialogACI);
            forwardRequest(st, outgoingDialog);
        } catch (Throwable e) {
            tracer.severe("Failed to process incoming INVITE.", e);
            replyToRequestEvent(event, Response.SERVICE_UNAVAILABLE);
        }
    }
                
                

Successful final responses can be received on both session legs: as a response to the INVITE sent to the outgoing leg, or as a response to a BYE sent to any of the session legs.

If it is a response to the INVITE the application sends an ACK immediately, to minimize the response retransmissions, and then forwards the message to the incoming leg.

In case of a BYE response, the application does not forwards it to the other leg, because the other leg's BYE was already replied.

The event handler method's code:



    public void on2xxResponse(ResponseEvent event, ActivityContextInterface aci) {
        final CSeqHeader cseq = (CSeqHeader) event.getResponse().getHeader(
                CSeqHeader.NAME);
        if (cseq.getMethod().equals(Request.INVITE)) {
            // lets ack it ourselves to avoid UAS retransmissions due to
            // forwarding of this response and further UAC Ack
            // note that the app does not handles UAC ACKs
            try {
                final Request ack = event.getDialog().createAck(
                        cseq.getSeqNumber());
                event.getDialog().sendAck(ack);
            } catch (Exception e) {
                tracer.severe("Unable to ack INVITE's 200 ok from UAS", e);
            }
        } else if (cseq.getMethod().equals(Request.BYE)
                || cseq.getMethod().equals(Request.CANCEL)) {
            // not forwarded to the other dialog
            return;
        }
        processResponse(event, aci);
    }
                
                

The CANCEL request is received when a party wants to cancel the session, in such scenario the application first needs to check the state of the other leg, and if it is already established, a BYE request is sent instead of forwarding the CANCEL message,.

The event handler method's code:



    public void onCancel(CancelRequestEvent event, ActivityContextInterface aci) {
        if (tracer.isInfoEnabled()) {
            tracer.info("Got a CANCEL request.");
        }
        
        try {
            this.sipProvider.acceptCancel(event, false);
            final ActivityContextInterface peerDialogACI = getPeerDialog(aci);
            final DialogActivity peerDialog = (DialogActivity) peerDialogACI
                    .getActivity();
            final DialogState peerDialogState = peerDialog.getState();
            if (peerDialogState == null || peerDialogState == DialogState.EARLY) {
                peerDialog.sendCancel();
            } else {
                peerDialog.sendRequest(peerDialog.createRequest(Request.BYE));
            }
        } catch (Exception e) {
            tracer.severe("Failed to process cancel request", e);
        }
    }
                
                

The application defines a few helper methods to deal with message forwarding, providing example and optimal usage of the JAIN SIP RA APIs:



    private void replyToRequestEvent(RequestEvent event, int status) {
        try {
            event.getServerTransaction().sendResponse(
                    sipProvider.getMessageFactory().createResponse(status,
                            event.getRequest()));
        } catch (Throwable e) {
            tracer.severe("Failed to reply to request event:\n" + event, e);
        }
    }
    private void processMidDialogRequest(RequestEvent event,
            ActivityContextInterface dialogACI) {
        try {
            // Find the dialog to forward the request on
            ActivityContextInterface peerACI = getPeerDialog(dialogACI);
            forwardRequest(event.getServerTransaction(),
                    (DialogActivity) peerACI.getActivity());
        } catch (SipException e) {
            tracer.severe(e.getMessage(), e);
            replyToRequestEvent(event, Response.SERVICE_UNAVAILABLE);
        }
    }
    private void processResponse(ResponseEvent event,
            ActivityContextInterface aci) {
        try {
            // Find the dialog to forward the response on
            ActivityContextInterface peerACI = getPeerDialog(aci);
            forwardResponse((DialogActivity) aci.getActivity(),
                    (DialogActivity) peerACI.getActivity(), event
                            .getClientTransaction(), event.getResponse());
        } catch (SipException e) {
            tracer.severe(e.getMessage(), e);
        }
    }
    private ActivityContextInterface getPeerDialog(ActivityContextInterface aci)
            throws SipException {
        final ActivityContextInterface incomingDialogAci = getIncomingDialog();
        if (aci.equals(incomingDialogAci)) {
            return getOutgoingDialog();
        }
        if (aci.equals(getOutgoingDialog())) {
            return incomingDialogAci;
        }
        throw new SipException("could not find peer dialog");
    }
    private void forwardRequest(ServerTransaction st, DialogActivity out)
            throws SipException {
        final Request incomingRequest = st.getRequest();
        if (tracer.isInfoEnabled()) {
            tracer.info("Forwarding request " + incomingRequest.getMethod()
                    + " to dialog " + out);
        }
        // Copies the request, setting the appropriate headers for the dialog.
        Request outgoingRequest = out.createRequest(incomingRequest);
        // Send the request on the dialog activity
        final ClientTransaction ct = out.sendRequest(outgoingRequest);
        // Record an association with the original server transaction,
        // so we can retrieve it when forwarding the response.
        out.associateServerTransaction(ct, st);
    }
    private void forwardResponse(DialogActivity in, DialogActivity out,
            ClientTransaction ct, Response receivedResponse)
            throws SipException {
        // Find the original server transaction that this response
        // should be forwarded on.
        final ServerTransaction st = in.getAssociatedServerTransaction(ct);
        // could be null
        if (st == null)
            throw new SipException(
                    "could not find associated server transaction");
        if (tracer.isInfoEnabled()) {
            tracer.info("Forwarding response "
                    + receivedResponse.getStatusCode() + " to dialog " + out);
        }
        // Copy the response across, setting the appropriate headers for the
        // dialog
        final Response outgoingResponse = out.createResponse(st,
                receivedResponse);
        // Forward response upstream.
        try {
            st.sendResponse(outgoingResponse);
        } catch (InvalidArgumentException e) {
            tracer.severe("Failed to send response:\n" + outgoingResponse, e);
            throw new SipException("invalid response", e);
        }
    }
                
                

The Root SBB XML Descriptor has to be provided and match the abstract class code.

First relevant part is the declaration of the sbb-classes element, where the sbb class abstract name must be specified, along with the cmp fields:



        <sbb-classes>
            <sbb-abstract-class>
                <sbb-abstract-class-name>org.mobicents.slee.example.sip11.b2b.SimpleSip11B2BTestSbb</sbb-abstract-class-name>
                <cmp-field>
                    <cmp-field-name>incomingDialog</cmp-field-name>
                </cmp-field>
                <cmp-field>
                    <cmp-field-name>outgoingDialog</cmp-field-name>
                </cmp-field>
            </sbb-abstract-class>
        </sbb-classes>
            
            

Then the events handled by the SBB must be specified too:



        <!-- INITIALS EVENT, OUT OF DIALOG -->
        <event event-direction="Receive" initial-event="True">
            <event-name>InviteEvent</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.message.Request.INVITE</event-type-name>
                <event-type-vendor>net.java.slee</event-type-vendor>
                <event-type-version>1.2</event-type-version>
            </event-type-ref>
            <initial-event-select variable="ActivityContext" />
        </event>

        <!--  EVERYTHING ELSE HAPPENS IN DIALOG -->
        <event event-direction="Receive" initial-event="False">
            <event-name>1xxResponse</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.message.Response.PROVISIONAL</event-type-name>
                <event-type-vendor>net.java.slee</event-type-vendor>
                <event-type-version>1.2</event-type-version>
            </event-type-ref>
        </event>
        <event event-direction="Receive" initial-event="False">
            <event-name>2xxResponse</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.message.Response.SUCCESS</event-type-name>
                <event-type-vendor>net.java.slee</event-type-vendor>
                <event-type-version>1.2</event-type-version>
            </event-type-ref>
        </event>

        <event event-direction="Receive" initial-event="False">
            <event-name>Bye</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.Dialog.BYE</event-type-name>
                <event-type-vendor>net.java.slee</event-type-vendor>
                <event-type-version>1.2</event-type-version>
            </event-type-ref>
        </event>
        <event event-direction="Receive" initial-event="False">
            <event-name>Cancel</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.message.Request.CANCEL</event-type-name>
                <event-type-vendor>net.java.slee</event-type-vendor>
                <event-type-version>1.2</event-type-version>
            </event-type-ref>
        </event>
            
            

Note that there is a single event defined as initial, which triggers the sbb logic, remaining events all happen in activities that the service instance is already attached, abstracting the application from calculating which session it handles.

Finally, the SIP11 Resource Adaptor must be specified also, otherwise SLEE won't put its SBB Interface in the SBB's JNDI Context:



        <resource-adaptor-type-binding>
            <resource-adaptor-type-ref>
                <resource-adaptor-type-name>
                    JAIN SIP
                </resource-adaptor-type-name>
                <resource-adaptor-type-vendor>
                    javax.sip
                </resource-adaptor-type-vendor>
                <resource-adaptor-type-version>
                    1.2
                </resource-adaptor-type-version>
            </resource-adaptor-type-ref>
            <activity-context-interface-factory-name>
                slee/resources/jainsip/1.2/acifactory
            </activity-context-interface-factory-name>
            <resource-adaptor-entity-binding>
                <resource-adaptor-object-name>
                    slee/resources/jainsip/1.2/provider
                </resource-adaptor-object-name>
                <resource-adaptor-entity-link>
                    SipRA
                </resource-adaptor-entity-link>
            </resource-adaptor-entity-binding>
        </resource-adaptor-type-binding>