Seam Telco Framework Basics
The Seam Telco Framework (STF) is based on JSR-289 (Sip Servlets 1.1) and Mobicents Media Server API and is aiming to unify the programming models for Web, Sip and Media applications. JBoss Seam
components can handle real SipServletRequests and Media Server
events while taking advantage of the Seam perks - bijection, scoping,
transaction management, interceptors and everything else. If we look at the
integration aspect of JBoss Seam, it is just plugging in different
frameworks (from JEE, some Web frameworks, jBPM, rules, cache and so
on) and makes them availalable from your applications in the same
context for a certain role. From this point of view, we are just adding
another set of standard APIs to be used in the same context as the rest.
Your SIP and HTTP Servlets are running in the same servlet context
and they share the same application-scoped components, while the
session-scoped components are mapped to the respective SIP and HTTP
sessions. Since the RTP sessions and SIP session in a single call
roughly span the same logical session, they are united under the
JSR-289 SipSession context (or we can call it a call context
in this case). This context can be used to store or exchange state
within the call modified by either SIp or Media events. The context
itself is the SipSession object and you can share information using the
set/getAttribute() methods.
You can work with both EJBs and POJOs, interact with your
web-layer, and still build very loosely coupled telco components
working on the same messages without being aware of each-other. Seam
provides synchronous and asynchronous light-weight message passing
through the @Observer-annotated methods, which covers most of the
communication needs in converged applications.
JBoss Tools, JBDS or other Seam-enabled IDEs already have great support for most of the Seam infrastructure syntax. Since STF reuses the Seam infrastructure in the telco apps, you can work with any IDE very easily on top of any existing Seam application or create other new applications without installing new plugins.
How it works?
The Seam 2 core is mostly independent of HTTP Servlets and JSF.
Basically we install a controller Sip Servlet and session listeners to control the context assignment for incoming SIP
messages and reproduce the Seam lifecycle completely. When a SIP message arrives, we raise Seam events to notify
the Seam components subscribed to receive the SIP events.
The Media Server events occur in the context of a SIP session and
are treated in a similar way as the SIP events. You just subscribe
methods with @Observer and process them as regular Media Server
listener notifications.
The setup is straightforward - just add the controller servlet to
your Seam application in a Sip Servlets container and it's ready to go.
For now you can start with the sample application.
Theoretically this technique can work on any JSR289-compliant
container that supports JBoss Seam, but it is only tested on
Mobicents Sip Servlets 0.8 and higher with JBoss Application Server 4.2.3 and
Seam 2.1.x.
Example
This is simple SIP service which responds to an INVITE and counts
the number of messages per session. There is a session-scoped counter,
which is incremented on every message. Note how you can subscribe
methods to SIP events. You can subscribe as many methods as you like in
any Seam component, they all will be notified.
@Name("simpleSeamSipService")
@Scope(ScopeType.STATELESS)
@Transactional
public class SimpleSeamSipService {
@Logger Log log;
@In SessionMessageCounter sessionMessageCounter;
public void inc() {
sessionMessageCounter.increment();
log.info("Processed SIP messages "
+ sessionMessageCounter.getMessages());
}
@Observer("INVITE")
public void doInvite(SipServletRequest request)
throws Exception {
inc();
request.createResponse(180).send();
Thread.sleep(100);
request.createResponse(200).send();
}
@Observer("ACK")
public void doAck(SipServletRequest request)
throws Exception {
inc();
}
@Observer({"REGISTER","BYE"})
public void doRegister(SipServletRequest request)
throws Exception {
inc();
request.createResponse(200).send();
}
}
You should keep in mind that the Seam events are application-wide.
If you need cross-application messaging use JMS. Seam would help a lot
with JMS too.
And this is the SIP-session scoped component:
@Name("sessionMessageCounter")
@Startup
@Scope(ScopeType.SESSION)
public class SessionMessageCounter {
private int messages;
public int getMessages() {
return messages;
}
public void increment() {
messages++;
}
}
Actually, this component is created and stored in every Seam
session, no matter if it's SIP or HTTP. This is a bit inefficient, but
has some advantages. Ultimately we would want to have a separate
SipSession and SipApplicationSession scopes in Seam and we will
probably get there with the user-defined scopes in Web Beans (JSR 299)
or a newer version of Seam.
It is useful to know that you can always inject the current SipSession. This is particularly useful when processing Media Events. Media Events by default do not have a SipSession association, but the Seam Telco Framework work it out. To inject a SipSession simply do this:
@In SipSession sipSession; // note that this will not be available in HTTP context(sessions). it is only available in SIP or Media context.
Similarly you can inject the SipFactory if you need it:
@In SipFactory sipFactory; // This is available from any context HTTP, SIP or Media.
Making calls from a Web Page
There is nothing special here, you just make some action that creates a sip request and send it. Note how the SipFactory is available from the web context without any special configuration.
@Name("callAction")
@Scope(ScopeType.STATELESS)
@AutoCreate
public class CallAction {
@Logger
private static Log log;
@In SipFactory sipFactory;
public void dialParticipant(final CallParticipant participant) {
SipApplicationSession appSession = sipFactory.createApplicationSession();
Address from = sipFactory.createAddress("here@localhost");
Address to = sipFactory.createAddress(participant.getUri());
URI requestURI = sipFactory.createURI(participant.getUri());
SipServletRequest request = sipFactory.createRequest(appSession,
"INVITE", from, to);
request.setRequestURI(requestURI);
request.send();
log.info("Calling " + to);
}
}
.. of course, you can call this action programatically or directly from a JSF template like this:
<h:commandLink value="Dial" action="#{callAction.dialParticipant(someParticipant)}"/>
This is just a button that will call the action when clicked. Keep in mind that almost all Java Web Frameworks are based on top of HTTP Servlets, including JSF, GWT and those on top of them. Thus, the code is equivalent to initiating a call from HTTP servlet. We do not obtain the SipFactory and the application session in the recommended way from the JSR-289 spec in this case, because this method can be called from within Web Context, SIP Context or Media Context. The JSR-289 recommended way is meant for Web Context only as it expects HttpSession object to be available at all times. Injecting the SipFactory from the Seam IoC makes it more generic and fool-proof. If you don't plan to call this dialParticipant method from SIP or Media Context you can also do this, but there is generally no benefit, even though it is recommended by JSR-289:
// Don't use this because we might call it from SIP context (faces context not available outside HTTP context)!
FacesContext context = FacesContext.getCurrentInstance();
ConvergedHttpSession session = (ConvergedHttpSession) context.getExternalContext().getSession(false);
Media Support
The Media events from Mobicents Media Server also work in concert with the rest of the framework. All Media events occur in the context of a SipSession
and you can use the Sip Session to share state between the Media and
SIP events. The Media events are carried in the same classes as the events from MSC API. It is highly recommended to get familiar with MSC API before reading the rest of the document. You can learn more about MSC API in the official Mobicents documentation. Basically the Seam Telco Framework installs it's own listeners for all Media objects (connections, links and endpoints) that are used in the context of a call (SipSession), then the listener callbacks are transalted into Seam events and delivered in the call context (SipSession context).
This is a list of commonly used method signatures. Most of them should already be familiar to MSC API developers:
@Observer("mediaEvent")
public void mediaEvent(MediaEvent mediaEvent) {}
@Observer("connectionCreated")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionDisconnected")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionFailed")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionHalfOpen")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionModeRecvOnly")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionOpen")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionModeSendRecv")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("connectionModeSendOnly")
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
// Media Link Events
@Observer("linkConnected")
public void doLinkEvent(MsLinkEvent linkEvent) {}
@Observer("linkCreated")
public void doLinkEvent(MsLinkEvent linkEvent) {}
@Observer("linkDisconnected")
public void doLinkEvent(MsLinkEvent linkEvent) {}
@Observer("linkFailed")
public void doLinkEvent(MsLinkEvent linkEvent) {}
@Observer("modeFullDuplex")
public void doLinkEvent(MsLinkEvent linkEvent) {}
@Observer("modeHalfDuplex")
public void doLinkEvent(MsLinkEvent linkEvent) {}
// Notification events (for DTMF and others)
@Observer("mediaEvent")
public void doMediaEvent(MediaEvent mediaEvent) {}
// Since STF 2.1 if you application doesn't handle a media exception, this event is raised
// similarly to the core Seam exception handling for out-of-context exceptions.
@Observer("org.mobicents.media.unhandledException")
public void doUnhandledException(Exeption exception) {}
There are also some auxiliary events derived from the base events that help you work with IVR and link/connection lifecycle:
(DEPRECATED since STF 2.1, use linkConnected instead) @Observer("storeLinkConnected") // This event is fired after the MediaSessionStore is initialized with the MsConnection
public void doLinkEvent(MsLinkEvent linkEvent) {}
(DEPRECATED since STF 2.1, use connectionOpen instead) @Observer("storeConnectionOpen") // This event is fired after the MediaSessionStore is initialized with the MsLink
public void doConnectionEvent(MsConnectionEvent connectionEvent) {}
@Observer("DTMF") // Fired when we are listening for DTMF in the current SipSession context
public void doDTMF(String digit) {}
@Observer("announcementComplete") // Fired when the announcement in the current SipSession context is complete
public void doAnnouncementComplete() {}
@Observer("announcementFailed") // Fired when the announcement in the current SipSession context has failed
public void doAnnouncementFailed() {}
@Observer("audioFailed") // Fired when there is a problem with audio
public void doAudioFailed() {}
Media Event Relationships
Most of the events are directly mapped to the MSC-API events, but there are some exceptions for convenience. You can learn more about these relationships from the following diagram or from the source code:

You can handle these events and relate them to a sip session simply
by injecting the SipSession object and you can store or read state from
there as shown in the example below. We are reusing the MSC API objects
with the exception of MediaEvent where the notification message is
wrapped and carries more information than the original event structure.
You can see the demo for more details. And generally the best way to
learn it is by playing with the examples, where more useful utility
classes have been developed (the media framework demo in particular)
Here is an example conference application using the most
common functions:
@Name("conference")
@Scope(ScopeType.STATELESS)
public class Conference {
@Logger Log log;
@In MediaController mediaController;
@In SipSession sipSession;
@In(scope=ScopeType.APPLICATION, required=false)
@Out(scope=ScopeType.APPLICATION, required=false)
String conferenceEndpointName;
@Observer("INVITE")
public void doInvite(SipServletRequest request) throws Exception {
String sdp = new String((byte[]) request.getContent());
request.createResponse(180).send();
sipSession.setAttribute("inviteRequest", request);
if (conferenceEndpointName == null)
conferenceEndpointName = "media/trunk/Conference/$";
mediaController.createConnection(conferenceEndpointName).modify("$",
sdp);
}
@Observer("connectionOpen")
public void doConnectionOpen(MsConnectionEvent event) throws IOException {
conferenceEndpointName = event.getConnection().getEndpoint()
.getLocalName();
SipServletRequest request = (SipServletRequest) sipSession
.getAttribute("inviteRequest");
SipServletResponse response = request.createResponse(200);
response.setContent(event.getConnection().getLocalDescriptor(),
"application/sdp");
response.send();
}
@Observer( { "BYE", "REGISTER" })
public void sayOK(SipServletRequest request) throws Exception {
request.createResponse(200).send();
// Since STF 2.1 - no need to release MsConnection and MsLink objects stored in the mediaSessionStore.
// They are cleaned up automatically.
}
}
And here is another example which focuses on building media endpoint topologies with detailed comments explaining the order of execution of the media callbacks:
@Name("serviceMessageHandler")
@Scope(ScopeType.STATELESS)
public class ServiceMessageHandler {
@In MediaController mediaController;
@In MediaSessionStore mediaSessionStore;
@In IVRHelper ivrHelper;
@In SipSession sipSession;
/*
* Represents the current state of the call. I.e. - are we connected to IVR
* or a conference/1 or conference/$. Basically we store here the last pressed
* DTMF button as it identifies the mode uniquely for this application.
*/
@In(required=false,scope=ScopeType.SESSION)
@Out(required=false,scope=ScopeType.SESSION)
String mode;
@Logger
private static Log log;
/* [0]
* We receive an incoming call. INVITE has been sent from some SIP client
* to our applications.
*/
@Observer("INVITE")
public void doInvite(SipServletRequest request) throws Exception {
/* [1]
* Let's store the invite in the session. This is equivalent to
* what we do with the "mode" variable, which is session-scoped,
* but declared in the Seam-way.
*/
sipSession.setAttribute("inviteRequest", request);
/* [2]
* Let's tell the caller that we received the INVITE and we are
* ringing now.
*/
request.createResponse(180).send();
/* [3]
* Take the remote SDP sent by the caller.
*/
String sdp = new String((byte[]) request.getContent());
/* [4]
* Now create a Media connection between the caller and any available
* PacketRelay endpoint in our media server. Note that we pass the SDP
* with the modify method. The remote SDP contains the RTP port where the caller
* want to receive the RTP stream. By the following line of code we are telling our
* Media Server to create a local UDP socket and start preparing to stream
* to the remote socket using the parameters contained in this SDP.
* Other than the RTP port of the caller, the SDP contains information about
* the supported codecs and media capabilities. We usually dont care about
* it as it is responsibility of the media server to process the data. SDP is
* not very human-readable anyway.
*/
mediaController.createConnection("media/trunk/PacketRelay/$").modify("$", sdp);
}
/* [5]
* Once the RTP connection is fully negotiated fras a result from step [4], the Media
* Server notified us with this callback and the RTP is ready for streaming
* immediately after we send our SDP to the caller. So far we have built the
* following topology:
*
* Caller <----MsConnection----> PacketRelay
*/
@Observer("connectionOpen")
public void connectionOpenRequest(MsConnectionEvent event) throws Exception {
/* [6]
* Let's get the MsConnection object that connects the caller to the
* PacketRelay Endpoint. We can take it from mediaSessionStore, where STF
* collects objects related to the Media Topology of the current call(session).
*/
MsConnection connection = mediaSessionStore.getMsConnection();
/* [7]
* Let's take the local SDP, where the information about our own RTP port and
* supported codecs is contained. We must send this info to the caller so they
* will know where to begin streaming they media and what codecs they can use.
*/
String sdp = event.getConnection().getLocalDescriptor();
/* [8]
* Recall the INVITE we stored in step [1]
*/
SipServletRequest inviteRequest = (SipServletRequest)
sipSession.getAttribute("inviteRequest");
/* [9]
* Prepare the response we are about to send to the caller.
*/
SipServletResponse sipServletResponse = inviteRequest
.createResponse(SipServletResponse.SC_OK);
/* [10]
* Put our local SDP in the response and send the response.
*/
sipServletResponse.setContent(sdp, "application/sdp");
sipServletResponse.send();
/* [11]
* Set the initial mode of the application
*/
mode = "1";
/* [12]
* And connect the PacketRelay endpoint to an IVR endpoint. The IVR endpoint
* supports a variety of operations like playback, recording, DTMF. Basically,
* when this line is executed we have built the following topology:
* Caller <----MsConnection----> PacketRelay <----MsLink----> IVR
*/
mediaController.createLink(MsLinkMode.FULL_DUPLEX)
.join("media/trunk/IVR/$",
connection.getEndpoint().getLocalName());
}
/* [13]
* The Media Server notifies us that the link we created in step [12]
* was connected successfully. This method is also called from steps [20],
* [21], [22] where we perform a similar link connection operation. This
* method can handle all cases as long as the mode is set.
*/
@Observer("linkConnected")
public void doLinkConnected(MsLinkEvent event) {
if("1".equals(mode)) {
/* [14]
* If we are in IVR mode just play some announcement and detect DTMF.
*/
ivrHelper.playAnnouncementWithDtmf(
"http://mobicents.googlecode.com/svn/tags/servers/media/examples/" +
"mobicents-media-server-examples-1.0.0.GA/mms-demo/web/src/main/" +
"webapp/audio/welcome.wav");
} else {
/* [15]
* Otherwise just detect DTMF so we can keep reacting on DTMF events
*/
ivrHelper.detectDtmf();
}
}
/* [16]
* This callback is called when the caller has pressed a button on her phone.
* Here, we switch between different endpoint to perform different functions.
* Initially, the caller is connected to IVR (through the PacketRelay), but
* we can connect the caller to a Conference endpoint for example as shown below.
*/
@Observer("DTMF")
public void doDtmf(String digit) throws Exception{
log.info("DTMF = " + digit + ", previous mode = " + mode);
/* [17]
* Change the mode to the new digit pressed
*/
mode = digit;
/* [18]
* Some cleanup to release the link that we don;t need anymore (as
* we create a new one). This operations is done automatically in
* newer versions of STF.
*/
mediaSessionStore.getMsLink().release();
/* [19]
* Branch the logic depending on which button is pressed
*/
if("1".equals(digit)) {
/* [20]
* If "1" is pressed build the following topology:
* Caller <----MsConnection----> PacketRelay <----MsLink----> IVR
*/
mediaController.createLink(MsLinkMode.FULL_DUPLEX)
.join("media/trunk/IVR/$",
mediaSessionStore.getMsConnection().getEndpoint().getLocalName());
} else if("2".equals(digit)) {
/* [21]
* If "2" is pressed build the following topology:
* Caller <----MsConnection----> PacketRelay <----MsLink----> Conference
*/
mediaController.createLink(MsLinkMode.FULL_DUPLEX)
.join("media/trunk/Conference/$",
mediaSessionStore.getMsConnection().getEndpoint().getLocalName());
} else if("3".equals(digit)) {
/* [22]
* If "3" is pressed build the following topology:
* Caller <----MsConnection----> PacketRelay <----MsLink----> Conference
*
* The difference from the previous option is only that here we strictly
* connect to the Conference Endpoint 1, while in the previous option we
* connect to the first available (unused) Conference endpoint.
*/
mediaController.createLink(MsLinkMode.FULL_DUPLEX)
.join("media/trunk/Conference/1",
mediaSessionStore.getMsConnection().getEndpoint().getLocalName());
} else if("0".equals(digit)) {
/* [23]
* Here we just end the call if the user presses "0"
*/
sipSession.createRequest("BYE").send();
}
}
@Observer("BYE")
public void doBye(SipServletRequest request) throws Exception {
request.createResponse(200).send();
}
@Observer("INFO")
public void doInfo(SipServletRequest request) throws Exception {
request.createResponse(200).send();
}
@Observer("REGISTER")
public void doRegister(SipServletRequest request) throws Exception {
request.createResponse(200).send();
}
}
The Component Framework
In addition to the Event hooks, there are a few helper classes and components that help you manage the current call context and other unrelated call contexts, which are stored at a central static storage:
- IVRHelper - executes managed MSC-API opertaions. The events and the results from these operations will be delivers as Seam Events.
Usage: @In IVRHelper ivrHelper;
Methods:
void |
detectDtmf()
Detect DTMF on the endpoint specified in mediaSession store using either the MsLink
(higher precedence) or the MsConnection. |
void |
endAll()
Stops any previously executed commands. |
void |
playAnnouncement(java.lang.String file)
Play an announcement to the specified endpoint using either the MsConnection object or
the MsLink object stored in the mediaSessionStore. |
void |
playAnnouncementWithDtmf(java.lang.String file)
Play an announcement AND detect DTMF to the specified endpoint using either the MsConnection object or
the MsLink object stored in the mediaSessionStore. |
void |
record(java.lang.String file)
Record to a file. |
- MediaEventDispatcher - dispatches common MSC-API Media events and produces finer-grained events like DTMF or announcement complete/failed. It also stores useful DTMF stacks accumulated in Media Objects.
Usage: @In MediaEventsDispatcher mediaEventDispatcher
Methods:
java.lang.String |
getDtmfArchive(java.lang.Object object)
Get the DTMF buffer for a given object (SipSession, MsLink or MsConnection) |
java.lang.String |
getDtmfArchive(java.lang.Object object,
int digits)
|
void |
reset(java.lang.Object object)
Reset the DTMF buffer for give object (SipSession, MsLink or MsConnection) |
- IVRHelperManager - central place for all IVRHelpers in the application. Very frequently you need to manipulate not your own context, but some other Sip Session. In this case you can lookup the other Sessions's IVRHelper and to IVR opertaions there. A typical case would be if you are disconnecting the current session and want to play an announcement to some other user that you have left the call and you are no longer busy or similar function.
Usage: IVRHelperManager.instance()
Methods:
- MediaSessionStore - Each Sip Session will have exactly one instance of this class. Here we store the
Media objects associated with the SipSession. We make some attempts to fill in the
objects automatically, but it is recommended that you set them manually.
Usage: @In MediaSessionStore mediaSessionStore
Methods:
- MediaController - Media controller is a low-level per-SipSession entity responisble for the audio RTP stream associated with the SipSession. It corresponds to the MsEndpoint MSC API, but in a Seam-managed way.
From here you can contruct the audio path (or the chain of endpoints) that the RTP will pass through for processing).
You can construct MsLink or MsConnection objects from here that are under Seam Telco Framework management. When a
MsLink or MsConnection is under Telco Framework management the events produced from these objects will be passed
as Seam events and can be captured byt subscribing methods with the @Observer annotation.
You don't have to install your own listeners, the framework will do that for you. Avoid creating MsLinks and
MsConnections with the MSC-API methods when possible.
Usage: @In MediaController mediaController
Methods:
org.mobicents.mscontrol.MsConnection |
createConnection(java.lang.String endpoint)
Create a connection managed by the framework. |
org.mobicents.mscontrol.MsLink |
createLink(org.mobicents.mscontrol.MsLinkMode mode)
Create a link managed by the framework. |
void |
execute(org.mobicents.mscontrol.MsEndpoint endpoint,
org.mobicents.mscontrol.events.MsRequestedSignal[] signals,
org.mobicents.mscontrol.events.MsRequestedEvent[] events)
Execute a a request or signal in a managed way. |
void |
execute(org.mobicents.mscontrol.MsEndpoint endpoint,
org.mobicents.mscontrol.events.MsRequestedSignal[] signals,
org.mobicents.mscontrol.events.MsRequestedEvent[] events,
org.mobicents.mscontrol.MsConnection connection)
Execute a a request or signal in a managed way. |
void |
execute(org.mobicents.mscontrol.MsEndpoint endpoint,
org.mobicents.mscontrol.events.MsRequestedSignal[] signals,
org.mobicents.mscontrol.events.MsRequestedEvent[] events,
org.mobicents.mscontrol.MsLink link)
Execute a a request or signal in a managed way. |
- MediaControllerManager - This is a central repository of all MediaControllers for all SipSessions. You can lookup other SipSessions's MediaControllers to manipulate them similarly to to how you would do with IVRHelperManager.
Usage: MediaControllerHolder.instance()
Methods:
- MediaEvent - This is a structure that carries information associated with an event produced by the Media Server.
The MsNotifyEvent is the original event and the rest are just the related objects for the event -
the link is the MsLink or the MsConnection object where the event occured. The endpoint is the endpoint
where the event occured. And of course the SipSession is the the SIP session where the event occured.
Usage: Delivered as a Seam event @Observer("mediaEvent")
Methods:
More Examples
Download
IDE Development and Setup
The Seam Telco Framework applications can use almost all Seam features including
hot-deployment and JBoss Tools-assisted development. Note that you
don't need JBoss Tools, you can use your own IDE or no IDE with Ant or
Maven or whatever you want.
Here is what you need to get started with JBoss Tools:

- The latest Mobicents Sip Servlets
with JBoss AS 4.2.3 (please do NOT use it with JBoss AS 5.0 for now,
since the Media support there is still in technology preview stage)
- Install the latest JBoss Tools 3.0 or later in Eclipse 3.4 or the nightly build of JBoss Tools (this is the update site)
- you need JBoss Seam, JBoss AS Tools and Richfaces plug-ins as
minimum. You must use the nightly builds for Seam 2.1 support. Soon, a
new JBoss Developer Studio will be released with official Seam 2.1
support. Be sure to read the documentation of the Seam plugin and the rest of the JBoss Tools plugins.
- Get Seam 2.1.1.GA and configure it as Seam runtime in JBoss Tools when asked.
Once you create a Seam 2.1 project you can extend it with the Telco framework by following these steps:
- Get the latest binary of sip-servlet-seam-entrypoint.jar and put in the Web App lib folder (under WebContent/WEB-INF/lib)
- You will also need to add the following jars - mobicents-media-server-constants, mobicents-media-server-api, mobicents-media-server-api-local-impl (take them from here). Bascially you want your project to look like the screenshot on the left. Note that the Web App Libraries folder is just an alias of WebContent/WEB-INF/lib folder in your project. If you manually put jars in WebContent/WEB-INF/lib they will appear in Web App Libraries and they will be packaged in your war file. The jars in Referenced Libraries on the other hand are not packaged with the war, they are just used at compilation time, you should NOT put the files mentined above in the Referenced Libraries folder.
- Put this sip.xml in WebContent/WEB-INF/ :
<?xml version="1.0" encoding="UTF-8"?>
<sip-app>
<app-name>PUT_SOME_APPLICATION_NAME_HERE</app-name>
<display-name>SeamEntryPointApplication</display-name>
<description>SeamEntryPointApplication</description>
<main-servlet>
SeamEntryPointServlet
</main-servlet>
<servlet>
<servlet-name>SeamEntryPointServlet</servlet-name>
<display-name>SeamEntryPointServlet</display-name>
<description>Seam Entry Point Servlet</description>
<servlet-class>
org.mobicents.servlet.sip.seam.entrypoint.SeamEntryPointServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<listener>
<listener-class>
org.mobicents.servlet.sip.seam.entrypoint.SeamEntryPointServlet
</listener-class>
</listener>
</sip-app>
Using Maven
If you want to use Maven, you can simply take one of the demo applications and build on top of it. The demo application are complete and do not require modifying the projects in any way. If you specify environment variable JBOSS_HOME you can deploy by running
mvn clean install -Pdeploy and this will deploy you application. The demo applications are here
http://mobicents.googlecode.com/svn/trunk/frameworks/sip-servlets/seam-telco-framework/examples/
Guidelines
- Do not subscribe methods to SIP and media events in Seam components with SESSION or CONVERSATION scopes! The
reason is that each of these SESSION or CONVERSATION scoped components
is likely to have multiple instances (depending on the number of the
sessions) and they all will be called, which is probably not what you
want.
- Always subscribe public methods to SIP and media events. Any other access modifier will cause you method not to be called.
- Whenever you want a Session-scoped component that is shared between HTTP and SIP you can just use the same component name, but assign values from different components responsible for the particular protocol. For example, when you want a shared Identity between SIP and HTTP users, you could simply have a HTTP authenticator and SIP authenticator that initialise the Session0scoped component "user" in both sessions. The rest of the application will not care where it came from (HTTP and SIP).
- Avoid using @Begin and @End for tx or conversation boundaries in SIP components. It won't work. For HTTP components it will work fine.
- Avoid using application-scoped componnets and injecting other components from finer-grained scopes (Session or Method, etc). The injection might not occur properly, because the memober might be flagged as injected already, be sure to test it.
Advantages
- All the Seam goodness.
- Development is easier, because you can use the Seam
WEB-INF/dev
classloader to deploy Seam components faster. Otherwise you have to
redeploy the whole war/ear (10-20 secs vs 1-2 secs).
- Testing - Seam provides a great framework for testing based on dependency injection and mocking.
- Programming model is practically the same as the one you use for Web apps with Seam and similar to the future Web Beans.
- Potential to integrate with jBPM and JBoss Rules through Seam. (contributors?)