JBoss.orgCommunity Documentation
The example application is defined by a service descriptor, which refers the included root SBB. The root SBB uses the Location Service SBB (from SIP Services Example) as a child, to retrieve the SIP entities registered.
To obtain the example's complete source code please refer to Section 2.2, “JBoss Communications 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);
}
}
For each CMP field, which will hold the service logic instance data, the application defines two abstract methods, the getter and the setter. SLEE is responsible for the implementation of those methods.
The CMP field's accessors code:
public abstract void setSender(Address sender);
public abstract Address getSender();
public abstract void setCallId(CallIdHeader callId);
public abstract CallIdHeader getCallId();
public abstract void setBody(String body);
public abstract String getBody();
The SIP MESSAGE is the starting point of each instance of the service logic, its responsibility is:
Extract the relevant message information and store in CMP fields, the correct place holders for service logic instance data event handler is the entry point.
Set the timer with the duration extract from the SIP MESSAGE request on a new Null Activity, needed to keep the service logic instance alive. Recall that SLEE garbage collects all SBBEntities which are not attached to a single ActivityContext, and at this point the entity is only attached to the SIP ServerTransaction activity, that is goin to end once the application returns a final response.
Reply the successful processing of the SIP request.
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 SBB uses SIP Service's Location Service to retrieve the URIs of all entities registered with the target address, the child relation method is an abstract class that SLEE implements.
The child relation's getter code:
/**
* Child relation to the location service
* @return
*/
public abstract ChildRelation getLocationChildRelation();
The JAIN SLEE TimerEvent handler is invoked when the duration requested by the SIP Message has passed, it is the final "piece" of the service instance logic, and its responsibility is:
Retrieve all instance data from CMP fields.
Create a Location Service child SBB and retrieve the target's registered URIs.
Send the wake up message(s).
The event handler code:
/**
* Event handler from the timer event, which signals that a message must be
* sent back to the UA
*
* @param event
* @param aci
*/
public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) {
// detaching so the null AC is claimed after the event handling
aci.detach(sbbContext.getSbbLocalObject());
// 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);
// create location service child sbb
final LocationSbbLocalObject locationChildSbb = (LocationSbbLocalObject)
getLocationChildRelation().create();
// get sender bindings and send a message to each
MessageFactory messageFactory = sipProvider.getMessageFactory();
for (RegistrationBinding registration : locationChildSbb
.getBindings(sender.getURI().toString()).values()) {
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);
}
}
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>