Showing posts with label jboss seam. Show all posts
Showing posts with label jboss seam. Show all posts

Friday, July 10, 2009

Join us at JBossWorld 2009 in Chicago








Once again, someone made a terrible mistake and I will be leading the BOF session on Rapid VoIP development - SIP Servlets 1.1, Seam Telco Framework, JRuby on Rails, Eclipse VoIP tooling. They just never learn :)

The talk is on September, 2nd at 5:45 PM. It seems a bit late, but knowing the JBoss parties that occur during summits, that's actually early and a very good slot. The other good news is that I will have Jean Deruelle and some guests with me. Jean also has another talk on Telco 2.0 on Friday in the What's next track. It is worth mentioning that this year JBossWorld and Red Hat Summit take place at the same time on the same location and visitors have have access to both with their passes.

In my BOF session, we will begin with a short presentation and demos how to start developing VoIP and Converged applications with Mobicents from scratch with our telco application frameworks/platforms focusing on Seam Telco Framework and the IDEs. We will move on to some real-world examples. We will have a lot of time for questions and discussion on other Mobicents topics, VoIP technology and the current trends in the market. I hope it will be a great experience for everybody.

As always, if you are around Chicago, we can do another Mobicents user meeting somewhere.

Update: Early bird registration extended until 17th.

Sunday, May 31, 2009

Time for Seam Telco Framework 2.1

The Mobicents application framework for SIP and Media on top of Seam is now officially named Seam Telco Framework (STF) and it just reached version 2.1!

There have been a number of changes and fixes since the last public version and the current 2.1 version must be stable enough to be consumed by all users.

The goal is still the same - to unify the programming model for Telco and JEE application. A somewhat new perspective for the framework is to minimize the new APIs and reuse as many standard or established APIs as possible (JSR-289, MSC or JSR-309) in order to keep a flat learning curve. You can think of it as something using the Seam infrastructure to expose these APIs to your application. For example the SIP messages and the media notification are delivered through Seam events in the context of a JSR-289 Sip Servlets Session. The Sip Servlets Session itself is backing the Seam SESSION scope context similarly to how the HTTP sessions work in Seam. Additionally, most of the framework objects are available and exposed through the Seam IoC and scoped at the right level. The STF simply plugs into Seam and reuses whatever makes sense in the SIP world. By the same logic, Seam uses that same infrastructure to expose the other APIs from the diagram (JEE, JBoss Frameworks and others) in certain roles - JSF for presentation, jBPM for flow and navigation, Drools for security and so forth.

We also want to highlight the point that most major IDEs already have support for Seam core syntax. You can use the same IDE tools to code SIP components without any extra plugins. Again, everything from STF is exposed through the Seam core infrastructure as events, components or scopes.

You should note the following changes:
  • The new documentation is here.
  • This source code of the latest stable release 2.1 is here (where the examples are stable).
  • The source code trunk has moved to here (including the dev examples).
  • The media framework is bundled.
  • The Connection and Link IVR helper classes are unified under a single IVRHelper class now. You can check the examples and the documentation for more information.
To have quick glance let's dive into the familiar conference IVR example:
@Scope(ScopeType.STATELESS)
public class MediaFrameworkDemo {
@Logger Log log;
@In MediaController mediaController;
@In SipSession sipSession;
@In MediaSessionStore mediaSessionStore;
@In IVRHelper ivrHelper;
@In MediaEventDispatcher mediaEventDispatcher;

@In(scope=ScopeType.APPLICATION, required=false)
@Out(scope=ScopeType.APPLICATION, required=false)

String conferenceEndpointName;

private final String announcement =
"http://mobicents.googlecode.com/svn/branches/servers/media/1.x.y/examples/mms-demo/web/src/main/webapp/audio/welcome.wav";

@Observer("INVITE")
public void doInvite(SipServletRequest request) throws Exception {
// Extract SDP from the SIp message
String sdp = new String((byte[]) request.getContent());

// Tell the other side to ring (status 180)
request.createResponse(SipServletResponse.SC_RINGING).send();

// Store the INVITE request in the sip session
sipSession.setAttribute("inviteRequest", request);

// If this is the first INVITE in the app, then we must start a new conference
if (conferenceEndpointName == null)
conferenceEndpointName = "media/trunk/Conference/$";

// Create a connection between the UA and the conference endpoint
mediaController.createConnection(conferenceEndpointName).modify("$",
sdp); // also updates the SDP in Media Server to match capabilities of UA
}

@Observer("connectionOpen")
public void doConnectionOpen(MsConnectionEvent event) throws IOException {
// Save this connection where the framework can read it
// mediaSessionStore.setMsConnection(event.getConnection());// This is done automatically in STF 2.0

// The conference endpoint is now assiged after we are connected, so save it too
conferenceEndpointName = event.getConnection().getEndpoint()
.getLocalName();

// Recall the INVITE request that we saved in doInvite
SipServletRequest request = (SipServletRequest) sipSession
.getAttribute("inviteRequest");

// Make OK (status 200) to tell the other side that the call is established
SipServletResponse response = request.createResponse(SipServletResponse.SC_OK);

// Put the SDP inside the OK message to tell what codecs and so on we agree with
response.setContent(event.getConnection().getLocalDescriptor(),
"application/sdp");

// Now actually send the message
response.send();

// And start listening for DTMF signals
ivrHelper.detectDtmf();
}

@Observer("DTMF")
public void dtmf(String button) {
// If the other side presses the button "0" stop the playback
if("0".equals(button)) {
ivrHelper.endAll();
} else {
// otherwise play announcement
ivrHelper.playAnnouncementWithDtmf(announcement);
}
// Also log the DTMF buttons pressed so far in this session
log.info("Current DTMF Stack for the SIP Session: "
+ mediaEventDispatcher.getDtmfArchive(sipSession));
}

@Observer( { "BYE" })
public void doBye(SipServletRequest request) throws Exception {
request.createResponse(200).send();

// And clean up the connections (not really required, because there is automatic cleanup in STF 2.1)
MsConnection connection = mediaSessionStore.getMsConnection();
connection.release();
}

@Observer("REGISTER")
public void doRegister(SipServletRequest request) throws Exception {
request.createResponse(200).send();
}

}
This is how you can do conferencing and IVR just by subscribing a few methods to a few events without implementing any callback interfaces or keeping track of how the media and SIP events are related. You will notice some differences and comments. There is significant effort by STF to automatically assign and cleanup media objects when it's clear what is expected or when they should not be used any more (for example they are cleaned up when the SIP session is destroyed). Overall we just try to keep the glue code out of your application or at worst keep the glue in the metadata.

What happened with version 2.0?

Version 2.0 is was released without announcment only to solve particular problems without being fully tested. The 2.1 version has some fixes on top of 2.0 and the API changes are explained in the documentation. If you are 2.0 user all APIs are back-compatible and you should be able to switch to 2.1 without any effort.

As usual, any suggestions and feedback are welcome!

Tuesday, February 3, 2009

Rich Telco Applications with Seam

UPDATE: The post was edited for clarity.

The Mobicents Seam-based Sip Servlets framework was extended with Media functions and now goes beyond SIP to unify the component models for Telco and Web applications. With this move, we are addressing the need to build quickly Media-intensive applications like PBX, conferencing, Interactive Voice Response (IVR), transaction confirmation and others.

With the help of the Seam development tools these applications can be written and tested in no time, so even if you don't want to use the Seam model in your production applications, you will find it useful for smaller "disposable" applications, or for rapid prototyping and proof-of-concept applications.

Need a conferencing application?

@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();
}

}

That's all. No cheating.

What is happening here is that when an INVITE comes (i.e. when you dial the server), we connect the call to a conference endpoint in Mobicents Media Server. If this is the first call, the conference endpoint is not assigned yet (null) and we allocate a new endpoint (with .../Conference/$) for the application. Once the connection is established, the connectionOpen event occurs, then we store the conference endpoint name at the application scope and when the subsequent calls in this application see it they will connect to the same endpoint. We can easily extend this application to work with multiple conferences or add other media functionality. All SIP and Media events occur in a SIP Session, which is basically one call from one user (or one phone). If you want to share data in the SIP session scope simply inject the SipSession as shown in the application or create a SESSION-scoped Seam component. This conference example is available here. If you want to understand Mobicents Media Server and the MSC API you should read the Mobicents Media Server Guide.

In addition to the basic media support we are aiming to simplify the most common use-cases for media applications. While developing media applications I noticed that one SIP call is always constructed like this:

or the more simple chain:

In both chains the call is terminated at some media endpoint - IVR or Conference or an Announcement endpoint (which is not shown in the diagrams). After the call is established, the user agent is connected to exactly one media endpoint at any time, either directly or through a Packet Relay endpoint. This means that we can safely store all these links and endpoints related to the call right into the SIP session and never worry again about how to pass them to another method or component. If you need to switch to another chain just keep the session data updated. That's why we are looking into reserving a special place in the SIP session for the media objects.

To understand it better let's look at another example:
@Name("mediaFrameworkDemo")
@Scope(ScopeType.STATELESS)
public class MediaFrameworkDemo {
@Logger Log log;
@In MediaController mediaController;
@In SipSession sipSession;
@In MediaSessionStore mediaSessionStore;
@In ConnectionIVRHelper connectionIVRHelper;
@In MediaEventDispatcher mediaEventDispatcher;

@In(scope=ScopeType.APPLICATION, required=false)
@Out(scope=ScopeType.APPLICATION, required=false)

String conferenceEndpointName;

private final String announcement =
"http://mobicents.googlecode.com/svn/branches/servers/media/1.x.y/examples/" +
"mms-demo/web/src/main/webapp/audio/welcome.wav";

@Observer("INVITE")
public void doInvite(SipServletRequest request) throws Exception {
// Extract SDP from the SIp message
String sdp = new String((byte[]) request.getContent());

// Tell the other side to ring (status 180)
request.createResponse(SipServletResponse.SC_RINGING).send();

// Store the INVITE request in the sip session
sipSession.setAttribute("inviteRequest", request);

// If this is the first INVITE in the app, then we must start a new conference
if (conferenceEndpointName == null)
conferenceEndpointName = "media/trunk/Conference/$";

// Create a connection between the UA and the conference endpoint
mediaController.createConnection(conferenceEndpointName).modify("$",
sdp); // also updates the SDP in Media Server to match capabilities of UA
}

@Observer("connectionOpen")
public void doConnectionOpen(MsConnectionEvent event) throws IOException {
// Save this connection where the framework can read it
mediaSessionStore.setMsConnection(event.getConnection());

// The conference endpoint is now assiged after we are connected, so save it too
conferenceEndpointName = event.getConnection().getEndpoint()
.getLocalName();

// Recall the INVITE request that we saved in doInvite
SipServletRequest request = (SipServletRequest) sipSession
.getAttribute("inviteRequest");

// Make OK (status 200) to tell the other side that the call is established
SipServletResponse response = request.createResponse(SipServletResponse.SC_OK);

// Put the SDP inside the OK message to tell what codecs and so on we agree with
response.setContent(event.getConnection().getLocalDescriptor(),
"application/sdp");

// Now actually send the message
response.send();

// And start listening for DTMF signals
connectionIVRHelper.detectDtmf();
}

@Observer("DTMF")
public void dtmf(String button) {
// If the other side presses the button "0" stop the playback
if("0".equals(button)) {
connectionIVRHelper.endAll();
} else {
// otherwise play announcement
connectionIVRHelper.playAnnouncementWithDtmf(announcement);
}
// Also log the DTMF buttons pressed so far in this session
log.info("Current DTMF Stack for the SIP Session: "
+ mediaEventDispatcher.getDtmfArchive(sipSession));
}

// Just say OK to these messages.
@Observer( { "BYE", "REGISTER" })
public void sayOK(SipServletRequest request) throws Exception {
request.createResponse(200).send();

// And clean up the connections
MsConnection connection = mediaSessionStore.getMsConnection();
connection.release();
}

}

This is almost the same conference application with some IVR capabilities and some comments between the lines. The MediaSessionStore stores the call-related media objects (the MsConnection in this case) and ConnectionIVRHelper will read it from there when it is doing DTMF detection or playing announcement.

Once you are connected to the conference the application works like this - when you press a button 1-9 it will play a personal announcement (only the user who pressed the button can hear it). If the users presses "0" the announcement will be stopped. Note that MediaSessionStore, MediaEventDispatcher and ConnectionIVRHelper are not part of the framework right now, but you can copy and paste them into your own application from the media framework demo application in SVN (the discussed example). Eventually the classes from the org.mobicents.servlet.sip.seam.session.framework package will be moved into the main framework jar.

Development


If you've read the previous post about the Seam framework, you would already know that these 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:
  • Mobicents Sip Servlets 0.7.2 with JBoss AS 4.2.3 or Mobicents Sip Servlets 0.8 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 nightly build of JBoss Tools in Eclipse 3.4 (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.
  • 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:
<?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>
  • Now you are done. Just start adding Seam components.
Another way to get started is to checkout one of the examples mentioned above and play with them without IDE support (but it requires Maven).


Some 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.
  • When dealing with JPA, always use your own EntityManager. Either EVENT or METHOD scoped or manage it manually through the EntityManagerFactory. The default CONVERSATION-scoped entityManager might produce "EntityManager closed" errors.
  • When initiating a SIP request from a Web session, do it in another thread! Seam uses thread-local storage and the Web contexts will collide with the SIP contexts. We are working on solving this issue and will probably be addressed in the future.
  • Keep in mind that outjection occurs at the end of a method call. If you attempt to use an outjected variable from a nested method call, it will fail.
Any feedback or contributions are welcome!

Thursday, November 20, 2008

JBoss Seam enhances Sip Servlets

Seam-managed SIP components. How about that? Seam components can now handle real SipServletRequests while taking advantage of the Seam perks - bijection, scoping, transaction management, interceptors and everything else.

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*. 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.

How it works?

The Seam 2 core is now mostly independent of HTTP Servlets and JSF. Basically we hooked a controller Sip Servlet and session listeners to the Seam lifecycle to control the context assignment for incoming SIP messages. When a SIP message arrives, we raise Seam events to notify the Seam components subscribed to receive the SIP events. We had to use a small Java reflection hack to reach some protected Seam contexts, but I believe it is safe. We will try to make it more clean.

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 I have only tested it on Mobicents Sip Servlets 0.6/0.7 with JBoss Application Server 4.2.3 and Seam 2.1.0.SP1.

One 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 incrementMessageCounter() {
sessionMessageCounter.increment();
log.info("Processed SIP messages "
+ sessionMessageCounter.getMessages());
}

@Observer("INVITE")
public void doInvite(SipServletRequest request)
throws Exception {
incrementMessageCounter();
request.createResponse(180).send();
Thread.sleep(100);
request.createResponse(200).send();
}

@Observer("ACK")
public void doAck(SipServletRequest request)
throws Exception {
incrementMessageCounter();
}

@Observer({"REGISTER","BYE"})
public void sayOK(SipServletRequest request)
throws Exception {
incrementMessageCounter();
request.createResponse(200).send();
}

@Observer({"RESPONSE"})
public void sayOK(SipServletResponse response)
throws Exception {
incrementMessageCounter();
}
}
You should keep in mind that the Seam events are application-wide. If you need cross-application messaging use JMS (which accidentally is also effortless with Seam).

And here 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.

Why use it?
  • All the Seam goodness.
  • Development will be easier, because you can take advantage of 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). This feature is not perfect yet.
  • 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?)

Why not to use it?
  • It is not standards-based.
  • Performance is suffering a bit with Seam.
You can find the sample application in SVN:
http://mobicents.googlecode.com/svn/trunk/servers/sip-servlets/sip-servlets-seam

We will soon provide a kit for integrating this capability in your applications. Until then, use the sample application.



*(We currently have no use for conversation scope in SIP.)