As most of you will know the GX WebManager Component Framework is an application level abstraction build on top of the Apache Felix implementation of the OSGi™ specification. GX WebManager Components are basically services as are the platform services provided. Therefore building WebManager Components, that do a little more then just sitting there, means you are accessing other services. Here is a short overview of the different ways you can do that and why you should or should not prefer them.
In the Component Framework we explicitly added the concept of dependencies on other components or, for that matter, any service in general. So, at the risk at losing all my readers after this sections, here is the spoiler.. as a component developer you should (almost?) always prefer this option! By using Component Dependencies you stay within the development model, that keeps things simple to start with. In addition it provides you with many ease-of-development advantages hiding the complexity of dealing with services at the bare OSGi™ level.
The Component Framework allows you to declare which collaborators you either require or would like to use and brings them to you. This is commonly known as dependency injection, a form of Inversion of Control (IoC). This has some serious advantages:
These obvious advantages allow you to keep your business logic clean and therefore readable and, maybe even more important, testable. How to use Component Dependencies and how they work is detailed in the developer documentation but here are a few snippets to give you an idea:
Registering a required dependency that is injected on the member field
//Component Activator
private ServiceComponentDefinitionImpl getServiceComponentDefinition() {
// Adding a required dependecy without callbacks
ComponentDependencyImpl cDep = new ComponentDependencyImpl();
cDep.setServiceName(ServiceToTrack.class.getName());
cDep.setRequired(true);
definition.setDependencies(new ComponentDependency[]{ cDep });
return definition;
}
//Component Implementation
private ServiceToTrack myInjectedService; // automagically injected when available
Registering an optional dependency that is passed on through a callback
//Component Activator
private ServiceComponentDefinitionImpl getServiceComponentDefinition() {
// Adding an optional dependecy with callbacks for tracking
ComponentDependencyImpl cDep = new ComponentDependencyImpl();
cDep.setServiceName(ServiceToTrack.class.getName());
cDep.setRequired(false);
cDep.setAddedCallback("serviceToTrackAdded");
cDep.setRemovedCallback("serviceToTrackRemoved");
definition.setDependencies(new ComponentDependency[]{ cDep });
return definition;
}
//Component Implementation
private static final Set myTrackedServiceSet = Collections
.synchronizedSet(new HashSet());
public void serviceToTrackAdded(ServiceReference ref, Object obj) {
ref.getBundle(); // OSGi classloading bug workaround
myTrackedServiceSet.put((ServiceToTrack) obj);
}
public void serviceToTrackRemoved(ServiceReference ref, Object obj) {
myTrackedServiceSet.remove((ServiceToTrack) obj);
}
There is also the option of accessing services using the standard OSGi™ interfaces. Again, the Component Framework is merely an abstraction and we are not hiding anything. If you are implementing standard Component you are always extending ComponentBase that will allow you to access the OSGi™ BundleContext and you can use this as your starting point for accessing any service you may need. For example:
//Component Implementation
ServiceReference[] refs = getBundleContext().getServiceReferences(TrackedService.class.getName(), null);
for (ServiceReference ref : refs) {
TrackedService ct = (TrackedService) getBundleContext().getService(ref);
// Do something sensible...
}
Although there is nothing wrong with this code it should be clear that you lost the 'deregister if required dependency unavailable' feature, you are getting OSGi boilerplate in your business logic and thus your code becomes harder to test.
Finally you could lookup services through the Framework service locator and, although it is also documented in our developer documentation for very specific use cases, I must stress that you actually should NEVER use it when implementing a Component. If you are tempted to use it, take a step back and look at your design. Having said that.. Using it would look something like this:
//Component Implementation Object[] services = FrameworkFactory.getInstance().getFramework().getServices(TrackedService.class.getName(), null);
Using this pattern in your Component implementation is bad because it breaks the isolation that the Component/Service model provides. You are tying your implementation to a singleton outside your control. Actually, to be honest, you are tying it to a singleton that is even outside to OSGi container itself. All chances of properly ever testing your code have gone.
So why is it there? Well you could consider it to be an implementation detail of the Component Framework. As GX WebManager encapsulates the OSGi™ runtime in a standard web application we needed some way to bridge these two worlds and this was the obvious choice. Looking back we should have probably hidden it by not exposing it through the framework classloader but now there is no way back without breaking backward compatibility. Mea culpa :)
I hope this post has made this topic a little clearer. I recommend reading the developer documentation if you want to know more about using the Component Dependency feature. As always, do not hesitate to post your comments or get in touch with me if you have additional questions!
Next time I feel like writing a blog I will go into a little more detail on the topic of dependency injection.
Regards,
Bram
Bram de Kruijff is Product Architect and one of the co-architects of the GX WebManager framework with a focus on OSGi and services framework. Bram is part of the NAF Web 2.0 forum group to define standards on community technologies.
Other blog entries: