Running Spring Applications on OSGi with the SpringSource Application Platform

Rob Harrop

A lot of people have been asking what exactly the SpringSource Application Platform does for Spring applications to make them run well under OSGi, over and above what you can get out of the box with OSGi and Spring Dynamic Modules. Adrian's post yesterday highlighted some of the general issues, now lets look at a few of the details.

The three most challenging aspects of running Spring applications on OSGi are:

  • Load-time weaving
  • Classpath scanning
  • Thread context classloader management

The remaining, but less interesting, issues include: JSP support, TLD scanning, annotation matching and resource lookups. Overall, there was a decent-sized set of issues that needed to be solved to make applications deploy smoothly.

Load-time weaving

Load-time weaving was one of the most problematic features to support in a robust manner. At the basic level, it requires hooking into the Equinox ClassLoader so that standard ClassFileTransformers can be attached and used during the defineClass calls. On top of this, many uses of LTW require access to a throwaway ClassLoader that can be used to inspect types to decide what needs to happen during the weave, without affecting the real ClassLoader.

This base level of support was actually reasonably simple to achieve. The difficulty comes in when weaving is driven by classes in one bundle, but classes in another bundle need to be woven. This is pretty common in enterprise applications where one bundle contains domain entities and another contains types that use a JPA EntityManager. The Platform takes care of this complexity by ensuring that all bundles in an application can be woven with the appropriate ClassFileTransformers.

When you start propagating weaving across to other bundles, you really need to know when to stop. If you simply apply weaving to all bundles, then applications will interfere with each other. The Platform prevents this from happening by explicitly scoping weaving so that it applies only to modules in the application.

Another issue with LTW is that it complicates refresh. When a bundle is refreshed, OSGi will refresh all the bundles that depend on it. This means that, in the example I gave above, refreshing the domain bundle will cause the EntityManager bundle to be refreshed. However, refreshing the EntityManager does not refresh the domain bundle, meaning that weaving is possibly out of sync. The Platform handles this by propagating refresh to other bundles that are affected by weaving.

Classpath scanning

With classpath scanning, the main issue is that Equinox doesn't expose standard jar: and file: resources. The Platform puts an adapter in the middle so that libraries see the resource protocols that they expect. This has a nice side-effect of making a lot of third-party libraries work - it's not just a fix for classpath scanning.

Thread context classloader management

Many third-party libraries use the thread context ClassLoader to access application types and resources. Each bundle in OSGi has it's own ClassLoader, so therefore, only one bundle can be exposed as the thread context ClassLoader at any time. This means that if a third-party library needs to see types that are distributed across multiple bundles, it isn't going to work as expected.

The Platform fixes this by creating a ClassLoader that imports all the exported packages of every module in your application. This ClassLoader is then exposed as the thread context ClassLoader, enabling third-party libraries to see all the exported types in your application.

This is just a small cross-section of the issues that are addressed by the Platform but hopefully it gives you an idea of what the Platform means for Spring Framework users.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • TwitThis
 

13 responses


  1. Interesting to see how you're solving some of these problems. One question, does your auto-importing ClassLoader update dynamically as bundles are registered and deregistered? Have you seen any issues around this? I can imagine a lot of memory leak scenarios with classes being endlessly loaded by this ClassLoader in a long-running server scenario.


  2. Colin,

    There are certainly some hairy situations with memory leaks. We are in the process of tracking them all down, but I doubt that they will prove to be insurmountable :)
    Rob


  3. Hehe, I imagine so. So your ClassLoader does actually dynamically update as bundles are added and removed? Have you had any issues with removing or replacing bundles? We had some tricky issues around these sorts of issues with our persistence layer, do you just force a refresh in those circumstances?


  4. Colin,

    OSGi handles pretty much all the ClassLoader updates as bundles come up and go down. All we need to do is monitor for scenarios where refresh has to propagate to other bundles or to the application as a whole. So, it's not so much that we have a ClassLoader that updates when bundles come up, but rather we get new ClassLoaders when bundles are replaced, and updates cascade as needed.

    Rob


  5. hi does this classloader have visibility to the other classes in the bundle


  6. You mention that you have a bundle importing 'abc.xyz' but not where it is exported from. The CCL will import all exported packages for all bundles in a PAR file but not for all bundles across the framework.

    Rob


  7. Are all your bundles in the same PAR?

    Where in time are you accessing the TCCL?

    Can you write the the toString() of the TCCL out to console? This contains relevant information about which loader is being used.


  8. This is my scenario

    I have three bundles in all
    Bundle datalayer with the export of package of abc.xyz

    Bundle JMS with a class MYListener(being exported)
    in the constructor of MYListener i am trying to get the instance of abc.xyz.MyClass
    Thread.currentThread().getContextClassLoader().loadClass(
    "abc.xyz.MyClass").newInstance();

    Bundle PAX with a class MYClass where in init block i am getting new instance of MYListener using new MYListener();

    When i printed the TCCL
    System.out.println(Thread.currentThread().getContextClassLoader());

    it came out to be BundleDelegatingClassLoader for [PAX Bundle (PAX)]


  9. That output means that the TCCL is the CL for one of your bundles, not the dm Server generated CL. To take advantage of the TCCL support you need to package all your bundles inside a PAR file and then deploy them that way.

    You can find information on PAR files in the Programmer's Guide: http://static.springsource.com/projects/dm-server/1.0.x/programmer-guide/html/ch05s02.html#developing-applications-packaging-par

    Rob


  10. Thanks

    out of curiosity ,what is TCCL set to for each bundle and what all is visible to it


  11. That really depends on what thread you are on and what the entry point to your unit of work is.

    For single bundle web applications, the TCCL is the CL of the bundle itself. For other units of work that are dispatched by dm Server the same is true. For user-created threads, the TCCL is effectively unknown.

    Rob


  12. Then how is teh Struts code (in repository) able to instantiate action classes(my application code) in pickup ?

    Isnt it through TCCL ?


  13. Hi Rob,

    So Struts code (in repository) instantiates action classes(my appliction code) using TCCL ?

3 trackbacks

Leave a Reply