Thoughts on Blueprint and Declarative Services: Dependency injection or Dependency management
I’ve been using the OSGi Blueprint for a couple of years now and I have been happy with it. Blueprint is the obvious choice inside Apache Karaf and since it was a solution that is generally working well I never needed to look for alternatives.
Earlier this year I had the chance to watch a presentation by Scott England Sulivan which included a demo of a small project using OSGi Declarative Services which made think that I should start looking closer at OSGi Declarative Services.
So here are some thoughts about the two different approaches for dependency injection/management in OSGi.
Blueprint
Blueprint is a dependency injection solution for OSGi. It is almost identical to the Spring Framework with extra support for OSGi Services (in fact it was inspired by the Spring Framework). Its resemblance to Spring makes it dead simple to use, especially if you are already familiar with Spring.
Blueprint handles for you injection of OSGi services taking the availability of the services into consideration. When using services that are considered optional a proxy for that service will be created and injected. Calls to that service will block until the service becomes available or timeout.
Declarative Services
Declarative Services is a component model that simplifies the creation of components that publish or consume OSGi services. I don’t consider Declarative Services a dependency injection solution, as it is more like a component model with dependency management capabilities.
In Declarative Services you define component and its dependencies in a declarative way and the framework, will manage the lifecycle of the component based on wether its dependencies are satisfied or not. This means that a component will only get activated when all the dependencies are satisfied and will deactivate whenever a dependency is gone. So it is 100% free of proxies, but still guarantees that as long as a component is active, its internal dependencies will be reachable.
Proxy-ing vs Cascading
One of the main differences between the two approaches in questions is that Blueprint uses Proxies and Declarative Services uses a cascading approach (activate / deactivate components based on the dependency availability). I tend to prefer cascading instead of proxy-ing (not only because proxies are out of fashion but …), mostly because when using proxies you have no clue of what is that state/availability of the underlying object. Cascading on the other hand seems to be more fitting inside OSGi, as it handles better the dynamic nature of the framework.
A typical case where proxies cause head aches is when you need to use a reference listener for an optional service. If you need to react on the service object when it goes away (unbinds) it will just fail.
An other issue with proxies is that they will allow you to publish services that their dependencies are not satisfied yet. Calls the the service may end up hung, because the proxy is waiting for the unsatisfied dependency to become available. This prevents you from failing fast and not being able to fail fast can even compromise your entire system (if its not obvious why you may want to have a read at the excellent book Release It!).
An example
Let’s examine closer the approaches above using a close to real world example. Let’s assume that we are building a simple CRUD web application for Items.
The application can be composed of the following parts:
- The presentation layer
- The item service
- The data store
- The database
In OSGi the presentation layer could be a servlet registered in the Http Service, The Item Service could be an OSGi service that encapsulates the logic and the DataStore could also be an OSGi service that can be used for interacting with the database.
As shown in the diagram above, the web application depends on the item service and the item service depends on the datastore.
What happens if the datastore is not configured or available?
Proxying:
With the proxying approach the Item Service will get injected a proxy to the Data Store, that will block if the Data Store is not available. The Item Service will be registered to the Service Registry and the Web Application will try to use it, even if there is no Data Store. Requests to the Web Application may end up blocked waiting for the Data Store (which is not ideal).
Cascading:
With the cascading approach the Item Service will only be registered when a Data Store is present. In the same manner the Web Application will only be available if the Item Service is available. So we have a guarantee, that the Web Application will be available if all dependencies in the hierarchy are satisfied. Requests made while there are unsatisfied dependencies will result in an HTTP 404 error which gives a “fail fast” behaviour. Note that whenever the Data Store becomes available both the Item Service and Web Application will automatically detect the change and also become available themselves.
Final Thoughts
Declarative Services is a great tool for managing the dependencies of your components. The cascading approach can be a valuable tool for building robust dynamic, modular applications.
Blueprint is really easy to use and will feel natural to users familiar with spring. Also the proxying behaviour in some cases can be also useful.
So when to use one or the other?
I tend to think that Blueprint shines in cases, where you are constructing components that either have no service dependencies or are not exposed themselves as services. An other case is when “waiting for the service” is more natural. Such examples can be:
- shell commands (usually the are not used as services by other components)
- camel routes (waiting for dependencies is desired)
In cases where there are long dependency chains, components are highly dynamic etc I think that declarative services is a better choice.
Regardless of your choice, you need to know the strengths and weaknesses of your tools!
I hope that this was helpful !!!