OSGi Service Selection
Just some thoughts on how connecting service-impl to service-consumer can be made configurable in OSGi…
Introduction
Normally, an OSGi service consumer locates the appropriate producer simply by specifying an interface type (via BundleContext.getServiceReference directly, or via a ServiceTracker or some dependency-injection system). However what if a container has multiple bundles that provide different implementations of the same service interface?
Sometimes the consumer should see them all; that’s easy to achieve with both consumer-lookup and whiteboard approaches.
When a consumer should only see one producer, then this can be done via service-properties. However using a service-property requires the producer and consumer to be coded in the desired way, ie (a) for the code that invokes registerService to specify some magic property, and the code that finds the service to hard-code the right filter criteria, or (b) for the consumer to register itself with a specific property and the producer to filter (whiteboard pattern).
Sometimes you want the decision on which producer gets linked to which consumer to be a deployment decision. What are the options?
Service Priority
The OSGi standard defines a “service priority” property which gives some primitive control; this property is used by default when one service instance is requested but multiple match: the one with the highest priority is returned. The service consumer does not need to be aware or this, or take any particular action: they get the highest-priority match automaticaly. However (a) this still needs to be set in code by the publisher of the service, and (b) it affects all consumers of that service type (cannot be limited to just one bundle).
Adapter Services
The Felix Dependency Manager (DM) library makes it easy to configure a “trigger” which waits until a service of a specified type arrives, and then instantiates/registers an instance of some other type that “wraps” the original in some way. If a bundle is written to consume the wrapper type, then an external bundle can be implemented to wrap (adapt) whichever service is appropriate. However this (a) requires the bundle to be configured to use the “adapter” type rather than the original type, and (b) for a bundle to exist which contains the appropriate DM configuration.
The same effect can of course be achieved manually, ie with code that directly calls the OSGi APIs rather than using Felix DM.
The Service Hook API
The OSGi core defines a set of “hooks” that can be used to modify the behaviour of BundleContext.getServiceReference(...)
, and to modify the events seen by listeners registered via BundleContext.addServiceListener
. All hook APIs need to be treated with care; they are powerful but delicate. From the documentation, the intended use-cases include:
- “Control visibility - Provide a mechanism to hide the visibility of a service to one or more bundles.”
- “Intercept finds - Provide a mechanism to detect the searches of a bundle in the service registry and restrict the set of found service references.”
A bundle can publish an EventListenerHook to hide events about a particular service from other bundles. Similarly, it can publish a FindHook to modify the results returned from method getServiceReference.
I haven’t tried this myself, but I imagine that it would be possible to build a bundle that could be configured to only allow some bundle X to see services of type Y which are published by bundle Z. Such a generic helper bundle could then be used by an OSGi container administrator to override any binding of services between bundles. Of course such configuration should be persistent - and such a bundle should be loaded early in the container startup sequence. This would seem to be a very useful facility.
Notes on Wire Admin
When I encountered the OSGi WireAdmin specification in the OSGi “compendium” document, I at first thought this service was intended to address the above use-case. The initial description seemed to imply it:
- “used to control a wiring topology in the OSGi Framework”
- “The purpose of wiring services together is to allow configurable cooperation of bundles in an OSGi Framework..”
However WireAdmin is (IMO) an odd beast; it is a kind of osgi-specific event-broker, where the linking of event producers and consumers can potentially be controlled by an external administrator.
A service producer explicitly registers a service of type Producer, and the service consumer explicitly registers a service of type Consumer. The WireAdmin bundle then detects these and adds the objects to its list of “managed” producers and consumers. It then checks its wiring configuration and uses the methods on the Producer and Consumer interfaces to notify both ends of the relevant objects. However the WireAdmin API must be used extensively at both ends; OSGI-defined types Producer, Consumer, Wire, WireAdminEvent and others must be used explicitly in the code.
There appear to also be elements of copy-paste style data-transfer in here, where the ends negotiate an appropriate format for the transferred data.
WireAdmin uses some unusual terminology:
- update ==> used by publisher to post a message
- updated ==> callback on subscriber notifying it of a message (ie asynchronous callback of registered message-consumer)
- poll ==> used by subscriber to synchronously get the next message (if one is present)
- polled ==> callback on publisher notifying it that a subscriber is trying to get the next message (ie callback on registered message-provider)
Notes on the OSGi Compendium and Enterprise Specifications
This isn’t really related to selecting services, but while I was reading up on WireAdmin, I also took a look at the other specs defined in the “compendium” document.
In R4.3, specs were split into “core” and “compendium”. The R5 specs introduces an additional “enterprise” spec which is a subset of the R5 compendium (ie contains a copy of about 50% of the chapters from the R5 compendium). It appears that the “enterprise” spec contains the most-useful stuff; what is in compendium but not enterprise is a set of odd items.
- Preferences Service ==> meant to store per-user settings; I cannot imagine a use-case that this simple service would satisfy.
- Wire Admin Service ==> odd variant on an event-broker; surely event-admin would be more useful..
- IO Connector Service ==> depends on javax.microedition. Replaced by URL service?
- UPnP Device Service ==> not widely needed
- Deployment Admin ==> probably made obsolete by OBR
- Auto Configuration ==> a way to create ConfigAdmin entries for bundles installed via Deployment Admin
- Application Admin ==> probably not widely used (using an OSGi container like an operating system, with many separate “processes”)
- Dmt Admin ==> Device Management Tree integration - not widely needed
- Monitor Admin ==> JMX-like behaviour for bundles. I can’t imagine this is widely used..
- Foreign Application Access
- Position Specification
- Measurement and State Specification
- Execution Environment Specification