Adobe Flex Messaging (or BlazeDS) Vs. OSGi
Posted on March 25, 2009
by Tommy McGuire
Although I have not finished my series on creating JRuby on Rails applications using OSGi and the SpringSource dm Server, I have moved on a bit to converting a small number of Adobe Flex applications to the server platform. (Adobe Flex, the Flash-based application development platform. Not flex, the lexical analyzer thing that goes with Bison.)I do not want to go into all of the details of our framework/stack/platform/thingy that I am adapting the Flex applications for, but I do want to provide a quick description of what I needed to do to deploy the Flex app in our framework.
SSdmS will deploy any .war file I have run across, but to integrate it into the OSGi environment I did need to add some metadata to the MANIFEST.MF:
Bundle-SymbolicName: my.webapp
Bundle-Version: 2.0.0
Bundle-Name: webapp
Bundle-Classpath: WEB-INF/classes,WEB-INF/lib/my.servicelocator-0.0.3.jar
Import-Package:
javax.servlet;specification-version="[2.4.0,3.0.0)",
javax.servlet.http;specification-version="[2.4.0,3.0.0)",
org.osgi.framework;specification-version="[1.4.0,2.0)",
org.osgi.util.tracker;specification-version="1.3.3",
...
flex.messaging; version="[3.0.0.544,4.0)"
Bundle-Activator: my.osgi.OsgiServiceProvider
Module-Type: Web
Web-ContextPath: /webapp
Eclipse-RegisterBuddy: flex-messaging
Most of the additions are typical: Bundle-Name, Bundle-SymbolicName, etc. The Bundle-Classpath I modified to add a service locator package that provides the interface between our application and our framework. That package also provides the Bundle-Activator and requires a small stack of packages to be imported. Module-Type and Web-ContextPath are extensions for the SSdmS. Eclipse-RegisterBuddy I will get to in a minute.
Note: On my OSGi wishlist is a way to look into contained jar files to find Import-Package statements that should be added to the containing jar file. For example, org.osgi.framework is not needed by any of the application code, but is needed by the my.servicelocator jar; it would be nice if the requisite Import-Package statements did not have to live in the application's manifest. (And yes, the service locator kind of needs to live in the application bundle, although I might be able to revisit that.)
The one package I am importing there that is important is flex.messaging, which comes from a flex-messaging bundle. The flex.messaging package provides access to everything this application needs from the Flex messaging system, specifically flex.messaging.MessageBrokerServlet and flex.messaging.HttpFlexSession, both mentioned in the web.xml configuration file.
What does the flex-messaging bundle look like? Here is the bnd file I used to create it:
-classpath: \
../orig/flex-messaging-common.jar, \
../orig/flex-messaging-core.jar, \
../orig/flex-messaging-opt.jar, \
../orig/flex-messaging-proxy.jar, \
../orig/flex-messaging-remoting.jar, \
../orig/cfgatewayadapter.jar
Bundle-SymbolicName: flex-messaging
Bundle-Name: BlazeDS - Common Library
Bundle-Version: 3.0.0.544
Export-Package: \
!coldfusion.*, \
*;version=${Bundle-Version};-split-package:=merge-first
Import-Package: \
coldfusion.flex.rmi; resolution:=optional, \
com.ibm.*; resolution:=optional, \
edu.emory.mathcs.backport.java.util.concurrent.*; version="[3.0.0,4.0)", \
flex.data.messages; resolution:=optional, \
jrun.*; resolution:=optional, \
jrunx.*; resolution:=optional, \
oracle.security.jazn.*; resolution:=optional, \
org.apache.commons.httpclient.*; version="[3.0.1,4.0)", \
org.apache.xpath.*; version="[2.7.0,3.0)", \
org.jgroups.*; resolution:=optional, \
weblogic.*; resolution:=optional, \
*
Private-Package: coldfusion.*
Eclipse-BuddyPolicy: registered,dependent
Yes, I am sucking all of the gunk from a stack of jar files into this single bundle: all of the Flex messaging stuff seems to be fairly closely interrelated and I don't see any of our applications using any single part of it without also needing most. I am exporting all of the flex.* packages and importing the stuff I recognize at specific versions and the stuff I have never heard of optionally.
Now, I maintain a list, the Big List o' Evil. On this list, I put things that I see as, well, evil. Not just small evil, though, like cell phones and black licorice. Big evil, like persistent objects, Sun's Access Manager stuff, and (important in this case) remote procedure calls. RPC (and related things like CORBA and SOAP) are not only the wrong way to do networking, they are seductive. They seem to work, they seem to make things easy, then they rip off your leg, rub the bloody stump in margarita salt, and set out to club your puppy with your own ex-appendage.
In this particular case, Adobe Messaging Framework is serializing an object on the browser, Flash side and sending the serialization across the wire to be re-inflated into a Java object on the server side. The specific problem is that the de-serialization code needs access to a class from the application bundle, but the de-serialization code lives in the flex-messaging bundle and has no way to get at the application's class. ClassNotFound! (The new "Fail!")
Anyway, adding the "Eclipse-BuddyPolicy: dependent" header to the flex-messaging solves the problem by reversing the incoming links to the flex-messaging bundle: when it needs an application class, it (eventually) follows the "flex.messaging" imports back to the application bundle and finds the application class. To do this, it is using an Eclipse Equinox-specific hack, although I understand the new standard work from OSGi might possibly address something like this issue.
I added the "registered" buddy policy and the Eclipse-RegisterBuddy header in an attempt to speed up the lookups. It also marks the application bundle as somehow special. (A scarlet RegisterBuddy?)
[Later....]
Yes, and I just got bitten by what I think is the Class.forName caching problem. Buddy classloading works fine, until the web application is updated without restarting the server. Then message de-serialization fails silently until the server is restarted. I have checked the source, and BlazeDS is using Class.forName.
I suspect at this point I am hosed and every Flex web application is going to have its own copy of the assorted messaging jars.