OSGi Services versus Extenders
In the OSGi community there are two patterns that are widely used, but looking at a recent Stackoverflow question, not so widely understood. So an attempt to elucidate.
The best metaphor I can find for bundles, services, and extenders is our day to day society. In a society with interact with others to achieve our goals. In this interaction we often play a role in a scenario. For example, in a commercial transaction we have the seller and a buyer role and the contract that governs the transaction is the scenario. In the OSGi µservice model, a µservice is one of the actors in the scenario described by the service package. The µservice model is eminently suitable for this role playing since, unlike other service models, is based from the ground up to be dynamic, just like the real world. For example, the existence of a Bluetooth service is not a means to interact with any Bluetooth devices, nope, each Bluetooth service signals that there is a Bluetooth device available in the area and provides the means to talk to that device. It is hard to overestimate the value of this tool. Though there is a place for registering your service in the Bundle’s activator, OSGi becomes really powerful when you make this registration dynamic, fully taking dependencies into account, which is trivial to do with Declarative Services. Services are therefore a powerful a domain design tool that solves untold ordering, boilerplate, and concurrency problems.
In our society metaphor, bundles are the people. They control the behavior and choose what scenarios to play in, what services to consume and what services to provide. Though µservices are a great tool for many scenarios we have to participate in, there are also many not so interesting scenarios that are repetitive, boring, and not in our prime interest. An example. Our family has two cars. In one car I can always open the car door since it detects that I have a key, the other car not. Since I always forget that I actually have to get the key, insert it in the lock, and open the car, I dislike this stupid car. By detecting my presence through the key, the smart car saved me from looking foolish.
We can mimic this behavior in OSGi because we can find out what bundles are installed and get notifications when new ones are installed. Just as our smart car can detect the key in my pocket, so can a bundle detect specific cargo in other bundles and react accordingly. Just yesterday I discussed database migration in OSGi, a perfect example. A Bundle that interacts with a database is written to expect a specific schema in the database it uses. When a new version comes along, the database needs to be updated to match this new schema. Instead of the domain bundle updating the schema itself, a chore, the bundle could contain a description of the new schema. A database schema extender bundle (for example based on Liquibase) could detect the presence of this schema description and, if necessary, adjust the database. Extender bundles can take information from an active bundle and ensure that certain chores are done on the bundle’s behalf. An extender like Declarative Services is controlled from an XML file in your bundle to provide all the chores of setting up your components, handling dependencies, etc. In general, any information you can provide declarative (i.e. in resources in your bundle) should use extenders so that the bundle can focus on its domain work.
Extenders react to the life cycle of the bundles that provide the parameters. This makes it a very powerful to deliver information as well. Paremus’ packager packs native executables inside bundles and manage’s the life cycle of this native application according to the extendee bundle’s life cycle. That is, starting the bundle will start the application, stopping it will stop it. Providing a single model for deploying code, static content, native executables is surprisingly powerful. I have a bundle that can map the static directory in a bundle to a web server, ensuring proper cache control, range requests, and version control. I even use this model to deploy configuration data. You should see these extenders as humble servants that are ready for you and provide lots of value as long as you carry their key.
So when should you use the service pattern and when the extender pattern?
The service model is usually about the core domain stuff your bundle is supposed to do, it is generally right where the primary focus of your bundle is, it is where your code should be. The extender model is about removing chores and boilerplate from the domain bundles to specialized bundles. And the trick of this all is to understand how the dynamics of the services and bundles unexpectedly make things easier.
Peter Kriens @pkriens
P.S. The book Antifragile from Taleb describes the effect that unreliable dynamic components can make extremely reliable systems.