Tuesday, March 16, 2010

Since Spring 2.0, Spring supports custom namespace handlers. This feature is really useful and can be used to reduce the verbosity of your spring based applications. Spring-DM has brought this feature along, so it's also available in an OSGi environment. Unfortunately, the integration of those namespace handlers leads to a lot of problems, mostly due to the legacy spring way of configuring the handlers which does not map well at all in OSGi.

In a standard environment, Spring lives in a simple classloader. The classloader which is used to create the spring application is not supposed to change at any time, and everything that is needed for the application is supposed to be available from that classloader. So let's see what happens with custom namespace handlers. Spring detects custom namespace handlers through the presence of a file in the following location META-INF/spring.handlers. This file contains the name of the class supporting a given namespace. In a non OSGi environment, spring detects the namespace handlers by iterating through the resources and finding all these files. However, in an OSGi environment, things must be done a bit differently. The direction which has been taken by Spring-DM is to watch all started bundles and check for this META-INF/spring.handlers file. The custom namespace is then registered and available for use. This leads to multiple problems.

The first one comes from the fact that there is now way to express a dependency onto a "started" bundle. The OSGi specification defines the Require-Bundle header and Import-Package, but both headers do not care about the fact that the bundle is started or not, only that it is resolved. This means that when you deploy your bundles, you need to control the start order of all your bundles to make sure that all custom namespace handlers are started before any application that use them. Failing to do so will result in applications not being started and the solution is to manually restart the bundle. This really goes against the OSGi best practices.

The second problem comes is caused by the non-staged approach in the parsing of the spring xml configuration files. The Spring-DM extender parses the xml and each time a non standard element is found, try to locate the appropriate namespace handler in its internal registry and delegate to that namespace. Once again, this does not work well in OSGi because namespace handlers can come and go and you can't be sure that the namespace are available at the time your bundle start.

The next problem is related to classloading. The namespace handler does not come with its own classloader than can contribute to the application (remember spring has been designed to work with a single static classloader). This means that in order to use a custom namespace, the classloader of the bundle needs to be able to access all the classes that the custom namespace handler will refer to. If you have ever tried to use CXF in OSGi, you know what I mean: you need to import dozens of packages for your application to start to allow the namespace handler to find the classes it needs.

Another limitation is that custom namespace can't be versioned with Spring-DM and it may lead to having to define different namespaces to solve class-related compatibility issues. Let's say you have a namespace foo. This namespace allows to to create an interface named bar (version 1.0). Later, you have a revision of this package . This new version of the interface may or may not be compatible with the previous 1.0 version, but this has nothing to do with the namespace or xml schema itself. With Spring-DM, you need to version your schema because you can't have two namespace handlers supporting the same namespace. This may be problematic because you end up with two solutions. The first one is to always change your namespace (by including the version in the namespace), which means all the bundles using your namespace need to change their xml configuration even it it was compatible. The other solution is to not allow two versions to live in the same OSGi environment. This is simply not possible unless you fully control everything and this goes against the OSGi very goal.

I really don't blame the Spring-DM guys: I think their goal was to be fully backward compatible and that's what they achieved. Sadly, the end result does not behave well in an OSGi environment.

Now, the nice part is that the Apache Aries Blueprint implementation which I've worked on for quite some time now does a much better job at handling those custom namespaces. All four problems have been solved by leveraging OSGi. The implementation leverages the service registry for custom namespace handlers, each bundle containing a custom handler registering its own service. When starting an application, the blueprint extender will make a first pass on the xml configuration files and detect which namespace are used, then put the application in a waiting state until all namespace handlers are available. Each namespace handler can load its own classes thus avoiding the need for the blueprint bundle to import classes that it's not really aware of (because it uses a custom xml namespace and not plain beans). Last, by ensuring class-space consistency, a much cleaner versioning can be done on the various namespaces.