4.6. Relevant org.osgi.framework APIs

Until now we've come across a number of OSGi APIs, which are summarized here. We use a few fictional services (for example, PrintService) in our examples. Their particular service interfaces and implementations are of little interest to our discussion and are not shown.

The emphasis is on how to use these APIs through examples. The formal and complete API specification can be found in Appendix B.

4.6.1. BundleContext Interface

The BundleContext APIs allow you to register services with the framework's service registry, to query registered services, and to retrieve services from the registry. You can also access a per-bundle private, persistent data storage area by using the BundleContext API.

An instance of BundleContext is created by the framework and is passed to a bundle as an argument in the bundle activator's start or stop methods. Normally a bundle should keep BundleContext received in its activator private, thus any service obtained by calling getService on BundleContext is attributed to this bundle (that is, its use count of the service is incremented). There is no ambiguity referring to the bundle as the calling bundle, as we do throughout this book.

However, what happens if bundle A is bent on giving out its BundleContext to bundle B? When code inside bundle B calls getService using bundle A's BundleContext, the service is still considered being used by A, not B. In the specification, A is referred to as the context bundle (instead of the calling bundle) to avoid any confusion regarding to which bundle the service use count should be charged. To keep it simple, do not share BundleContext objects across bundles.

public ServiceRegistration registerService(String clazz, Object service, Dictionary props)

The parameter clazz is the class name of the service, and service is the service object itself. If a service object is an instance of multiple types, it can be registered using the following alternative, which takes an array argument.

public ServiceRegistration registerService(String[] classes, Object service, Dictionary props)

The framework ensures that service is indeed of the type or types specified in the classes parameter. The class names must be fully qualified and should specify the service interface names (for example, com.acme.service.dictionary.DictionaryService), not the implementation class names (for example, com.acme.impl.dictionary.Devil).

Additionally, a set of properties can be registered together with the service. What goes in the properties is entirely application specific, and this parameter can be null if there are no properties to register. Properties consist of name/value pairs. The property name must be of type java.lang.String; the value can be any object. Let's look at some examples. The following code registers a service under its class name with no property:

bundleContext.registerService(
      "com.acme.service.print.PrintService", printService, null);

The following code registers a service under its class name, and registers a set of properties that describes its characteristics:

Properties props = new Properties();
props.put("location", "first floor");
props.put("laser", Boolean.TRUE);
props.put("capability",
   new String[] {"double-sided", "manual-feed", "color"});
props.put("dpi", new int[] {72, 300, 640, 1024});
props.put("paper", new String[] {"A4", "Letter", "Legal"});
bundleContext.registerService(
      "com.acme.service.print.PrintService", printService, props);

The following code registers a service under multiple classes with properties:

String[] classes = { "com.acme.service.print.PrintService",
   "com.acme.service.format.FormatService",
   "com.acme.service.speller.SpellingService" };
bundleContext.registerService(classes, printService, props);

In this case, printService obviously must be an instance of com.acme.service.print.PrintService, com.acme.service.format.FormatService, and com.acme.service.speller.SpellingService. The framework performs a type check and if printService is not an instance of any of the said classes, service registration fails.

The following code registers a service factory without properties:

bundleContext.registerService("com.acme.service.fax.FaxService",
      new FaxServiceFactory(), null);

Unlike service registration, type checking for service factory registration is postponed until the client bundle calls getService. Only then does the framework ensure that the service generated by the factory is compatible with the type used at registration.

public ServiceReference getServiceReference(String clazz)

The parameter clazz is the interface name of the service to get. The returned service reference is an indirection to the service object. If no service is found, this method returns null.

Oftentimes we register multiple instances of services under the same service interface. We have seen that in the dictionary bundle, Devil returns definitions of a humourous and cynical flavor, whereas Webster returns the definitions in the canonical sense, but both implement DictionaryService.

As another example, a PrintService interface allows a caller to make a hardcopy of data, but one implementation may use a local inkjet printer and another may use a laser printer over the network.

In situations like this, certain properties associated with the registered service are used to distinguish one instance from another.

To obtain a service based on certain criteria, use the method described in the next section.

public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException

The syntax used for expressing conditions in the filter argument is based on RFC 1960, “A String Representation of LDAP Search Filters” [12]. Each term in the filter expression has the format

(attribute name op value)

where attribute name is the name of the property. It must be a string and it is case insensitive in filter evaluations. value can be of the following types:

  • String

  • Number: Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimal

  • Character

  • Boolean

  • Vector or array of the previous

  • Array of the primitive types (byte, short, int, long, char, float, double, boolean)

  • Nested Vectors or arrays to any depth

op is one of the relational operators defined in Table 4.1

Table 4.1. Relational Operators in LDAP Filters
Operator Meaning Applicable Data Type
>= Greater than or equal String, Character, number
<= Less than or equal String, Character, number
= Equal All
~= Approximately equal String
=* Present String

The expression uses a prefix notation and defines three logical operators: AND (&), OR (|), NOT (!).

Note that we only borrow the LDAP filter syntax but otherwise do not involve the directory service itself in any way.

For those who find the formal definitions in the RFC unpalatable, the following examples should give you everything you need to know about syntax. The following code finds any print service that uses a laser printer and can do double-sided printing:

refs = bundleContext.getServiceReferences(
   "com.acme.service.print.PrintService",
      "(&(laser=true)(capability=double-sided))");

Because the property name is case insensitive in evaluation, you may spell capability as Capability or CAPABILITY without affecting the outcome. Note that you must not enter quotes around String or Character values. The following code finds all services registered with the framework:

refs = bundleContext.getServiceReferences(null, null);

The following code finds all print services:

refs = bundleContext.getServiceReferences(null,
      "(objectClass=com.acme.service.print.PrintService)");

objectClass is a special property that is automatically inserted by the framework at the time of service registration. It has the class names with which the service has been registered. The following statement

refs = bundleContext.getServiceReferences(
   "com.acme.service.print.PrintService", null);

can be used equivalently.

The following code finds all services from Acme. An asterisk in the value represents a wildcard. It matches zero or more arbitrary characters:

refs = bundleContext.getServiceReferences(null,
   "(objectClass=com.acme.service.*)");

The following code finds print services that use a laser printer or have resolution in dots per inch (DPI) of at least 300.

refs = bundleContext.getServiceReferences(
   "com.acme.service.print.PrintService",
   "(|(laser=true)(dpi>=300))");

Notice that the property value dpi is an array with multiple possible resolution values. In this case, each element in the array is checked in turn to see whether the element meets the condition. If any element satisfies the condition, the property is considered to satisfy the condition. If the array value has elements that themselves are arrays, each of them will be checked recursively. The same algorithm is applied if the property value is of type java.util.Vector.

The following code finds the service that can do printing and formatting, and is located on the third floor. Notice that & and | can precede any number of conditions:

refs = bundleContext.getServiceReferences(null,
   "(&(objectClass=com.acme.service.print.PrintingService)" +
   "(objectClass=com.acme.service.format.FormatService)" +
   "(location=third floor))");

The following code finds print services that do not use a laser printer but that have a DPI of at least 300.

refs = bundleContext.getServiceReferences(
   "com.acme.service.print.PrintService",
   "(&(!(laser=true))(dpi>=300))");

The following code finds print services that can do any kind of binding at all:

refs = bundleContext.getServiceReferences(
   "com.acme.service.print.PrintService",
   "(binding=*)");

Notice here that =* represents the presence test operator. If nothing appears to the right of =*, the expression is interpreted to perform the existence test. However, (binding=*foo) is interpreted to match a property named “binding” whose value ends with “foo.”

To include characters such as *, (, or ) in the value of the filter, “escape” them with a backslash. For example,

refs = bundleContext.getServiceReferences(
   "com.acme.service.format.FormatService",
   "(section_separator_char=\*)");

finds all formatting services that use a string of asterisks as the section separator.

RFC 1960 states that the semantics of the approximate equal operator (~=) is implementation specific. The Java Embedded Server framework evaluates (attribute~=value) as follows: Any nonalphanumeric characters are removed from the property and the filter value, and letters are compared in a case-insensitive way. If the filter value still appears in the property, the expression is evaluated to be true. The operator does not implement any “sound like” semantics, however. For example, assume the property vendor has the value “Sun Microsystems, Inc.” Then

(vendor~=sun)
(vendor~=sunmicro system)
(vendor~=Systems inc)

all evaluate to true.

public Object getService(ServiceReference ref)

This method gets the service referenced by the specified ServiceReference object. The call is delegated to a service factory's getService method if ref represents a registered service factory, rather than just returning the registered service itself. The framework increases a service use count for a particular client bundle each time it calls getService.

public boolean ungetService(ServiceReference ref)

This method “ungets” the service referenced by the specified ServiceReference. The framework decreases the service use count for the calling bundle. ungetService returns false if the count has dropped to zero. It returns true otherwise. The call is delegated to a service factory's ungetService method if the service use count drops to zero, and ref represents a registered service factory, rather than the service itself.

For example, the following code gets the fax services from the service factory:

ServiceReference ref = bundleContext.getServiceReference(
   "com.acme.service.fax.FaxService");
FaxService fax1 = (FaxService) bundleContext.getService(ref);
FaxService fax2 = (FaxService) bundleContext.getService(ref);
boolean isReleased = bundleContext.ungetService(ref);

In this example, we are getting service produced by FaxServiceFactory, because that is what we registered earlier. Because the framework caches the service produced for each calling bundle, fax1 and fax2 are identical service instances; in other words, fax1 == fax2. Additionally, isReleased reports true because we have two getService calls but only one ungetService.

As another example, the following ensures that the fax service is truly released:

while (ungetService(ref));

If ungetService is called more times than getService, no harm is done. It keeps returning false.

Suppose getService is invoked again afterward. The service factory will instantiate a new FaxService:

FaxService fax3 = (FaxService) bundleContext.getService(ref);

where fax3 != fax1 and fax3 != fax2. Note that fax1 and fax2 are still valid object references, but because the service they reference has been released, you are not supposed to use the service instance through them any more.

public File getDataFile(String path)

This method gets the File representing the path to the bundle's data storage area if the gateway supports a local file system. Each bundle is allocated such a data directory, and can use it in an application-specific way. If path is assigned an empty string (""), this method returns the root path of the directory. For instance, given and if root.getPath() returns /home/joe/jescache/bundle1/data, then d.getPath() returns /home/joe/jescache/bundle1/data/myapp.properties.

File root = myBundleContext.getDataFile("");
File d = myBundleContext.getDataFile("myapp.properties");

4.6.2. ServiceReference Interface

ServiceReference serves as an intermediary so that the client bundle can examine various properties of the service before committing to using it. It is returned by getServiceReference(s) APIs on the BundleContext interface or by the getReference method on the ServiceRegistration interface.

public Object getProperty(String key)

This method returns the value of the specified key in the service properties.

public String[] getPropertyKeys()

This method returns all property keys. Together with the getProperty method, you can examine the entire property set. See ServiceRegistration API setProperties for an example of modifying existing service properties.

public Bundle getBundle()

This method returns the bundle that has registered the service. A return value of null means that ServiceReference is stale—in other words, the service it references has already been unregistered.

4.6.3. ServiceRegistration Interface

The registerService API on the BundleContext interface returns a ServiceRegistration object.

public ServiceReference getReference()

This method returns the service reference for this registration. Service references obtained from different ServiceRegistration instances are all different, even for the same service object. For example,

FaxService fax = new FaxServiceImpl();
ServiceRegistration reg1 = bundleContext.registerService(
   "com.acme.service.fax.FaxService", fax, null);
ServiceRegistration reg2 = bundleContext.registerService(
   "com.acme.service.fax.FaxService", fax, null);

reg1.getReference().equals(reg2.getReference()) is evaluated as false.

public void unregister()

The service registration object can be kept around by the caller as a token for unregistering the service later using this method. If necessary, the original registering bundle can pass the object to other entities, which will be able to perform service unreg istration on behalf of the original registrant. Whoever has the service registration object can unregister the service, so give it out judiciously. For example,

ServiceRegistration printReg =
   bundleContext.registerService(classes, printService, props);
...
printReg.unregister();

public void setProperties(Dictionary properties)

This method allows a caller to modify the properties associated with the registered service after the registration. For example,

ServiceRegistration printReg = bundleContext.registerService(
   classes, printService, initProps);
// get existing properties
Hashtable props = new Hashtable();
ServiceReference printRef = printReg.getReference();
String[] keys = printRef.getPropertyKeys();
for (int i=0; i < keys.length; i++)
   props.put(keys[i], printRef.getProperty(keys[i]));
// modify a property
String features = (String)props.get("features");
features += " transparency double-sided n-up";
props.put("features", features);
printReg.setProperties(props);

adds a few descriptions to the print service's features property.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.102.162