The Liferay portal provides an OSGi framework for the collaborative environment of objects which are registered as services. Those Declarative Services are ways to share services between the components. When we declare java class as component (using @component annotation) then that component gets registered with the services, which will be available for use for other components. DS service components are marked with @component annotation, and also implement or extend a service class.
It’s easy to call/use services inside a component class, we just need to use another Declarative Services (DS) annotation, @Reference, to get a service reference. And, when the referenced service is available, that component gets activated. But, in non-components classes, the injection will never occur and @Reference annotations will get ignored. One more thing, the static utility class is a bad idea to use in OSGI runtime’s dynamic environment as it may drag in unrecoverable runtime errors. Because it is difficult to find stopped or not registered services called by utility classes.
Although the use of services is not possible using the above annotations, the servicetracker comes into the picture to track the services. The service trackers are used to track the addition, modification or removal of services and a lot of controls over that tracking.
So, let’s take a simple example which is provided by Liferay 7.x servicebuilder services (XxxLocalServiceUtil classes).
public static DealLocalService getService() {
return _serviceTracker.getService();
}
private static ServiceTracker<DealLocalService, DealLocalService>
_serviceTracker = ServiceTrackerFactory.open(DealLocalService.class);
To implement service tracker in non-controller class :
Bundle bundle = FrameworkUtil.getBundle(this.getClass());
BundleContext bundleContext = bundle.getBundleContext();
ServiceTracker<DealLocalService, DealLocalService> serviceTracker =
new ServiceTracker(bundleContext, DealLocalService.class, null);
serviceTracker.open();
return serviceTracker.getService();
After creating servicetracker, it needs to be open before its use. Later when the application is being destroyed/undeployed, service tracker can be closed using close() method of servicetracker.
One more usage of a Service Tracker is to interrogate the service’s state. If getService() will return null, that means there are no service instances available. If there are multiple service instances available, that method will return any one of those.
Customize the service tracking :
You can use ServiceTrackerCustomizer for many aspects like there can be a case where a list of services is available but, we want to use a specific one among those. Those services can be differentiated by using its properties defined inside @Component annotation and, the same property we can use in ServiceTrackerCustomizer to get specific service.
To use ServiceTrackerCustomizer, we need to override addingService, modifiedService and removedService methods by implementing ServiceTrackerCustomizer. To clean up resources, the removedService method can be used after the service is removed from the registry.
public class DealServiceTrackerCustomizer implements ServiceTrackerCustomizer<DealLocalService, DealLocalService>{
private final BundleContext bundleContext;
DealServiceTrackerCustomizer(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@Override
public DealLocalService addingService(ServiceReference<DealLocalService> reference) {
String dealType = (String) reference.getProperty("deal-type");
DealLocalService service = bundleContext.getService(reference);
if(dealType.equals(DEAL_TYPE_ONLINE)){
return service;
}else{
return null;
}
}
@Override
public void modifiedService(ServiceReference<DealLocalService> reference, DealLocalService service) {
//This method is called when a service has had it properties modified
}
@Override
public void removedService(ServiceReference<DealLocalService> reference, DealLocalService service) {
//This method is called after a service is no longer being tracked
//code related to clean up related resources can be added here
}
}
And, we can add this custom service tracker to obtain serviceTracker instance using the below code :
ServiceTracker<DealLocalService, DealLocalService> serviceTracker =
new ServiceTracker(bundleContext, DealLocalService.class, new DealServiceTrackerCustomizer(bundleContext));
To use this code as universally available in the project, we can simply create a class with extending ServiceTracker.