TeleStax.orgCommunity Documentation
The example application is defined by a service descriptor, which refers the included root SBB.
To obtain the example's complete source code please refer to Section 2.2, “Restcomm JAIN SLEE MS Control Demo 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>MsControlDemo</service-name>
<service-vendor>org.mobicents</service-vendor>
<service-version>1.0</service-version>
<root-sbb>
<sbb-name>CallSbb</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 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.example.msc.CallSbb</sbb-abstract-class-name>
<cmp-field>
<cmp-field-name>serverTransactionACI</cmp-field-name>
</cmp-field>
<cmp-field>
<cmp-field-name>dialogACI</cmp-field-name>
</cmp-field>
<cmp-field>
<cmp-field-name>mediaSessionACI</cmp-field-name>
</cmp-field>
<cmp-field>
<cmp-field-name>networkConnectionACI</cmp-field-name>
</cmp-field>
<cmp-field>
<cmp-field-name>mediaGroupACI</cmp-field-name>
</cmp-field>
</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>Invite</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>
<event event-direction="Receive" initial-event="False">
<event-name>AnswerGenerated</event-name>
<event-type-ref>
<event-type-name>javax.media.mscontrol.networkconnection.
SdpPortManagerEvent.ANSWER_GENERATED</event-type-name>
<event-type-vendor>org.mobicents</event-type-vendor>
<event-type-version>1.0</event-type-version>
</event-type-ref>
</event>
<event event-direction="Receive" initial-event="False">
<event-name>StreamFailure</event-name>
<event-type-ref>
<event-type-name>javax.media.mscontrol.networkconnection.
SdpPortManagerEvent.NETWORK_STREAM_FAILURE</event-type-name>
<event-type-vendor>org.mobicents</event-type-vendor>
<event-type-version>1.0</event-type-version>
</event-type-ref>
</event>
<event event-direction="Receive" initial-event="False">
<event-name>Joined</event-name>
<event-type-ref>
<event-type-name>javax.media.mscontrol.join.
JoinEvent.JOINED</event-type-name>
<event-type-vendor>org.mobicents</event-type-vendor>
<event-type-version>1.0</event-type-version>
</event-type-ref>
</event>
<event event-direction="Receive" initial-event="False">
<event-name>AnnouncementCompleted</event-name>
<event-type-ref>
<event-type-name>javax.media.mscontrol.mediagroup.
PlayerEvent.PLAY_COMPLETED</event-type-name>
<event-type-vendor>org.mobicents</event-type-vendor>
<event-type-version>1.0</event-type-version>
</event-type-ref>
</event>
<event event-direction="Receive" initial-event="False">
<event-name>Disconnect</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>
Finally, the Resource Adaptors 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>
<resource-adaptor-type-binding>
<resource-adaptor-type-ref>
<resource-adaptor-type-name>
MSC-1.0-RA
</resource-adaptor-type-name>
<resource-adaptor-type-vendor>
org.mobicents
</resource-adaptor-type-vendor>
<resource-adaptor-type-version>
1.0
</resource-adaptor-type-version>
</resource-adaptor-type-ref>
<activity-context-interface-factory-name>
slee/resources/media/1.0/acifactory
</activity-context-interface-factory-name>
<resource-adaptor-entity-binding>
<resource-adaptor-object-name>
slee/resources/media/1.0/provider
</resource-adaptor-object-name>
<resource-adaptor-entity-link>
MSCRA
</resource-adaptor-entity-link>
</resource-adaptor-entity-binding>
</resource-adaptor-type-binding>
The class
org.mobicents.slee.example.msc.CallSbb
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:
private SbbContext sbbContext;
private Tracer tracer;
private SleeSipProvider sipRaSbbInterface;
private SipActivityContextInterfaceFactory sipRaAciFactory;
private MsControlFactory msRaSbbInterface;
private MsActivityContextInterfaceFactory mscRaAciFactory;
public void setSbbContext(SbbContext sbbContext) {
this.sbbContext = sbbContext;
this.tracer = sbbContext.getTracer("MS-Control-DEMO");
try {
Context ctx = (Context) new InitialContext()
.lookup("java:comp/env");
sipRaSbbInterface = (SleeSipProvider) ctx
.lookup("slee/resources/jainsip/1.2/provider");
sipRaAciFactory = (SipActivityContextInterfaceFactory) ctx
.lookup("slee/resources/jainsip/1.2/acifactory");
msRaSbbInterface = (MsControlFactory) ctx
.lookup("slee/resources/media/1.0/provider");
mscRaAciFactory = (MsActivityContextInterfaceFactory) ctx
.lookup("slee/resources/media/1.0/acifactory");
} catch (Exception ne) {
tracer.severe("Could not set SBB context:", ne);
}
}
The SIP INVITE is the starting point of each this example, its responsibility is:
Do the initial SIP session setup, which means create the SIP Dialog and send provisional response.
Do the initial Media session setup, which means create the MediaSession object (the session is container for all MS Control objects) and create the NetworkConnection, which is then provided with the SIP client SDP.
The event handler code:
/**
* Handles the event notifying new SIP session invitation.
*
* @param event
* @param aci
*/
public void onInvite(RequestEvent event, ActivityContextInterface aci) {
tracer.info("Received new SIP session invitation.");
try {
initialSipSessionSetup(event, aci);
} catch (Exception e) {
tracer.severe("Failed to do initial sip session setup.", e);
abortSipSessionSetup();
return;
}
try {
initialMediaSessionSetup(event.getServerTransaction());
} catch (Exception e) {
tracer.severe("Failed to process sip invite", e);
abortSipSessionSetup();
abortMediaSessionSetup();
}
}
/*
* Setup of the media session: creates the media session, creates a network
* connection on it and process the client sdp received on SIP
*/
private void initialMediaSessionSetup(ServerTransaction serverTransaction)
throws MsControlException {
// create media session
MediaSession session = msRaSbbInterface.createMediaSession();
ActivityContextInterface mediaSessionACI = mscRaAciFactory
.getActivityContextInterface(session);
SbbLocalObject sbbLocalObject = sbbContext.getSbbLocalObject();
mediaSessionACI.attach(sbbLocalObject);
// store the media session aci in a cmp shortcut
setMediaSessionACI(mediaSessionACI);
tracer.info("Created media session: " + session);
// create network connection
NetworkConnection connection = session
.createNetworkConnection(NetworkConnection.BASIC);
ActivityContextInterface connectionACI = mscRaAciFactory
.getActivityContextInterface(connection);
connectionACI.attach(sbbLocalObject);
// store the network connection aci in a cmp shortcut
setNetworkConnectionACI(connectionACI);
tracer.info("Created network connection: " + connection);
// process the received sdp
SdpPortManager sdpManager = connection.getSdpPortManager();
tracer.info("Created SDP Manager, sending client sdp...");
sdpManager.processSdpOffer((byte[]) serverTransaction.getRequest()
.getContent());
}
/*
* Aborts the media session: releases the network connection and the media
* session.
*/
private void abortMediaSessionSetup() {
releaseMediaSession();
}
/*
*
*/
private void releaseMediaSession() {
// get sbb entity local object
SbbLocalObject sbbLocalObject = sbbContext.getSbbLocalObject();
// release media group (the ivr) if exists
ActivityContextInterface mediaGroupACI = getMediaGroupACI();
if (mediaGroupACI != null) {
mediaGroupACI.detach(sbbLocalObject);
try {
MediaGroup mediaGroup = (MediaGroup) mediaGroupACI
.getActivity();
if (mediaGroup != null) {
mediaGroup.release();
}
} catch (Exception e) {
tracer.severe("failed to abort media network connection.", e);
}
}
// release network connection if exists
ActivityContextInterface networkConnectionACI = getNetworkConnectionACI();
if (networkConnectionACI != null) {
networkConnectionACI.detach(sbbLocalObject);
try {
NetworkConnection networkConnection = (NetworkConnection) networkConnectionACI
.getActivity();
if (networkConnection != null) {
networkConnection.release();
}
} catch (Exception e) {
tracer.severe("failed to abort media network connection.", e);
}
}
// release media session if exists
ActivityContextInterface mediaSessionACI = getMediaSessionACI();
if (mediaSessionACI != null) {
mediaSessionACI.detach(sbbLocalObject);
try {
MediaSession mediaSession = (MediaSession) mediaSessionACI
.getActivity();
if (mediaSession != null) {
mediaSession.release();
}
} catch (Exception e) {
tracer.severe("failed to abort media session.", e);
}
}
}
The MS Control AnswerGenerated is sent when Media Server successfully processes SDP offer. Event handler code responsibilities are:
Finish the SIP session setup, that is, send the received SDP offer back to the sip client.
Finish the Media session setup, that is, create the MediaGroup IVR and join it with the NetworkConnection.
The event handler code:
/**
* Event with the media server generated sdp, send it back to the sip
* client.
*
* @param event
* @param aci
*/
public void onAnswerGenerated(SdpPortManagerEvent event,
ActivityContextInterface aci) {
tracer.info("Received SDP answer.");
try {
finishSipSessionSetup(event.getMediaServerSdp());
} catch (Exception e) {
tracer.severe("Unable to send OK response with generated SDP", e);
abortSipSessionSetup();
abortMediaSessionSetup();
return;
}
try {
finishMediaSessionSetup(aci);
} catch (Exception e) {
tracer.severe("Unable to initiate join.", e);
terminateSipSession();
abortMediaSessionSetup();
}
}
/*
* End of the media session setup: creates ivr and make it join the session
* with the sip client.
*/
private void finishMediaSessionSetup(
ActivityContextInterface networkConnectionAci)
throws MsControlException {
NetworkConnection connection = (NetworkConnection) networkConnectionAci
.getActivity();
MediaSession session = connection.getMediaSession();
MediaGroup mediaGroup = session
.createMediaGroup(MediaGroup.PLAYER_RECORDER_SIGNALDETECTOR);
connection.joinInitiate(Direction.DUPLEX, mediaGroup, "context");
ActivityContextInterface mediaGroupACI = mscRaAciFactory
.getActivityContextInterface(mediaGroup);
mediaGroupACI.attach(sbbContext.getSbbLocalObject());
setMediaGroupACI(mediaGroupACI);
}
The MS Control JoinEvent is sent when Media Server controller joins resources . Event handler code responsibilities are:
Request media playback from group player.
The event handler code:
/**
* Event notifying the connection between the sip client and the media
* server.
*
* @param event
* @param aci
*/
public void onJoined(JoinEvent event, ActivityContextInterface aci) {
tracer.info("SIP client and media server connected, requesting play of announcement...");
try {
ActivityContextInterface mediaGroupACI = getMediaGroupACI();
MediaGroup mediaGroup = (MediaGroup) mediaGroupACI.getActivity();
mediaGroup.getPlayer().play(new URI(WELCOME), null, null);
} catch (Exception e) {
tracer.severe(
"Unexpected error playing annoucenment, terminating sip and media sessions.",
e);
terminateSipSession();
terminateMediaSession();
}
}
/*
* Terminates the media session: releases the network connection and the
* media session.
*/
private void terminateMediaSession() {
releaseMediaSession();
}