Lazy Declarative Services the Simple Way

One of the great advantages of the Declarative Services (DS) approach to OSGi development is laziness. If you listened to the podcast interview with Chris Aniszczyk on EclipseZone recently, you might think that laziness in DS is a special, new-fangled feature in Equinox 3.5 that requires funky bundle activation states. Frankly it sounds complex and scary. But in fact DS laziness can be quite simple and clean if we eschew a little bit of Eclipse/PDE baggage.

First let’s just explain what we’re talking about here: OSGi services are used to decouple the producers of functionality from consumers. For example consider a Translator interface. A “producer” may offer an instance of this interface to translate English strings into Klingon, by publishing it as a service. A “consumer” needs to know how to say “revenge is a dish best served cold” in Klingon, so it queries the service registry to find an instance of the Translator interface and then invokes the translate method. This model is wonderful because the producer knows nothing about the consumer and the consumer knows nothing about the producer: they are perfectly decoupled. But in one way it is too decoupled: the producer doesn’t know whether any consumers even exist!

Suppose that setting up a full English to Klingon translation service takes some time and memory; we wouldn’t want to do it unless we were sure that at least one consumer exists. At the very least we should defer creating it until an interested consumer comes along, so that we don’t unnecessarily delay the startup of our application. Imagine if our application was constructed out of hundreds of services, each of which has a non-negligible cost to create, but only a fraction of which may actually be used by a real consumer.

Typically though, services themselves are not very expensive at all. Most are simple stateless Java objects with a very low marginal instantiation cost. But in the traditional, eager approach to service registration we must at least create a classloader for the bundle in order to load the service implementation class and instantiate the service object, and in large systems the cost of all those classloaders may be quite high.

Enter DS. If we declare our Translator service in an XML file, then the DS runtime bundle is able to register an entry in the service registry before actually creating the implementation class for the service. Only when a consumer comes along wishing to use the service does the implementation class actually get loaded. Handily, this all happens completely transparently to both the consumer and the producer.

However there’s a little wrinkle which, at first glance, makes this seem rather pointless: DS will ignore our bundle and all the lovely XML declarations it contains unless it is in the ACTIVE state! This is very important, by the way, because ACTIVE state gives us explicit control (contrast with the Extension Registry in Eclipse which looks at RESOLVED state, and there is no way to explicitly move a bundle in and out of RESOLVED state). However, activating the bundle means running its activator, which means loading the bundle and creating a classloader and all the other stuff that we just said was expensive. This is the point where some people get confused and start looking to the fancy-schmancy “lazy activation policy” stuff introduced in OSGi R4.2. But wait a minute… what if the bundle has no activator?

The key point here is that bundles can still be active even without an activator class. This may seem silly; what is the purpose in activating a bundle if it has no activator? Well in that case, the ACTIVE state is merely a flag that tells DS to look at the bundle. Now activation is free because there is no need for the framework to create a classloader, as there is no activator class to load.

Therefore, the simplest approach by far to developing services with DS and benefiting from laziness is to build bundles without activators. As far as I know, this works with all OSGi R4.x implementations.

So what’s all the lazy activation policy stuff in OSGi R4.2 about? Well I believe this is because Eclipse wants to have its cake and eat it. The problem is most legacy Eclipse plug-ins do have activators, and furthermore the “New Plug-in” wizard in PDE offers to generate an activator class for you by default. All these activators barely do anything useful and they get in the way of using DS lazily. Eclipse wanted to have activators and delayed classloader creation, so the OSGi spec was enhanced (i.e., made more complicated) in R4.2 to allow bundles to be “lazily started”. This means they enter a kind of limbo where they hang in the STARTING state — a state that normally only exists briefly during the execution of the activator. In this state the bundle classloader has not yet been created and the activator has not yet been executed, but DS can see the XML service declarations and can register the services. As soon as a consumer requests the service, then the classloader is created, the activator is executed and the service instantiated. Yikes, bundle lifecycle is difficult enough to explain already!

There are some nice enhancements in DS in OSGi 4.2 though. The most noticeable is that it is now much easier to write DS components as POJOs, because the activate and deactivate methods are no longer required to take a ComponentContext parameter, and they can therefore be written without any dependency on the OSGi APIs. Also they can receive configuration data from the Configuration Admin service via a Map parameter, and they can be made dependent on configuration data being available.

Sadly, the laziness of DS has its limits, no matter whether we use the no-activator approach or the lazy activation approach. In a DS component we declare references to other services; for example the Translator service may refer to a Dictionary service to look-up the meanings of individual words. DS will see this declaration and supply the Dictionary service to our Translator service implementation via the bind method, e.g. a setDictionary method. This is enough to count as “using” the Dictionary service, which will cause the implementation class for the Dictionary to be loaded and so on, even if our Translator doesn’t really need to use the Dictionary until much later on. There is a way around this problem: instead of asking DS to supply the Dictionary object, we can ask it to supply a ServiceReference object, which we can use later to get the Dictionary when we really need it from the ComponentContext. Unfortunately this pollutes our code with OSGi API calls: our components are no longer POJOs. The only OSGi component model that I know of that avoids this problem is iPOJO, which in this scenario would inject a proxy Dictionary object into the Translator wait until the translator actually accesses the dictionary field before fetching the dictionary service object (thanks to Clement for correcting me on how this works).

Anyway, I enjoyed the interview with Chris, and the DS component editor introduced in Galileo should be very handy — if only it worked outside of PDE projects!

10 Comments

  1. Clement:

    Hi,

    Just a comment about iPOJO. If you use field injection, it will not inject a proxy. In fact, it will not inject anything UNTIL the field is used (a method accesses it). Once used, the service object will be created and injected. By default, iPOJO does not create proxies.

    Clement

  2. Chris Aniszczyk:

    The component editor will work outside of PDE projects in 3.5.1 and 3.6, be patient ;)

    If you find any more issues regarding activators being generated by default… please file bugs!

    In the end, I think the enhancements to DS make it a lot more useful in a variety of use cases. The ability to use POJOs is great!

  3. Chris Brind:

    I like the idea of DS, but I don’t like the XML - it implies an additional level of testing is required before you can risk putting your bundle in to production (e.g. in case you’ve made a mistake in your XML). Compilation should do this for you, and you lose that when you start going all declarative. I personally think ‘declarative’ should be used as little as is possible. Loosely coupled is good, but too loosely and things get hard to test and maintain and feel that’s a risk you run with DS.

    I’ve been writing bundles without Activators for a while, so I totally agree with you there - but I’ve been doing this with a bundle I’ve written called ‘Glue’ which lets you declare one or more ‘components’ in your bundle’s manifest. Glue instantiates the class, injects it with services (and activates/deactivates it) as those services come and go. It uses interfaces rather than annotations or an XML definition so that you get that compilation reassuring though it does use an element of ‘convention’ to provide the mechanism for injection (i.e. it’s all method based).

    I’ll be putting it on github soon (maybe even tonight), I’d be glad to get your feedback, though I’m sure you’ll have something to say about my approach without even seeing the code. =)

  4. Richard S. Hall:

    Activation policies were introduced in R4.1, otherwise I agree with what you are saying.

  5. philk:

    Thanks for pointing out that one does not need a BundleActivator. Eclipse Plug-Ins usually used them to get access to the bundles context which you can also do by ((BundleReference)getClass().getClassLoader).getBundle().getBundleContext(). However this approach failed to deliver a BundleReference implementing class loader when the bundle’s activation Policy was not “lazy”. Any ideas why that is?

    Also you mention that Components can be written with activate and deactivate not taking a ComponentContext parameter. How can I access services that my component depends on then? Or are the paramter-less methods only used when I do not need the ComponentContext?

  6. Neil:

    @Clement: thanks for the correction, I will update the post.

    @Chris: I understand your motivation, static typing is good and should be used as much as possible. The problem is that if all the metadata about the component (e.g. what service interface it provides etc.) is encoded in the class file, then you have to create a classloader for the bundle in order to read the metadata, and this works against laziness. That is, unless you employ some kind of bytecode inspection using a tool like ASM.

    Another option is to generate the declarations from either the Java source or the class files at build time. This is essentially what iPOJO does (in fact it generates a fair bit of bytecode too which gets injected into your classes). Also Kai Toedter did some work on generating the DS XML files from Java annotations: http://max-server.myftp.org/trac/pm/wiki/a4ds

  7. Neil:

    @philk: Bundle.getBundleContext() returns null if the bundle is not either ACTIVE, STARTING or STOPPING. It shouldn’t be affected by the lazy activation policy except that a bundle can be held in the STARTING state for a long period, and Eclipse makes use of this by lazily starting any bundle with the lazy activation policy enabled.

    If you don’t take a ComponentContext as a parameter to your component then you can access services supplied via the bind method. I.e. use the so-called “event strategy” rather than the “lookup strategy”.

  8. Chris Brind:

    @neil - I get what you’re saying, but don’t see at as such a complicated problem. The code is on github.com now if you’d like to take a look: http://github.com/brindy/arumbundles/tree/3d1f63d76788fc7138339d2e919d9f99bf8b4a0b/uk.co.arum.osgi.glue

    The explaination of what’s happening is mainly in the Javadoc and starts here: http://github.com/brindy/arumbundles/blob/3d1f63d76788fc7138339d2e919d9f99bf8b4a0b/uk.co.arum.osgi.glue/src/uk/co/arum/osgi/glue/package.html

    (is there a better way to reference code in github)?

    Cheers, Chris

  9. Chris Brind:

    Sorry Neil, been having a weird night - I must go to bed!

    The code is actually here, if you’re interested: http://github.com/arum/bundles/tree/master

    Cheers, Chris

  10. Sunny Kapoor:

    Dear Neil,

    After you reading your post and studying OSGI DS specification and iPojo, I am little bit confused which way to go. For me (new to OSGI and relatively junior programmer) going for OSGI DS way is little intimidating as compared to iPojo which is relatively little easier to grasp and use.

    Your thoughts are mush appreciated.

Leave a comment