OSGi and Dependency Injection

Categories: Java, OSGi

The Problem

In any object-oriented application, there needs to be a way for objects to find other objects that provide “services”. There have been many different solutions to this, such as the static singleton pattern, JNDI, and Dependency Injection (aka DI). The singleton pattern and JNDI are “lookup-based” approaches where an object needing a service contains code to actively find the needed resource. Dependency Injection instead decouples users of services from the implementation by having objects accept references to the services they need as parameters to either the constructor or setter-methods, thus moving the hard coupling between service user and provider into whatever instantiates the object - and then having that instantiation done by a framework which is configured via xml or code to inject the desired references.

The OSGi service registry provides a central place where service users can find service providers, ie can be used in a “lookup” style to obtain references to services. However an object cannot just fetch references to services from the registry once on init; the required service might not be available yet. And OSGi provides another potential problem: a required service can potentially disappear or be replaced later (not usual in traditional Java applications). Thus lookup needs to be done every time the service is needed (inefficient), or the service registry needs to be _watched for changes (complex). And any lookup-based approach requires OSGi-specific lookup code in classes which is not elegant - classes should remain POJOs where possible. It also means that an object may during some periods exist without its mandatory dependencies, which is also not elegant.

The best solution is to combine DI and OSGi. Application classes are POJOs with injected references to the common “service objects” they require, and the DI framework retrieves the services from the OSGi registry.

Unfortunately typical DI frameworks such as Guice and Spring don’t work well in an OSGi environment. This article discusses why, and what alternatives exist.

Traditional Dependency Injection Framework Assumptions

Traditional (non-OSGi) DI frameworks make an assumption when injecting singleton “service” objects into other objects under construction: that they can create the required service object if it doesn’t yet exist, and that the service object never goes away. These assumptions are not true in an OSGi environment. Under OSGi, when a DI framework instantiates some object T which requires some singleton service A (ie an object implementing interface A):

  • Service A might not exist yet (the bundle that supplies it might not have been loaded, or might not have completed startup)
  • An implementation of A cannot be created on-demand by the DI framework because the implementation is in a separate bundle. In a monolithic java application, there is usually just one DI configuration that is responsible for all objects (ie capable of instantiating all of them). This is not the case in OSGi.
  • Even if object A is available, it might disappear at some future time.

Because of these issues, libraries such as Guice or Spring just don’t work (by themselves) in OSGi environments. You could possibly use them to initialise parts of a bundle which have no direct or indirect dependencies on services provided by other bundles - but in practice that is pretty rare. It is also potentially error-prone, as it would be easy for an indirect dependency to accidentally creep in. An example of an indirect dependency chain could be:

  • An instance of S1 needs to be created and registered as a service. It depends on external service X1 and an instance of local type T (ie an X1 and a T need to be injected)
  • An instance of T needs an instance of local type V
  • An instance of V needs a reference to an external service X2

In this case, S1, T and V cannot be created until X2 is available - something traditional DI frameworks don’t support. And for best integration with OSGi S1, T and V should be somehow deactivated if X2 is later removed, also something that traditional DI frameworks cannot do.

OSGi-aware DI frameworks

The following dependency-injection frameworks are explicitly designed for use under OSGi:

Blueprint (formerly Spring-DM)

The OSGi standard defines a dependency-injection framework called Blueprint, which is derived from the earlier spring-dm framework. Blueprint solves the problems of traditional DI frameworks by always using a proxy for services:

  • Service X1 or X2 doesn’t exist at startup? No problem, just inject a proxy anyway and hope the target service exists before the proxy is used (or optionally fail after a timeout, which has its own problems)
  • No need to create the actual implementation; assume that something else will register service X1 or X2, and the proxy will look it up when needed - or fail when used.
  • Service X2 disappears (and possibly reappears) after using object V has been created? Proxy will silently rebind to new service - or fail when used.

This allows a bundle to configure many different objects, injecting other objects from the same bundle, and services from external bundles. Much of the traditional functionality of Spring is available, but not all. For example, configuration of objects from properties-files is not supported - it is expected that the OSGi ConfigAdmin service will be used instead.

Unfortunately there are some major disadvantages to the Blueprint proxy-based approach. Instead of services being plain references to real Java objects, they are proxies which:

  • makes for ugly stack-traces
  • has a significant impact on performance
  • changes the behaviour of identity comparisons on objects (which can break the spring transaction-manager for example)
  • makes interactive debugging (stepping into service-calls) unpleasant
  • turns any call to a service into something that can fail at runtime (technically, this can happen with other solutions, but is much more common with Blueprint)
  • and most significantly, services which are published by a blueprint-enabled bundle don’t behave like “proper” OSGi services; they don’t get deregistered when their dependencies disappear.

Blueprint is clearly useable, but I personally don’t like it; the above issues are just too severe.

The Peaberry extension for Guice takes a similar approach to Blueprint: any reference to a service is replaced by a reference to a proxy object which looks up the desired object from the OSGi service registry when needed. Peaberry therefore has the same limitations. It’s a “quick fix” to get a guice-based app running under OSGi, but instantiated objects don’t participate in the “native” OSGi lifecycle.

OSGi Declarative Services (aka DS or SCR)

The OSGi standard defines a simple dependency-injection system called Declarative Services. A Declarative Services (aka DS) XML file declares which objects should be created and which OSGi services should be injected into them. At runtime the OSGi environment must include an extender bundle which watches for bundle registration events, scans the bundle for DS configuration files and instantiates objects as required. This extender bundle is named the SCR (Service Component Runtime).

SCR is fully OSGi-aware; when the config specifies that an object has a mandatory service dependency then it will only be created when that service is available, and will be discarded if the required service disappears.

The DS specification defines a set of annotations which can be placed in the java code, and which tools (such as BND) can use at compile-time to generate corresponding DS-format xml files and manifest entries, as an alternative to writing the config files by hand.

DS has a very limited feature-set when compared to Guice and Spring - and even Blueprint. In some cases, the lack of features in the DI support itself is because that functionality is part of the OSGi service registry behaviour, is not relevant under OSGi, or is provided by one of the “standard services” defined in the OSGi specification. However one particular omission is significant: the only objects that DS can inject into an object it constructs are references to OSGi services, ie if an object is to be injected into another object then it must be registered as an OSGi service in the osgi-container-wide service registry. SCR can also optionally provide a ComponentContext object to the target object’s activate method, containing a map<String,String> of properties defined in the DS xml file. Of course using the ComponentContext means the object is no longer a plain pojo, but instead references an OSGi type. See the section below on “DS and DM Service Injection” for further discussion on the lack of support for injecting things other than services.

One advantage of using XML-based configuration (compared to using a BundleActivator or similar) is that bundle initialisation can be “lazy”; when the SCR extender bundle detects a DS-enabled bundle it can potentially choose to delay initialisation of the bundle. DS config files are plain data, not classes, which means classloader resolution for the bundle does not need to be performed until the SCR environment decides that the bundle is needed. In particular, this allows things like IDEs to load bundles that provide additional functionality, but not initialise them until the user performs an operation that requires a service from that bundle. The Eclipse IDE is built from large numbers of OSGi bundles and tries to optimise startup performance and memory use by not initialising bundles (plugins) that are not used. Any approach that relies on a Bundle-Activator within the bundle to configure dependency injection naturally forces most of the bundle’s classes to be loaded immediately.

The Apache Felix project provides an implementation of SCR. They also provide a set of Gogo shell commands which can be used to query the state of DS-managed services and in particular which services have not been registered because a dependency is missing. This is extremely useful for debugging purposes; the draft OSGi R6 enterprise spec defines internal APIs for SCR which can be used to provide such functionality for any SCR implementation.

SCR is implemented using the standard OSGi features for listening for bundle and service events. An SCR implementation is not tightly coupled to the framework implementation; it’s just a “helper” - which shows the elegant extensibility of OSGi.

Apache Felix Dependency Manager (aka DM)

The Apache Felix Dependency Manager is an OSGi-enabled dependency injection framework. This is a subproject of the Apache Felix project which provides the Felix Framework OSGi container, but DM is not coupled to the Felix Framework container - it works on any standard OSGi container.

A bundle’s BundleActivator uses the DM API to declare object dependencies in a similar manner to Guice Modules, but the lifecycle of objects (components) is managed in a similar manner to DS: objects are instantiated when their dependencies can be found in the OSGi registry, and are discarded when those dependencies disappear. Internally, DM creates a set of standard OSGi ServiceTracker instances which trigger the creation or removal of the managed objects. In other words, Felix DM is just a user-friendly wrapper over standard OSGi mechanisms for tracking services - just like SCR. However the increase in user-friendliness (and thus code reliability and readability) over hand-written service-tracking code is large.

One advantage that Felix DM has over extender-based solutions like SCR is that a bundle uses Felix DM just as it would use any other library. It therefore has a normal package-import dependency on felix-dm code, and can choose whichever version it wishes. There is no problem with multiple bundles within a single OSGi container having dependencies on different versions of Felix DM. [Possibly multiple versions of SCR could be supported via different xml namespaces, or by a contract-entry in the manifest or similar; I don’t know if any SCR implementation actually does this].

Felix DM also supports the annotations + compile-time processing + extender-bundle approach used by DS, which potentially supports “lazy” initialisation.

As with DS (and unlike Blueprint) Felix DM can only inject OSGi services into other objects, not arbitrary DI-managed objects. See the section on “DS and DM Service Injection”.

Felix DM has some interesting abilities that DS does not:

  • Adapter services: an “adapter type” can be defined for some target service-interface. Whenever a matching target service is registered, a new instance of the adapter is automatically created and also registered with service-interface matching the adapter type.

  • Aspect services: a “decorator class” can be defined for some target service-interface. Whenever a matching target service is registered, a new object is created which wraps the original. The new instance is then published with the same interface name and higher priority, causing consumers of service X to bind to the “aspect” implementation by default. Effectively provides the decorator pattern.

  • Temporal Dependencies: like Blueprint, DM can generate a proxy which is always available, and internally forwards method-calls to whichever service is available - or fails when invoked if no service available within a specified timeout. This can then be injected into objects instantiated by DM instead of a reference to the real service.

  • Configuration Dependencies: wait until configuration for a specific pid is available, then instantiate component, invoke update to provide configuration, then invoke activate.

  • Instance-bound dependencies: a component’s init method can dynamically add extra service dependencies. This covers the case where a class can’t know its full set of dependencies until runtime. Felix-DM starts the component only after the known dependencies are available (as usual), then leaves the component in a special “pending” state until the extra runtime-computed dependencies are available.

  • Composites: declare a single set of dependencies, and when they are satisfied then instantiate a set of objects together.

And by the way, the optional Gogo shell commands provided by the Felix DM project are excellent for diagnosing missing dependencies and other runtime problems.

See:

Apache Felix iPojo

iPojo is yet another dependency-injection framework run as a subproject of Apache Felix. And like Felix DM, it can be used with any OSGi container.

It can be configured via annotations in the code, hand-written XML files or via explicit Java code. Like Declarative Services or Felix Dependency Manager, the objects defined via iPojo follow the usual OSGi lifecycle - ie are created only when required dependencies exist, and are removed when those dependencies vanish. As with both DS and DM, annotations on Java classes are processed at compile-time.

iPojo was created initially for a PhD thesis published in December 2008. Coincidentally, Felix Dependency Manager development started in 2002 but it also became an Apache open-source project in 2008; it appears that both projects were invented completely independently.

iPojo offers many more features than DS (and a few more than DM). There are ways:

  • to initialise fields of a service impl on construction
  • to bind properties of an exported service to the value of a field on the service impl.
  • for a service impl to “deregister itself” without making any osgi-specific calls (component config defines a boolean field used to signal impl state)

iPojo’s composition feature allows creation of nested service registries, so that components can be created without making them globally visible. The <composite> tag defines a new registry as a child of some other one (eg of the global registry), the <subservice> tag imports a service from the parent registry, and the <provides> tag exports a service to the parent registry. A composite cannot span multiple bundles (ie it is not a replacement for the Subsystem service) but does mean that, like Blueprint, this DI framework can be used to manage objects internal to the bundle.

The OSGi Subsystem service

The OSGi standard (R5 or later) defines a way to group a number of bundles together as a “subsystem”, and control which services are exported from this subsystem into the container-wide service registry. However this standard isn’t widely implemented (the Apache Felix project doesn’t provide an implementation of this service for example). It also appears to be quite “heavy-weight”, comparable to building a .ear file for JEE environments. Thus while it seems possible to use it to “hide” services which are internal details of a bundle, it isn’t convenient enough to be a solution to the issue of bundle-private services published just to make them usable with DS or DM.

DS and DM Service Injection

DS and DM both currently only support injecting OSGi services into the objects they construct, ie references to objects retrieved from the global OSGi registry.

All objects created by a “real” OSGi-aware DI framework (ie not Blueprint or Peaberry) need to be managed via a service registry, due to the reasons listed at the top of this article: that services can come and go, which normal DI frameworks cannot handle. Removing a dependency should trigger a cascade of removals of the objects that depend on them, and a service registry is the way to handle that.

DS and DM currently support only a single global service registry, ie in order to inject any object T into any other object, T must be managed via the global service repository, and thus visible to all bundles. There are different opinions on whether this is a problem or not; here and here are statements from experienced OSGi developers that (to paraphrase) dependency injection of services is all that is needed, and all other construction of objects within a bundle (including providing them with their dependencies where needed) can be done by hand. If you agree, then please skip the rest of this section. For those who feel otherwise, read on.

DI configuration within a single bundle is often concerned with wiring together objects whose only valid users are other objects within the same bundle. As DS/DM only inject OSGi services, one solution is to publish these private objects in the global registry anyway. However the issues are:

  • clutters the global registry with large numbers of ‘useless’ objects, potentially causing performance problems;
  • allows other bundles to (accidentally or deliberately) access purely internal objects which are implementation details of another bundle

As lookup of services “by interface” (ie with a filter-expression that specifes the desired OBJECT_CLASS) is extremely common, I would have thought that OSGi containers would optimise for this case, and thus would scale well with lots of registered services as long as they were registered with different service interfaces. However Felix DM has a special feature which allows “filter indices” to be defined to speed up service lookup - including by-interface. It therefore appears that at least some OSGi containers at some time had performance problems with service-lookup, so avoiding publishing unnecessary (bundle-internal) objects (regardless of which interface they are registered with) seems to be a good idea.

The deliberate-or-accidental use by an external bundle of what was intended to be an “internal” service is not so serious as it first appears. Internal objects will often be registered with an OBJECT_CLASS which is an interface from a bundle-private (ie not-exported) package. In that case, other bundles won’t be able to see the service unless they go to considerable effort, and when they do get a reference they cannot cast it to any useful type. Yes, they could use reflection to access it but that is then going from “accidental” use to deliberate abuse.

Nevertheless, it does feel weird to clutter the global service registry with objects that are only internal to a bundle. And if a bundle registers an object intended for internal use using a common interface (eg javax.sql.DataSource), then other bundles might accidentally retrieve it.

As noted earlier, Blueprint and Peaberry don’t have this problem - they support injecting non-services. Bundles using iPojo can solve this problem with the composite tag which creates a new service-registry.

I believe that with a moderate amount of work, the DM DependencyManager and Component classes could be extended to also define a “nested service registry” where:

  • a call to Component.setInterface(someiface.class, someprops, Scope.LOCAL) would register the service only in the “nested” registry, not in the global one;
  • lookups of a single service by interface would look in the nested registry before the parent one;
  • lookups of a list of services would return the union of the nested registry and the parent one.

As a less-elegant solution, possibly the ConditionalPermissionAdmin could be configured with a custom rule such that any service with property “service.local” set can only be seen/got from the same bundle that registered the service. This would solve the visibility issue, but the performance impact is not clear.

Another alternative might be to use the OSGi “Service Hooks” or “Resolver Hooks” feature. These are deliberately intended to allow control over which services are visible to which bundles, and the specification includes examples of how to hide a service. The API appears to have sufficient information to be ablt to hide services with a property “service.local” from all bundles except the one that registered the service. As with the ConditionalPermissionAdmin approach, this would solve the visibility issue but the performance impact is not clear.

Framework Comparisons

The iPojo wiki has a nice table comparing iPojo, DS and Blueprint.

Note however:

  • DS now supports annotations
  • for “composite services” with DS, there should perhaps be a note to “see SubSystem services”.
  • OSGi enterprise R6 adds “damping” for DS

Unfortunately I can’t find any page comparing Felix DM with iPojo. As far as I can see, they are almost identical and there is no reason to choose one over the other except for:

  • composites (see above)
  • better support for optional services via “Null Objects”

Lifetimes and Scopes

Every object has an associated lifetime scope. When performing dependency injection, it is valid for a long-lived object to be injected into a short-lived one. However injecting a short-lived object into one with a longer lifetime will cause problems, as the long-lived object may later try to reference this object when it is no longer ‘live’. Technically, a holder object’s lifetime should be enclosed by the lifetime of any holdee object it keeps a reference to. One example is server-side web applications with scopes like application, session, and request; a session-scoped object should never hold a direct reference to a request-scoped object.

Some dependency injection frameworks detect scoping problems and report an error; for example the dependency-injection performed by JSF will report something like:

Unable to create managed bean bean. The following problems were found: 
   The scope of the object referenced by expression #{param.foo}, request, is shorter
     than the referring managed beans (bean) scope of view

Others just fail mysteriously at runtime. Spring does so; the recommended solution is to use the <aop:scoped-proxy> tag, which works similar to a Blueprint service reference, in that a proxy is generated which looks up the target object as needed (and might not find it, ie might fail).

In an OSGi environment, every service can be considered to have an undetermined lifetime - it can be stopped/unregistered at any moment. When the service is optional, then ensure the holder of a reference to a service gets notified when the service is no longer available, so the reference can be set to null or equivalent. When the service is mandatory, possible solutions are:

  • Use a proxy so that a valid instance will be looked up when needed (or service will fail). Blueprint does this automatically, and DM can be configured to do this if desired (see temporal dependency).
  • Inject a service locator of some kind which fetches the relevant service when needed; however in the case of a mandatory service, this then has the potential to fail in a similar way to the proxy approach; in OSGi the elegant way to handle code that is missing its mandatory services is to deregister the code so that it is not callable in the first place.
  • Make the holder itself a service, and link the lifetimes of the holder and holdee so that when the referenced service is unregistered, the holder service is also unregistered (becomes unavailable for use); OSGi-aware dependency injection frameworks such as DS or DM are designed for this. Note that this can trigger cascades of service deregistrations.

This is really just another way of rephrasing the statement in section Traditional Dependency Injection Framework Assumptions: standard DI frameworks don’t understand OSGi.

Note that if you use some kind of service locator to look up an OSGi service, then you need to make sure the service reference is released later. Using a Java 8 lambda expression (as a callback to a method that acquires/releases the service) would be an elegant way to do this.

And remember that this applies to objects that hold a reference to a service for a long time; if an object is created, used, and then discarded within a short time-period (eg one “transaction”) then it might be acceptable for it to hold a direct reference to a service.

Summary

A DI framework for OSGi really should have an event driven design, to match OSGi itself. Traditional DI frameworks don’t do that at all. Proxy-based solutions such as Blueprint and Peaberry try to hide event-related behaviour behind the proxies but are only partially successful. Declarative-services, Felix Dependency Manager and iPojo are fully event-aware.

The event-driven DI frameworks require all objects which can be injected to be managed by a “service registry”. Sadly, DS and DM support only a single global service registry, which isn’t really an appropriate place to manage objects that are intended to be used only within the bundle that registered them. Nevertheless, as long as they are registered as a type which is private to the bundle, this is acceptable. Adding support for bundle-private service registries (to DM at least) seems feasable. iPojo does support bundle-private registries, although the syntax is somewhat clumsy.

My personal preference is Felix Dependency Manager. When “lazy loading” of bundles is not important then I would recommend using DM via its Java API - otherwise use the annotation/xml-config approach.

References

comments powered by Disqus