So what's the deal with Spring-OSGi?

Costin Leau

Welcome to my blog!
This is my first entry…ever. I manage to resist the urge of blogging but since so many people encouraged me to write about what I do at i21 I decided to give it a go. This and the fact that the Spring-OSGi had its first release yesterday evening (EET time zone).

I've been involved with Spring-OSGi since August last year and it has been quite a ride. It's one of the most challenging projects I have worked on and I'm glad to have it released, even as a milestone, to the public. Thanks a lot to everybody involved for making this happen, especially my team mates - Adrian, Andy and Hal!

In this entry, I'd like you give you a glimpse at what Spring 1.0 M1 provides at the moment; I'll skip introducing OSGi since there is plenty of excellent material available on the internet (see the links at the bottom).

The basic idea behind Spring-OSGi is to make it easy to build/write/deploy Spring applications in an OSGi environment. That is, to have the comprehensive POJO based programming model that Spring offers (IoC, AOP, service abstractions) working transparently in a dynamic execution environment which is focused on versioning and modularity.

One of the biggest challenges when adopting OSGi is dealing with its dynamic nature. Services (which are are simple object instances) come and go and your application has to deal with that. The solution is not straight forward, depends from case to case and requires an application-wide scope just like exception handling and transaction do. Classloading restrictions, enforced by the modularity mentioned, combined with AOP can cause a lot of grief and force the developer to create hacks, thus throwing out the window the benefits OSGi provides.
These are just a few examples of the things we are addressing in Spring-OSGi which in the end, should allow a smooth adoption path to OSGi.

Let's take a look at some of the features you'll find in 1.0 M1:

1. OSGi Application Context

OSGi is based on bundles which are nothing more then jars with some dedicated manifest entries. They are modules, units that export and import class packages and/or services.
An application can be composed of one or multiple bundles. Spring-OSGi provides an application context which builds on top of a bundle and its lifecycle, giving you access to the OSGi context in which the application lives, an OSGi custom scope as well as an extra Aware interface. As the rest of its ilk, the interface provides the ability to do dependency lookup, something you should think twice before using as OSGi service dependency injection is fully supported.

2. Resource abstraction

Classloading is not what is used to be in OSGi - the classpath for example has a different meaning since it can be assembled from multiple bundles (which in turn, can be used in more then one classpath). Thus getClass().getResource() can have a different outcome as the environment in which are running has changed quite a lot. Here is an example of what you might get in case you are looking for a class:

Equinox: bundleentry://5/my/package/MyClass.class (can be also bundleresource://)
Knopflerfish: bundle://13/my/package/MyClass.class
Felix: bundle://18.0/0/my/package/MyClass.class

Relying on URL schemes is not portable thus one of the first things done in Spring OSGi was to encapsulate low-level access through the simple, yet effective Resource interface, so no matter what OSGi implementation you are using, you can find your files. Moreover, pattern style lookups such as myFolder/* are possible (in fact we are using /META-INF/spring/* to detect 'spring powered' bundles).

3. Dynamic service support

Suppose you have the following application context:

<!– service layer–>
<bean id="myService" class="ServiceClass">
    <property name="dao" ref="dao"/>
</bean>

<!– dao layer –>
<bean id="dao" class="poorPerformerDAO">
    <property name="dataSource" ref="someDataSource"/>
</bean>

Most of the application have several layers which are excellent candidates for OSGi bundles since one can simply put the DAO classes in one bundle (the dao bundle), the service layer in another (service bundle) so when the DAOs implementations are updated (for example poorPerformerDAO above is replaced with excellentPerformerDAO) or a different version of the application is being deploy, no application restarts are required: one of the best reasons to choose OSGi!

However, to take advantage of the OSGi capabilities, the objects have to become services - that is, they have to be registered with the OSGi platform before being 'consumed' while the consumer (client) has to look for them. It's an SOA-like approach which avoids tight coupling between modules so when a bundle is shut down, the services published by it disappear. Which means that one first has to use the OSGi APIs to do the registration and lookup but also has to deal with failure as services can come and go.

Spring-OSGi greatly helps in this area by allowing literally any type of object to be exported and imported in your application without any code change.

Service Bundle:

<!– service layer–>
<bean id="myService" class="ServiceClass">
    <property name="dao>
       <osgi:reference interface="
daoInterface"/>
    </property>
</bean></code>

Dao Bundle:

<!– dao layer –>
<bean id="dao" class="goodPerformerDAO">
    <property name="dataSource" ref="someDataSource"/>
</bean>

<osgi:service ref="dao"/>

With Spring-OSGi to accomodate to the OSGI environment, two lines of configurations have to be added:

  1. to instruct the framework what OSGi service to look for
  2. to export an existing bean as an OSGi service

Obviously the same can be done for the dataSource dependency - externalize it into an OSGi bundle and just replace the straight refwith an osgi:reference. No new APIs to deal with, no exceptions to try/catch/finally for and especially built-in lookup behavior.
Spring-OSGi can be instructed so that the application context does not start unless a service implementating daoInterface is found - that is, the dao dependency can be satisfied. Moreover, at runtime if the service goes away, Spring-OSGi will automatically look for a new implementation based on your configuration (number of retries and timeout): if a call is invoked on 'dao' bean exactly when the owning bundle is updated (for example to upgrade goodPerformerDAO to excellentPerformerDAO) instead of getting a nasty, apparently inexplicable exception, one will get an imperceptible delay caused by new service lookup. As always, the behavior is fully configured.

To some degree the exporter/importer functionality resembles Spring remoting with the big difference that there is no remoting involved - everything is running in the same VM and there is no serialization involved what so ever.

4. Integration testing

Testing is important (even crucial) especially when migrating an application to a new environment, as a lot of things taken for granted can simply fail: we experienced this ourselves quite early on in development. This has been a big issue since testing was anything but easy or automated when talking about OSGi since the execution environment (the OSGi platform or container if you'd like) has to be started (no standardized API) and setup (install the bundles your test depends on). However the tricky part is that the test itself has to be OSGified - placed along side a manifest which declares the dependencies, into a bundle which has to be installed and started into the OSGi platform.

Meet AbstractOsgiTests & co:

public class SimpleIntegrationTests extends AbstractConfigurableBundleCreatorTests
{

  public void testInstalledBundles() {
    // get access to the OSGi bundle context
     Bundle[] bundles = getBundleContext().getBundles();

     getBundleContext().installBundle(someBundleLocation);
     assertEquals(bundles.length()+1, getBundleContext().getBundles().length());
  }

  // specify the bundles to install 
  protected String[] getBundles() {
        return new String[] {
                "org.springframework.osgi, commons-collections.osgi, 3.2-SNAPSHOT",
                "org.springframework.osgi, org.springframework.osgi.test.simple.service,1.0-SNAPSHOT"};
    }
}

AbstractOsgiTests builds on top of JUnit so you can write and run OSGi integration tests directly from your IDE. The entire setup is handled by the testing infrastructure so you don't have to: no need to write a MANIFEST.MF for your test, to do any packaging or deployment - everything is handled automatically. And it's fast, extremely fast! In fact, less then 10% percent of the startup time is spent inside Spring-OSGi code - the rest is used by the OSGi platform itself. Most of our integration tests are fully executed in 1-3 seconds each. Equinox, Knopflerfish and Felix are all supported.

Well, I think that's enough for a first entry… I'll write more about Spring-OSGi features in future entries. I hope I've made you curios enough to take 1.0 M1 for a spin (please note that it's the first milestone and it has some 'rough edges').

Thanks for reading!
Costin

OSGi Alliance which has some nice intros and whitepapers
Wikipedia
EclipseCon OSGi track
Spring-OSGi specification
Javapolis 2006 presentation on Spring-OSGi (by yours truly)
Last, but not least, good ol' Google.

 

17 responses


  1. Hi Costin,
    interesting entry. I'll keep one eye on your blog since now on ;-)

    Cheers,
    valerio


  2. Not knowing anything about OSGi, I'd be really interested in seeing a list of use cases for Spring-OSGi.


  3. # re-entered the comment as the author

    See the links I posted as the end of the blog. In my opinion, briefly, versioning along with package control are the most wanted features. It means that you can run two versions of the same application (that is two versions of the same class, with the same name, under the same package) on the same VM.
    Right now applications are monolithic when it comes to class space - any code update requires a new instance.
    OSGi fits nicely with the concept of IoC since versioning and OSGi dynamics can be used when assembling an application.


  4. Hi Costin,

    Great post!
    Personally, I see great future for Spring OSGi integration. Taking advantage of both Spring simplicity (programming model) and OSGi dynamic runtime capabilities.


  5. Our typical architecture is a Spring based fat client talking to a Spring based server via Spring Remoting. We use OSGI at a system level to deploy bundles of functionality which typically have both a client side and server side component (MVC). We currently painfully deal with the classloader issues for data serialized on the server and deserialized on the client.

    Will Spring OSGI directly help with Spring Remoting and classloader issues to make it easier to consume data on the client side that was remoted by Spring on the server side?


  6. Aaron, you raise an interesting point.
    Serialization and OSGi do not mix well since one allows easy classloading (especially when RMI is involved) while the latter mandates clear imports. You could use DynamicImports on OSGi but that can be too generic.
    There are other cases less 'esoteric' then serialization such as jdbc drivers and connection pools.
    We'll definitely try to provide some solutions but in the end we still rely on the OSGi platform which is in a much better position to deal with it. No matter what we do, we still rely on the classloading functionality available to us.
    We can try to 'hack' our way through but that is fragile and dangerous and can lead to many subtle problems.
    I'll raise the topic with Adrian and probably the EEG committee will address this (or OSGi R5). In fact, I would suggest to ask the question related to serialization on the Equinox/Felix/KF mailing lists in case you haven't done this already.


  7. Aaron, I've been informed (by Hal Hildebrand, OSGi EEG member) that there is already a RFP for serialization. There is no ETA at the moment but for sure, something will show up in the future.

    HTH,
    Costin


  8. Costin,

    As the rest of its ilk, the interface provides the ability to do dependency lookup, something you should think twice before using as OSGi service dependency injection is fully supported.

    If every object instance must be registered as an OSGI service, and if I need to create objects instances within my own application code (an not just by using declarative DI), whatever by using "new" or a factory method, I suppose my code will also need to access the the BundleContext API in order to register the new object instance, right ?

    Thanks,
    Rodolfo


  9. In short, yes Rodolfo.
    This makes sense since you also want to control the service - unregister perhaps at some point in time or change its properties while it is registered.
    It's just like any other resource API: you have to 'touch' it, if you want to programatically access some piece of functionality.


  10. Hi Costin,

    I have two bundles with same running (Installed & Started) on OSGi but they are different versions (1.0 & 2.0), both bundles have register service with common interface on service registry.

    While getting the service from "Service Registry" how can we differentiate the version 1.0 from 2.0?

    They known way is use LDAP filter while getting service, but for this purpose we have to change code and update the bundle where I am getting service.

    Is there any other way we will specify we want the version 1.0 not 2.0, like using manifest file or XML file (Spring OSGi XML)???

    Thanks,
    Shan.


  11. On October 1, 2007 at 5:49 am, Shan said:

    Hi Costin,

    I have two bundles with same running (Installed & Started) on OSGi but they are different versions (1.0 & 2.0), both bundles have register service with common interface on service registry.

    While getting the service from "Service Registry" how can we differentiate the version 1.0 from 2.0?

    They known way is use LDAP filter while getting service, but for this purpose we have to change code and update the bundle where I am getting service.

    Is there any other way we will specify we want the version 1.0 not 2.0, like using manifest file or XML file (Spring OSGi XML)???

    Thanks,
    Shan.

    What you need is sevice versioning. This is something that the OSGi specification doesn't cover (intentionally I think) since properties can support most versioning schema.
    We haven't added any support for this in Spring OSGi yet but we're likely to do that in the future. Feel free to discuss this on our mailing list and raise an issue on JIRA.

    Thanks,
    Costin


  12. Hi all,
    thanks costin for this quite interesting entry…
    I wondered about technical requirements for this project, as stated on the Website: spring 2.0.5 and JDK 1.4 , OSGir4…But I wondered about the opportunity to get a back port for this module into a Spring 1.2 environment ?
    Is it a great work ? I may understand (naively) that it's just a matter of ApplicationContext isn't it ?

    I'm obliged to used jdk 1.4 and Spring 1.2 for developing bundles (rcp application and osgi services)

    Cheers
    jerome


  13. Hi Jerome,

    I glad you liked the entry - I plan to write a new one in the near future.
    About the compatibility - we chose to not use Spring 1.2 since there just way too many things that we depend on that we\'d have to backport otherwise. In fact, we\'ve asked for some features in Spring 2.5 which we\'ll in there, in the RC release.
    That and the OSGi-friendly, out-of-the-box artifacts are quite handy also.
    It should be possible to backport things back to 1.2 but that would be a quite an effort. I\'m not sure why you would want though since 2.1 is compatible with 1.2 and in many cases, requires just updating the jar. Moreover, if you\'re running inside an OSGi environment, you\'d have nothing to worry about since the platform deals with versioning for you.

    Cheers,
    Costin


  14. Sorry :(


  15. Nice…


  16. Hi,
    I'm a real rookie when it comes to OSGi. But I've been reading A LOT about it :) - I've installed Equinox and all the spring bundles. I've even got a bundle working.

    But now I'm still facing some questions concerning the Import / Export / … statements in the manifest. (currently I am trying to start a bundle that needs the ApplicationEvent of Spring, which is as I see it exported by the spring-context bundle that is started in my Equinox. I'm doing the import in my bundle but when I start it, I get a ClassNotFoundException)

    Could you help me? (maybe some docs about it, urls, …)


  17. Berre, if you're looking for help try the Spring-DM forums or mailing list.
    Cheers,

5 trackbacks

Leave a Reply

Quote selected text