Monday, August 26, 2019

New in OSGi R8: Condition Service

OSGi R7 made developing Applications with OSGi very convenient. It supports complex applications, to become easier manageable and allows for a structure every developer can keep track of. It is furthermore a great Framework to avoid unnecessary complexity, even though it cannot totally prevent it in large applications.

Using services, either with DS and/or CDI, naturally leads to dependent services, which both models can handle conveniently and transparently. A developer can define mandatory services that need to be injected and/or filter for what kind of service he desires. This makes sure that services are available and activated, only if their preconditions are met. The system is not without limits though and the Condition Service is a supplement for such situations.

What are the Use Cases?

Indirect Preconditions
As an example: The whiteboard is one of the most powerful concepts OSGi allows, but it can be a double-edged sword. At development time, it is great to write a whiteboard that works, if none or numerous participants are available. Usually, at startup, you will find yourself in a situation, where you want a whiteboard to be available to you, only if it is already populated with certain services.

Service Not Available
OSGi allows you to tell a component to become active only if certain required services are available. It is, however, not possible to have an active service, as long as one of its required services is not available. Take the example of a Web-Application, that mainly depends on an external component. As long as this component is not available, you might want to have a registered servlet  that responds to every request with “Service temporarily not available”, but goes away the moment the component becomes available.

Configure a Component to activate only if service A and B are Available
Imagine a configurable service, that provides access to billing providers like Credit, Debit and PayPal, where every individual billing provider is a service by itself. At the moment it is not possible to create such a service via ConfigAdmin, that only becomes active if e.g., the Credit and Debit provider is available, without writing a lot of OSGi specific custom code to the component.

Condition Service to the Rescue!

What is a Condition?
In OSGi, a Condition is simply a component, that registers the marker interface org.osgi.service.condition.Condition. DS and CDI will soon have an implicit mandatory reference on every component that can be directed against any condition you like. Thus a component will only become active if such a condition is available. The framework itself will conveniently provide a default TRUE-Condition service registration, DS and CDI use as default. Thus no action is needed if you do not desire to utilize conditions. 

How can I modify this Condition?

Via Annotation
OSGi will provide a convenient component property annotation to set your own target filter to the default condition. 

Via ConfigAdmin
You can use the ConfigAdmin to modify the default condition after the fact by addressing it via the osgi.condition.target property and supplying a valid filter.

How Can I Create a Condition?
There are two main possibilities. A developer can simply register any service exposing the org.osgi.service.condition.Condition interface. Here you have everything in your own hand.

The other possibility is the usage of the ConditionFactory. This will be a configurable component, that can be addressed via ConfigAdmin to register a condition if certain filters apply. 

An example could look like the following Configurator JSON:
{
":configurator:resource-version": 1,
"osgi.condition.factory~test" : {
"osgi.condition.identifier" : "resulting.condition.id",
"osgi.condition.properties.custom.condition.prop" : "my.property",
"osgi.condition.match.all" : [
"(&(objectClass=org.foo.Bar)(my.prop=foo))",
"(my.prop=bar)"
],
"osgi.condition.match.none" : [
"(&(objectClass=org.foo.Fizz)(my.prop=buzz))"
]
}
}

This configuration defines three preconditions where both of the "osgi.condition.match.all" filters must find a service and no service must be available that matches "osgi.condition.match.none". If this is the case, the ConditionFactory registers a condition service, with the properties:

"osgi.condition.id" : "resulting.condition.id",
"custom.condition.prop" : "my.property"

The moment services come and go and violate the configured preconditions the condition will be unregistered and all the dependent components will be deactivated.

Conclusion
Usually, OSGi bundles and services have no preset starting order, because the system can figure this out by itself. For quite some time now, this is a contested and discussed issue in the community, because there are a couple of valid cases where the system needs hints and help to figure out the right starting order. The condition service will be a great step here, by providing missing information to the system.

No comments:

Post a Comment