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 included root SBB. The root SBB uses the DataSourceChild SBB (from SIP JDBC Registrar Example) as a child, to retrieve the SIP entities registered.

Important

To obtain the example's complete source code please refer to Section 2.2, “Mobicents JAIN SLEE SIP Wake Up 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>Wake Up Service</service-name>
        <service-vendor>NIST</service-vendor>
        <service-version>1.0</service-version>
        <root-sbb>
            <sbb-name>Wake Up Sbb</sbb-name>
            <sbb-vendor>NIST</sbb-vendor>
            <sbb-version>1.0</sbb-version>
        </root-sbb>
        <default-priority>0</default-priority>
    </service>
</service-xml>
        
        

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

The class org.mobicents.slee.examples.wakeup.WakeupSbb includes all the service logic for the example.

The javax.slee.SbbObject's setSbbContext(SbbContext) is used by SBBs to store the SBB's context into a class field. The SBB should take the opportunity to also store objects, such as SLEE facilities, which are reused by all service logic entities, a.k.a. SbbEntities, and are stored in the JNDI environment.

The class fields and setSbbContext(SbbContext) method's and related code:



    // the Sbb's context
    private SbbContext sbbContext; 
    
    // the Sbb's single tracer
    private Tracer tracer = null; 
        
    // cached objects in Sbb's environment, lookups are expensive
    private SleeSipProvider sipProvider;
    private TimerFacility timerFacility;
    private NullActivityContextInterfaceFactory nullACIFactory;
    private NullActivityFactory nullActivityFactory;
    
    /*
     * (non-Javadoc)
     * 
     * @see javax.slee.Sbb#setSbbContext(javax.slee.SbbContext)
     */
    public void setSbbContext(SbbContext context) {
        // save the sbb context in a field
        this.sbbContext = context;
        // get the tracer if needed
        this.tracer = context.getTracer(WakeUpSbb.class.getSimpleName());
        // get jndi environment stuff
        try {
            final Context myEnv = (Context) new InitialContext();
            // slee facilities
            this.timerFacility = (TimerFacility) myEnv
                    .lookup(TimerFacility.JNDI_NAME);
            this.nullACIFactory = (NullActivityContextInterfaceFactory) myEnv
                    .lookup(NullActivityContextInterfaceFactory.JNDI_NAME);
            this.nullActivityFactory = (NullActivityFactory) myEnv
                    .lookup(NullActivityFactory.JNDI_NAME);
            // the sbb interface to interact with SIP resource adaptor
            this.sipProvider = (SleeSipProvider) myEnv
                    .lookup("java:comp/env/slee/resources/jainsip/1.2/provider");
        } catch (Exception e) {
            tracer.severe("Failed to set sbb context", e);
        }
    }
                
                

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

The event handler code:



    /**
     * Event handler for the SIP MESSAGE from the UA
     * 
     * @param event
     * @param aci
     */
    public void onMessageEvent(javax.sip.RequestEvent event,
            ActivityContextInterface aci) {
        final Request request = event.getRequest();
        try {
            // message body should be *FIRST_TOKEN<timer value in
            // seconds>MIDDLE_TOKEN<msg to send back to UA>LAST_TOKEN*
            final String body = new String(request.getRawContent());
            final int firstTokenStart = body.indexOf(FIRST_TOKEN);
            final int timerDurationStart = firstTokenStart + FIRST_TOKEN_LENGTH;
            final int middleTokenStart = body.indexOf(MIDDLE_TOKEN,
                    timerDurationStart);
            final int bodyMessageStart = middleTokenStart + MIDDLE_TOKEN_LENGTH;
            final int lastTokenStart = body.indexOf(LAST_TOKEN,
                    bodyMessageStart);
            if (firstTokenStart > -1 && middleTokenStart > -1
                    && lastTokenStart > -1) {
                // extract the timer duration
                final int timerDuration = Integer.parseInt(body.substring(
                        timerDurationStart, middleTokenStart));
                // create a null AC and attach the sbb local object
                final ActivityContextInterface timerACI = this.nullACIFactory
                        .getActivityContextInterface(this.nullActivityFactory
                                .createNullActivity());
                timerACI.attach(sbbContext.getSbbLocalObject());
                // set the timer on the null AC, because the one from this event
                // will end as soon as we send back the 200 ok
                this.timerFacility.setTimer(timerACI, null, System
                        .currentTimeMillis()
                        + (timerDuration * 1000), new TimerOptions());
                // extract the body message
                final String bodyMessage = body.substring(bodyMessageStart,
                        lastTokenStart);
                // store it in a cmp field
                setBody(bodyMessage);
                // do the same for the call id
                setCallId((CallIdHeader) request.getHeader(CallIdHeader.NAME));
                // also store the sender's address, so we can send the wake up
                // message
                final FromHeader fromHeader = (FromHeader) request
                        .getHeader(FromHeader.NAME);
                if (tracer.isInfoEnabled()) {
                    tracer.info("Received a valid message from "
                            + fromHeader.getAddress()
                            + " requesting a reply containing '" + bodyMessage
                            + "' after " + timerDuration + "s");
                }
                setSender(fromHeader.getAddress());
                // finally reply to the SIP message request
                sendResponse(event, Response.OK);
            } else {
                // parsing failed
                tracer.warning("Invalid msg '" + body + "' received");
                sendResponse(event, Response.BAD_REQUEST);
            }
        } catch (Throwable e) {
            // oh oh something wrong happened
            tracer.severe("Exception while processing MESSAGE", e);
            try {
                sendResponse(event, Response.SERVER_INTERNAL_ERROR);
            } catch (Exception f) {
                tracer.severe("Exception while sending SERVER INTERNAL ERROR",
                        f);
            }
        }
    }
                
                

The Child SBB provides the registration bindings result asynchronously through a callback, it is the final "piece" of the service instance logic, and its responsibility is:

The callback method code:



    public void getBindingsResult(int resultCode, List<RegistrationBinding> bindings) {
        if (resultCode < 300) {
            // get data from cmp fields
            String body = getBody();
            CallIdHeader callId = getCallId();
            Address sender = getSender();
            try {
                // create headers needed to create a out-of-dialog request
                AddressFactory addressFactory = sipProvider.getAddressFactory();
                Address fromNameAddress = addressFactory
                        .createAddress("sip:wakeup@mobicents.org");
                fromNameAddress.setDisplayName("Wake Up Service");
                HeaderFactory headerFactory = sipProvider.getHeaderFactory();
                FromHeader fromHeader = headerFactory.createFromHeader(
                        fromNameAddress, null);
                List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
                ListeningPoint listeningPoint = sipProvider.getListeningPoints()[0];
                ViaHeader viaHeader = sipProvider.getHeaderFactory()
                        .createViaHeader(listeningPoint.getIPAddress(),
                                listeningPoint.getPort(),
                                listeningPoint.getTransport(), null);
                viaHeaders.add(viaHeader);
                ContentTypeHeader contentTypeHeader = headerFactory
                        .createContentTypeHeader("text", "plain");
                CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(2L,
                        Request.MESSAGE);
                MaxForwardsHeader maxForwardsHeader = headerFactory
                        .createMaxForwardsHeader(70);
                
                // send a message to each contact of the target resource
                MessageFactory messageFactory = sipProvider.getMessageFactory();
                for (RegistrationBinding registration : bindings) {
                    try {
                        // create request uri
                        URI requestURI = addressFactory.createURI(registration
                                .getContactAddress());
                        // create to header
                        ToHeader toHeader = headerFactory.createToHeader(sender,
                                null);
                        // create request
                        Request request = messageFactory.createRequest(requestURI,
                                Request.MESSAGE, callId, cSeqHeader, fromHeader,
                                toHeader, viaHeaders, maxForwardsHeader,
                                contentTypeHeader, body);
                        // create client transaction and send request
                        ClientTransaction clientTransaction = sipProvider
                                .getNewClientTransaction(request);
                        clientTransaction.sendRequest();
                    } catch (Throwable f) {
                        tracer.severe("Failed to create and send message", f);
                    }
                }
            } catch (Throwable e) {
                tracer.severe("Failed to create message headers", e);
            }
        }
        else {
            tracer.severe("Unable to send wake up message, the SIP Registrar did 
                not retrieved the target bindings with sucess");
        }       
    }
                
                

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 and child relation.:



        <sbb-classes>
            <sbb-abstract-class>
                <sbb-abstract-class-name>org.mobicents.slee.examples.wakeup.WakeUpSbb</sbb-abstract-class-name>
                <cmp-field>
                    <cmp-field-name>body</cmp-field-name>
                </cmp-field>
                <cmp-field>
                    <cmp-field-name>callId</cmp-field-name>
                </cmp-field>
                <cmp-field>
                    <cmp-field-name>sender</cmp-field-name>
                </cmp-field>
                <get-child-relation-method>
                    <sbb-alias-ref>LocationSbb</sbb-alias-ref>
                    <get-child-relation-method-name>
                        getLocationChildRelation
                    </get-child-relation-method-name>
                    <default-priority>0</default-priority>
                </get-child-relation-method>
            </sbb-abstract-class>
        </sbb-classes>
            
            

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



        <event event-direction="Receive" initial-event="True">
            <event-name>MessageEvent</event-name>
            <event-type-ref>
                <event-type-name>javax.sip.message.Request.MESSAGE</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>

        <event event-direction="Receive" initial-event="False">
            <event-name>TimerEvent</event-name>
            <event-type-ref>
                <event-type-name>javax.slee.facilities.TimerEvent</event-type-name>
                <event-type-vendor>javax.slee</event-type-vendor>
                <event-type-version>1.0</event-type-version>
            </event-type-ref>
        </event>
            
            

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>