Optimized service searching in OSGi framework on service interface name and service registration properties
Overview
Framework supports a mechanism of optimized service search for service queries with a specific service interface name and a specific service property. The optimized search supports only simple service queries bundleContext.getServiceReferences(String className, String filter), where the filter has only one single property.
A bundle utility for end applications to make quick service queries for services with specific interface name and properties is also provided.
Optimized service search for framework
Framework maintains a cache, which will be checked first, when there are BundleContext.getServiceReferences(String className, String filter) queries with a filter, which contains only a single service registration property. Since the cache is maintained centrally in the framework, there is only a single cache, and it does not matter how many different applications will make service queries.
The OSGi Framework supports both declarative and programmatic descriptions for defining the service interface names and properties for an optimized service search.
Declarative approach
The following format (a static description via a system property) is used about the services, for which the framework should support optimized searching in the service registry:
mbs.services.optimizeSearchFilters=clause1,clause2,...
clause=objectClass;prop1,prop2,prop3,...
It is very likely, that only a single property is used per objectClass. Here is an example for the most common case:
mbs.services.optimizeSearchFilters=com.prosyst.mbs.services.fim.FunctionalItem;uid
Programmatic approach
This is a dynamic approach, which allows bundles dynamically to add and remove pairs of (service interface, service properties) in the framework, for which optimized service searching should be supported. Bundles should register/unregister instances of ServiceQueryOptimizerPlugin, when adding/removing such pairs.
public interface ServiceQueryOptimizerPlugin {
/**
* Describes the different service registration infos, for which a software module is interested.
*
* @ return A list of {@link ServiceRegistrationInfo} objects.
*/
public List<ServiceRegistrationInfo> getServiceRegistrationInfos();
}
public final class ServiceRegistrationInfo {
private String objectClass;
private List< String> propertyNames;
/**
* Constructs a {@code ServiceRegistrationInfo} object with an object class
* and a list of property names.
*
* @param objectClass The object class for service query optimizations.
* @param propertyNames The property names for service query optimizations.
*/
public ServiceRegistrationInfo( String objectClass, List< String> propertyNames) {
if (objectClass == null || objectClass.length() == 0) {
throw new IllegalArgumentException( "ObjectClass null or
empty.");
}
if (propertyNames == null || propertyNames.isEmpty()) {
throw new IllegalArgumentException( "No property names
specified.");
}
this.objectClass = objectClass;
this.propertyNames = new ArrayList< String> (propertyNames);
}
/**
* Constructs a {@code ServiceRegistrationInfo} object with an object class
* and one or more property names.
*
* @param objectClass The object class for service query optimizations.
* @param propertyNames The property names for service query optimizations.
*/
public ServiceRegistrationInfo( String objectClass, String... propertyNames) {
if (objectClass == null || objectClass.length() == 0) {
throw new IllegalArgumentException( "ObjectClass null or
empty.");
}
if (propertyNames.length == 0) {
throw new IllegalArgumentException( "No
property names specified.");
}
this.objectClass = objectClass;
this.propertyNames = Arrays.asList(propertyNames);
}
/**
* Getter for the interface of the object class.
*
* @ return the interface of the object class.
*/
public String getObjectClass() {
return objectClass;
}
/**
* Registration property names, whose values are unique for every service registration under the specified
* objectClass. So the value can be used as an additional key for a more optimized search in the service registry.
*
* @ return the registration property names.
*/
public List< String> getPropertyNames() {
return propertyNames;
}
}
Bundle utility for optimized service search
Since this tracker utility is implemented on top of the standard ServiceTracker, each utility will maintain its own separate cache (opposite to the optimized service search support in the framework).
public class PropertyIndexedServiceTracker {
private TrackerCache tCache;
private ServiceTracker< Object, Object> tracker;
/**
* Constructs a tracker for the specified object class
*
* @param context the bundle context of the user of the api. Its region of visibility and its permissions will be taken into
* account, while tracking the service registry.
* @param objectClass the interface name, under which the service is registered
* @param propertyNames one or more service registration properties to be used as additional index for a more efficient
* service search. It is most efficient, when the property value is unique for every service registration under the specified
* <code>objectClass</code>.
*/
public PropertyIndexedServiceTracker(BundleContext context, String objectClass, String... propertyNames) {
tracker = new ServiceTracker< Object, Object>(context, objectClass,
tCache = new TrackerCache(context, propertyNames));
}
/**
* Opens the tracker. Enables tracking of services.
*/
public void open() {
tracker.open();
}
/**
* Opens the tracker. Enables tracking of services.
*
* @param trackAll if true enables tracking of all services, no matter of its class loader namespace.
*/
public void open( boolean trackAll) {
tracker.open(trackAll);
}
/**
* Retrieves all the services, registered under the <code>objectClass</code>, specified in the constructor and
* with the specified service registration <code>property</code>, which has the specified <code>value</code>.
* If the property values are unique, a list with a single service will be returned.
*
* @param property the service registration property key.
* @param value the service registration property value.
* @ return A <code>List</code> of services which have a matching value for specified property
* or <code> null</code> if there are no such services.
*/
public List< Object> getServices( String property, Object value) {
if (property == null) {
Object[] services = tracker.getServices();
return (services != null ? Arrays.asList(services) : null);
}
return tCache.getServices(property.toLowerCase(), value);
}
/**
* Retrieves all the services, registered under the <code>objectClass</code>, specified in the constructor and
* the specified <code>property</code>.
*
* @param property the service registration property key.
* @ return A <code>List</code> of services which have the specified property
* or <code> null</code> if there are no such services.
*/
public List< Object> getServices( String property) {
if (property == null) {
Object[] services = tracker.getServices();
return (services != null ? Arrays.asList(services) : null);
}
return tCache.getServices(property.toLowerCase());
}
/**
* Retrieves all the services, registered under the <code>objectClass</code>.
*
* @ return An array of service objects or null if no services are being tracked.
*/
public Object[] getServices() {
return tracker.getServices();
}
/**
* Closes the tracker. Disables tracking of services.
*/
public void close() {
tCache.close();
tracker.close();
}
}