Friday, January 23, 2009

Hacking the JBoss 5 deployers to load Sip Servlets Applications

After a great Christmas break, I came fully restored and since JBoss 5.0.0.GA was released on the fifth of December 2008, so out for about a month. Since the JBoss 5 features a great new shiny architecture :
So I decided it was a really good time to make Mobicents Sip Servlets able to work on JBoss 5. So I took a look at a very good blog post from Bob McWhirter to get a first understanding of how the JBoss 5 deployers worked together to deploy a .war archive and came up with a similar design to extend the existing deployers so that JBoss 5 is now able to deploy Sip Servlets and Converged HTTP/Sip Servlets applications :

So I hacked away and about a week later, Mobicents Sip Servlets was passing the Sip Servlets 1.1 TCK on top of JBoss 5, in addition to Tomcat 6.0.14 and JBoss 4.2.3 !
You can get the nightly snapshots binaries on our hudson job if you want to try it out and give us some feedback on mobicents-public google groups

It allowed us to refactor a bit Mobicents Sip Servlets to be more modular on plugging to various containers (such as Tomcat and JBoss) so it is just a matter of maven profiles now to build Mobicents Sip Servlets on top of Tomcat or various major versions of JBoss. With even more refactoring, I'm sure we could succeed to run Mobicents Sip Servlets on top of Jetty or why not Glassfish, but that's another story ;-)

This gives us a very great foundation to experiment on top of JBoss 5, such as Building converged telco applications integrated with JRuby by example so stay tuned for more in the coming months ! :-)

You can find more details here on what currently happens in the design diagram above when loading a sip servlets application :

Note : I copied most of the content of Bob McWhirter's blog post and adapted it to show how the sip.xml and annotations are parsed and injected and combined with the other existing MetaData :

"JBoss MicroContainer look in WEB-INF/ for meta-data descriptors, such as sip.xml, web.xml and jboss-web.xml. This is where true deployment of components starts. Deployment runs through a series of stages, with deployers setup to match particular files and stages, doing the right things at the right time.

One of the earliest stages is the PARSE stage. A deployer can be bound to this stage to be given an early chance to match, parse, and act upon any meta-data file. For normal WAR deployment, the WebAppParsingDeployer does exactly that. There’s a nice hierarchy of classes to make parsing XML descriptors such as web.xml super simple.

For our Sip Servlets case we are using SipAppParsingDeployer to parse the sip.xml descriptor and create the corresponding hierarchy of JAXB classes and reusing some of the existing web.xml ones when it is possible (ie when the sip_app.xsd reuses some javaee types from the web_app_2_5.xsd). It is indeed super simple :-)

The WebAppParsingDeployer is the bridge from a web.xml file sitting on the filesystem or in an archive to the MetaData deployment bits. The parser reads web.xml, and produces a WebMetaData object associated with the deployment. The WebMetaData is simply a nice object-tree representing anything you can denote in web.xml.

Same thing here in our case, we use a SipAppParsingDeployer. The parser reads sip.xml, and produces a SipMetaData object associated with the deployment. The SipMetaData is simply a nice object-tree representing anything you can denote in sip.xml.

We also might have a jboss-web.xml meta-data in our WAR, and that is parsed (during the PARSE stage) in our case by the JBossConvergedSipAppParsingDeployer (instead of JBossWebAppParsingDeployer). This deployer, like the previous, reads the jboss-web XML file and creates, in this case, a JBossConvergedSipMetaData (instead of JBossWebMetaData) object. It is also gathering the previous MetaData (SipMetaData and WebMetaData) and merged them with itself.

Once we’ve parsed these .xml files, the container has enough information to build up the classpath for the component. Some of these deployers have also thrown off or modified some ClassLoadingMetaData, which describe paths that should be added to the classpath.

As the container enters the CLASSLOADER stage of deployment, other magic occurs to actually set up the classpath.

After that, There is annotations parsing through ConvergedSipAnnotationMetaDataDeployer that produces SipMetaData and WebMetaData also but based on the annotations instead of the xml descriptors this time

In the end, it’s the JBossConvergedSipMetaData (instead of JBossWebMetaData) that drives the ultimate deployment, but what if we don’t have a jboss-web.xml? That’s where the MergedJBossConvergedMetaDataDeployer (instead of MergedJBossWebMetaDataDeployer) comes in. It looks for SipMetadata (parsed from the sip.xml and the annotation based one), WebMetaData(parsed from the web.xml and the annotation based one), and a JBossWebMetaData if one has been parsed, and merges everything into a singular JbossConvergedSipMetaData (instead of JBossWebMetaData).

I’m a little fuzzy on the ins and outs of the CLASSLOADER stage at this point, but magic occurs there.

And our app still isn’t deployed yet. But we’re getting there.

Finally, we enter the REAL stage of deployment, which fittingly-enough, is where the actual deployment occurs. Hooray!

Our TomcatConvergedDeployer (instead of TomcatDeployer) that is sip servlets aware is hanging out there, waiting for JBossWebMetaData objects to appear. When it sees one, it checks if the application is a sip servlets application (by checking if a sip.xml descriptor or Sip Servlets annotations are present), if it is not it deploy as a standard web app otheriwse it goes to work setting up information for our extended Tomcat (that is sip servlets aware) to deploy a sip-app. It configures everything in Tomcat from the information other deployers figured out from sip.xml, web.xml, jboss-web.xml and annotations and embodied in the MetaData by creating a TomcatConvergedDeployment (instead of TomcatDeployment for a standard webapp).

This TomcatConvergedDeployment also sets up the private jndi for the webapp and binds the needed objects to it (like SipFactory, TimerService and SipSessionsUtils). It also set the InjectionContainer, TomcatConvergedSipInjectionContainer(instead of TomcatInjectionContainer) so that needed objects can be injected into annotated attributes of the archive.

It jams it into Tomcat, hits the big red “go” button, and port 5080 is serving you sip-app (and port 8080 also if this is a HTTP/SipServlets converged application).

Finally. :-)