OSGi Service Locator
The Service Locator pattern is a well-established mechanism for accessing local and remote services in a consistent manner:
public interface ServiceLocator {
<T> T findService(String serviceName) throws ServiceNotAvailableException;
}
Using a structured service name interface we can improve uniformity and reduce the potential for typos:
public enum ServiceName {
SomeService("SomeService");
private final String filter;
...
/**
* @return a filter string used to identify the service classification/location.
*/
String getFilter();
}
public interface ServiceLocator {
<T> T findService(ServiceName serviceName) throws ServiceNotAvailableException;
}
In an OSGi environment, the recommended approach for retrieving services is via the org.osgi.util.tracker.ServiceTracker class:
... BundleContext context = ... ServiceTracker tracker = new ServiceTracker(context, SomeService.class.getName(), null); tracker.open(); SomeService service = (SomeService) tracker.getService(); ...
We can combine these two patterns to provide a more familiar and manageable approach to locating services:
import java.util.HashMap;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
public class OsgiServiceLocator implements ServiceLocator {
private final BundleContext context;
private final Map<ServiceName, ServiceTracker> serviceTrackers;
/**
* @param context the bundle context in which to find services
*/
public OsgiServiceLocator(BundleContext context) {
this.context = context;
serviceTrackers = new HashMap<ServiceName, ServiceTracker>();
}
@SuppressWarnings("unchecked")
public <T> T findService(ServiceName serviceName) throws ServiceNotAvailableException {
ServiceTracker tracker = serviceTrackers.get(serviceName);
if (tracker == null) {
synchronized (serviceTrackers) {
tracker = serviceTrackers.get(serviceName);
if (tracker == null) {
tracker = new ServiceTracker(context, context.createFilter(serviceName.getFilter()), null);
tracker.open();
serviceTrackers.put(serviceName, tracker);
}
}
}
final T service = (T) tracker.getService();
if (service == null) {
throw new ServiceNotAvailableException("Service matching [" + serviceName.getFilter() + "] not found.");
}
return service;
}
/**
* Clean up resources.
*/
public void reset() {
for (ServiceTracker tracker : serviceTrackers.values()) {
tracker.close();
}
serviceTrackers.clear();
}
}
An example usage might be something like this:
import org.osgi.framework.Constants;
public enum ServiceName {
SomeService("(" + Constants.OBJECTCLASS + "=" + SomeService.class.toString() + ")");
...
}
...
BundleContext context = ...
ServiceLocator serviceLocator = new OsgiServiceLocator(context);
SomeService service = serviceLocator.findService(ServiceName.SomeService);
...
Conclusion
By implementing the Service Locator pattern in an OSGi context we provide consistency and familiarity for code that is not OSGi-aware. This reduces the dependency on OSGi and improves the maintainability of our code.
Oct 1st 2009
One thing to add – one must not forget about the OSGi dynamics: a service can be unregistered at any time so ideally, on each call, the client would locate the service, invoke the methods needed and then discard the instance.
Otherwise, if the service gets unregistered, any further invocations will lead to unpredictable behaviour.
There are frameworks that handle this case (among other features) in a non invasive fashion (using dependency injection) – I’ll mention the ones I know best, namely Spring DM and OSGi Blueprint Services.
Cheers,
Costin Leau
Lead, Spring Dynamic Modules
Oct 7th 2009
@Costin Leau
Hi Costin,
Thanks I probably should have made the point that the Service Locator pattern is possibly the least desirable mechanism for retrieving service references in an OSGi context, due to the dynamic nature of service availability that you mention.
Sometimes however (particularly when dealing with legacy code and third-party libraries), approaches such as the whiteboard pattern and/or Dependency Injection may not be applicable. In such cases a Service Locator may be considered (albeit with caution!).
regards,
ben