‘OSGi Demystified’ is a series of articles addressing common OSGi issues in CICS. We offer insight into OSGi, discuss best practices, and provide setup and configuration advice. This is the fourth in the series of section 1, to see the previous post discussing your JAR as an OSGi bundle, and common misconceptions of OSGi bundles, follow this link.
- An Explanation of OSGi services and the OSGi Services registry.
- Declarative services and Service Component Runtime (SCR).
- How to dynamically update with OSGi Services and declarative services.
With the revelation that dynamic updates done through bundle-wiring (Import-Package with version) are quite bothersome, it is time to talk about OSGi services. Shift your thinking from imagining an OSGi bundle’s dependencies are on a particular package/version of code – and imagine it depends on one or more services. Just as with package-dependencies, those services are provided by other OSGi bundles (though nothing stops you providing them in the same OSGi bundle). The big difference is that services are dynamically registered with a central OSGi service repository rather than being declared statically as
Import-Package statements in the MANIFEST.MF. The OSGi service registry is essentially a dynamic cache of all services and provides a much more flexible way to facilitate component reuse.
An OSGi bundle can provide or consume one or more OSGi services. To be a provider, you should register your implementation of a service with the OSGi service repository; typically under a standard package/interface naming convention. To be a consumer, look-up a the service from the service repository and retrieve a service reference.
For any particular interface there can be zero or more services registered. Unlike
Import-Package and bundle-wiring, those services can come and go at any time. In essence, you can replace a service implementation with something better, stronger, faster, or fixed, as and when they become available. Of course it isn’t entirely zero-effort, you have to react to the loss of services and the appearance of newer implementations, and you’ll need a policy in place that determines whether you bind to a new service implementation, stick with the old, or tolerate loss of a service for a while. OSGi offers various ways to handle that churn, from ServiceListeners to ServiceTrackers, through declarative services (DS) and ultimately to something called Blueprint. For the purposes of brevity we’re not going to discuss all those approaches. Unless you really need fine-grained programmatic control and are a sucker for boiler plate code, go straight to declarative services (DS) instead. It is significantly cleaner, simpler, and more effective.
OSGi Declarative Services
Declarative services within OSGi are also known as the Service Component Runtime (SCR). Strictly speaking the XML is the declarative services part while the runtime is the SCR, but the two are generally used interchangeably. The vast majority of interaction policies and component requirements can be expressed in DS’s XML syntax, giving you plenty of flexibility with minimal coding.
So armed with your new approach to using OSGi Services and declarative services – how do you address dynamic updates? To start with, the bundle-wires, or package dependencies, are more stable because your dependencies are on an interface not on a constantly changing version of an implementation. Under that interface, there can be one or more implementing services and you can have a DS policy to determine which service you use. That policy could be as simple as when a newer implementation of a service is available, bind to it, or perhaps only bind to services from a particular Vendor.
Service implementations can be registered with an additional set of properties and it’s those properties which can help to implement the example policies above. When you need a service, you can filter the matches based on a specific property or combination of properties, LDAP filter syntax is respected. You can also give services a priority and bind to the implementation with the highest priority.
The second compelling aspect of declarative services, is that they are quite forgiving. When you remove an old implementation of a service and install a newer one, DS (or more precisely the SCR) has built-in buffering. It is that buffering that ensures requests for service don’t fail during the update window.
Follow this link to the final part of section 1 OSGi Demystified: 1.5 – JRE class visibility, bootdelegation & system.packages.extra.