I am new to OSGI, I am trying to understand how to register a service? is it always through BundleContext
object in Activator
? Is there any alternate way available?
Let say I have a Interface IService
and there are two implementation ServiceImpl1
and ServiceImpl2
in same bundle I am registering them as below.
context.registerService(IService.class.getName(), new ServiceImpl1(), props);
context.registerService(IService.class.getName(), new ServiceImpl2(), props);
But confusion is how do I specifically ask for a particular service implementation?
serviceImplObject = (IService) dictionaryServiceTracker.getService();</pre>
I am not sure which implementation i would get in this case. Also I don't see any option to set what type of service implementation i need?
There are alternatives for registering and consuming services declaratively. You can use either Declarative Services (DS) or Blueprint Services. There are others as well, but these are part of the actual specification.
As for the programatic approach you are currently using. You have to use the properties when you register and a filter when you create your tracker.
Map<String, String. prop1 = new HashMap<String, String>();
prop1.put("name", "primary");
context.registerService(IService.class.getName(), new ServiceImpl1(), prop1);
Map<String, String. prop2 = new HashMap<String, String>();
prop1.put("name", "secondary");
context.registerService(IService.class.getName(), new ServiceImpl2(), props);
Now for lookups.
ServiceTracker primaryTracker = new ServiceTracker(bundleContext, "(&(objectClass=my.service.Service)(name=primary))", null);
ServiceTracker secondaryTracker = new ServiceTracker(bundleContext, "(&(objectClass=my.service.Service)(name=secondary))", null);
(Updated with regards to ranking - thanks Neil)
Without the filter, you will get the service based on its ranking and service ID. If you are running in a dynamic environment (where these services are stopping and restarting) then it possible to get different implementations each time the service is looked up.
Here's the easy way to register a service. It uses Declarative Services and the annotations supplied with bnd:
@Component
public class ServiceImpl implements IService {
// ...
}
Now the ServiceImpl class will be instantiated and published as a service. For consuming a service it looks like this (in the simplest case):
@Component
public class ServiceImpl2 {
// ...
@Reference
public void setFoo(IService foo) {
// ..
}
}
Never use activators. DS components with annotations (see Neil's answer) are by far the best practice in OSGi. Also, Service Trackers are no longer needed with DS. If I had to do OSGi again, there would be no activators, no service trackers, just DS.
Now about service selection. The worst OSGi components are the components that want to be clever and select a specific implementation. If you have a dependency on IService then you should not care about what implementation you get. If you really need Impl1, specify a dependency on Impl1. You make your component significantly less reusable if you try to make those decisions. The number of times I significantly increased reusability in companies by just removing this kind of code is surprising.
One of the coolest aspects of DS is that it allows the deployer (the one in control of the box) to decide what services should be bound to whom. Since those people have significantly more knowledge about their world that you as a developer leave it up to them.
The OSGi mechanism to control the binding of services in DS is through Configuration Admin. You can set a target filter for references (dependencies) and a ranking (or properties) on the service registered by the component. So with DS, this becomes a configurable decision.
Again, I strongly advice to kill all this selection code unless the information on which the selection is made is really part of your domain. And in that case, get all the services and just filter them in the bind methods.