Introduction to the Apache Felix DependencyManager – part two

In the previous installment of this series of articles on the Felix Dependency Manager, we showed how the DependencyManager can help to handle required service dependencies. A required service dependency means that your component cannot act without it and the DependencyManager ensures that your component is only started when the required service is available. Likewise it stops your component when the required service is not available anymore. And of course, the DependencyManager injects the required service into your component, so you can use the beloved inversion-of-control principle to keep your component’s implementation free of handling dependencies.

Optional service

But what if your component just wants to use a service when it’s available, but can perfectly live without it. In other words: if the dependency on the service is optional. A good example of an optional service dependency is a log service: usually you don’t want your software to stop functioning, just because there is no logger. We’ll continue our running example and extend the temperature converter service with logging:

As before, we need to specify the dependency in the init() method of the Activator; we just create a second ServiceDependency and set required to false:

Note that you can add as many dependencies as you want. However, make sure that you add the dependencies to the component before you add the component to the DependencyManager (the last line in the code fragment above). Otherwise, the DependencyManager might already have started your component (triggered by the dependencyManager.add(component) call) while you’re still adding additional dependencies, which might lead to confusing behavior (your component being started and stopped even before the init() call has returned).

Let’s see how this revised example behaves with respect to the availability of a LogService. In the sample we used the standard OSGi LogService, of which you can find many implementations on the internet. For the demo, the OSGi Screen logger is very convenient. Just download it and install it in Felix; as soon as you start the bundle a logger window will appear. For the demo,DependencyManager we’ll need something that calls our service; in the sample repository on bitbucket you’ll find a display bundle that presents a tiny, but functional, UI for displaying the temperature. If you installed and started it all, you’ll see something like this:

If you hit the refresh button on the temperature display, you’ll notice an extra log message appearing in the logger window.

To see what’s happening when the log service becomes unavailable, stop the logger bundle; the log window will disappear. Now hit the refresh button on the temperature display again, and you’ll see that the temperature service is still functioning normally; it’s not affected by the LogService’s absence.

Null, but not really

That the service is still running when the (optional)log service is not available anymore, won’t surprise you, but you might wonder why the logger.log(…) call in the getTemperature() method does not throw a NullPointerException. It will even become more funny when you add a line like this

and come to the conclusion that it prints

WTF!? This surprise is powered by the Null Object pattern. For your convenience, the DependencyManager will inject a null object for each (optional) service that is not available, so you don’t have to pollute your code with if-not-null checks. Of course the injected null object implements the same interface as an actual service implementation would do.

While very convenient in a lot of cases, the null object injection might lead to unexpected behavior when the null-implementation does not conform to the interface specificiation. For example, suppose we have a service interface that returns a list:

The javadoc indicates that the method should never return null, so clients of this interface will never have to do any null checks on the result obtained from getBars(). However, the null object implementation that is injected by the DependencyManager is not that smart (it can’t read javadoc): it will simply return null. Of course, this is absolutely not what you want, because it would force you to introduce these ugly null checks again (which readers of the source code wouldn’t understand, because wasn’t the interface required to never return null???). To overcome this, you can provide your own null object implementation, and ensure that getBars() will always return a list.

We’ll demonstrate the use of a custom null object in our running example, in which the null object will do something usefull instead of being a no-op implementation. When there is no logger, we’d like to write the log messages to system out directly, like this:

Note that the DependencyManager uses the term “default implementation” for a custom null object (which is actually quite right in our example, as the SysoutPrinter is doing more than you’d expect a null object to do).

If you add these lines and update the bundle, you’ll see that the log message either appears in the logger window (when the log service is started) or in the console (when it’s not). Nice isn’t it?

Still, there is a second option: you can switch off the use of a null object (or default implementation) completely. But before we’ll go into that, we need to talk about callback methods.

Callback methods

The DependencyManager has two ways for dependency injection. The first is injecting the service into a member field, which you’ve seen being used in our examples so far. The DependencyManager searches the implementation class for a member field of the right type and injects the dependency into that field. In this case, the “right type” means the same type as the interface the dependency was declared with.
Usually, this algorithm works perfectly well, but you don’t have to be a rocket-scientist to think of an example where it doesn’t work out. For those cases, you can set the name of the field to inject too; we’ll come to that in a minute.

The second method for dependency injection is with callback methods. Simply pass the names of the callbacks to the ServiceDependeny with the setCallbacks method:

Note that you setting and unsetting (or adding and removing as we’d like to call it), requires two separate methods, which might differ from other dependency injection mechanisms that you’ve seen (that simply call a setter with a null argument to unset). We’ll explain the reason in the next installment about multiplicity. You implement the callback methods in your implementation class. For the number and type of parameters, you can choose various variants, but for now the following will do:

You can use any access modifier you like; we encourage you to use private because it avoids other classes accidently calling these methods (and the DependencyManager has no problem calling private methods).

If you use callbacks like the ones above, you have to do null-checks for optional services the moment you use them. A common mistake, is to check like this:

which will fail miserably when the service happens to be unset just between the condition and the log-statement (remember that clients call services on their own (clients’) thread). There are several ways to solve this: either synchronize all access to the member field,

or create a temporary copy of the member before the null check, and make the member volatile:

By now you’ll understand why the member field has to be volatile when you use member injection: the DependencyManager has no way of knowing how you implemented synchronized access to the member (e.g. which Object you’d use for synchronization) and even though object assignment is atomic in Java, the Java memory model requires use of the volatile keyword for data synchronization (refer to java memory model if you want to know more).

Of course, you can do anything you like in the callback methods and that’s usually why people use them: because more things have to be done than just injecting a dependency (notifying other classes, sending events, updating administration, etc.) – we’ll have a nice example at the end of this article.
However, it’s important to realize that if you use callbacks, member injection is not done anymore. And of course, as the service is not injected, the null-object or default implementation aren’t either.

Auto-config

Now let’s return to the question how to disable the use of the null object pattern. You can explicitely do this, by switching off the so-called auto-configure option:

which disables both member and null-object injection. As you would have guessed by now: the default value for the autoConfig property is true (auto configure is on). If you use callback methods (like we did above), auto-configure is switched off automatically.

If you expect ambiguities w.r.t. the injection field, you can explicitely set it too; and, surprisingly, with the same method:

This will inject the LogService service a member field named injectedLoggerService (if available). Naturally, setting the name of the to-be-injected field, switches on the auto-config option (if you’d switched it off before).

As explained above, specifying callbacks disables auto-config automatically, However, there is an escape which enables you to use both at the same time: set the auto-config on explictly before settting the callbacks, e.g.:

We will use this feature in our concluding sample.

Callback example

We conclude with an example that both uses auto-config and callbacks. It’s based on the temperature display bundle that accomponied the temperature samples. If you press the button, it displays the temperature; unless there is no temperature service in which case it clears the output box. From a usability point of view, it would be much better if the UI indicates that the service is not available, e.g. by disabling the button (and clearing the output box of course). This functionality is a perfect fit with the callback methods. As we still need the service itself being injected too, we use the combination of auto-config and callback methods as explained above. As before, you can find the complete source code on bitbucket.

We add callbacks for setting as well as for unsetting the TemperatureService:

In serviceAdded method we enable the UI and we trigger the updateUI method, which will call the injected temperature service to obtain the current temperature and displays it in the UI.

As explained above, we call both setAutoConfig and setCallbacks on the ServiceDependency, when setting up our DependencyManager component:

This concludes our discussion about optional services. As before, please comment if you have any questions. Next time, we’ll dive into injecting services with multiplicity.

Tweet about this on TwitterShare on LinkedIn

Reacties

  • Hi Peter,

    I really like the article. You lift the veal of complexity from the Apache Felix Dependency Manager.

    One jarring thing is specifying the callback methods. Specifying them in String loses the compile time security Java offers. Do you have any thoughts on how to provide a more compiler checked solution?

    • First, let me start by stating the reason for the chosen solution, as I’m to blame for that (a long time ago). Dependency Manager started out before Declarative Services or Blueprint even existed and back in those days I thought it was important to have a model that could work with any POJO without having to adapt it. That meant “life cycle interfaces” were out. It also meant that annotations were not an option as they arguably also (optionally) bind your code to a certain solution. By choosing this solution, you could even work with “existing” POJOs, without modifying them.

      Now, I’m not stating that any of the solutions that I rejected back then are a bad idea per se. In fact, I would advise you to either:

      1) Define a life cycle interface of your own, maybe application/domain specific and have your developers work with that. That’s an excellent way to get compile time safety and refactoring support.

      2) Use the optional annotation support that Dependency Manager has. It supports compile time annotations that you can put on your callback methods. Maybe Peter will cover this in one of his articles, or you can read about it here: http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/apache-felix-dependency-manager-using-annotations.html

  • Hi,
    thanks for those informative posts! I’d appreciate if you could write how FDM compares to OSGi Declareative Services and Blueprint. It appears that they are quite similar in purpose but FDM’s configuration approach is very different. It reminds me of Google Guice modules. It would be great to compare FDM and Guice too. Does it make sense to somehow combine one and the other? If I recall correctly there was a project called Peaberry that was aimed at using Guice with dynamic OSGi service, but I have no idea if’s still being developed.
    Cheers,
    Rafał

  • Hey Peter,

    Nice and clear view on Felix the Dependency Manager and the basic concepts of OSGi. I wish I could’ve read this before I started with OSGi.
    I’m looking forward to read your next article.

Het e-mailadres wordt niet gepubliceerd.

*