4.1. Writing Service Bundles

A service should provide a public interface that specifies what it does and the necessary implementation classes that realize how it is done. The service must be registered with the framework to be useful for other bundles on the gateway.

The general process of developing a service bundle is as follows:

1.
Design the service interface.

2.
Implement the service.

3.
Write a bundle activator that usually registers the service in its start method and unregisters the service in its stop method.

4.
Declare the packages exported by your bundle in the Export-Package manifest header; the service interface should belong to the exported packages.

5.
Compile the classes and pack everything into a bundle JAR file.

Let's see how this is done with a simple service example.

4.1.1. Design the Service Interface

Suppose we want to develop a dictionary service that returns word definitions.[1] The service interface may be defined as:

[1] Hopeless spellers may get excited over this service; the rest of us only need to heed the mechanisms of the process.

package com.acme.service.dictionary;
/**
 * This service produces definitions of words.
 */
public interface DictionaryService {
   /**
    * Gets the definition of a word.
    * @param word the word whose definition is sought.
    * @return the definition or null if the word is not found.
    */
   public String getDefinition(String word);

   /**
    * Gets the definitions of the words beginning with the specified
    * letter.
    * @param alpha the first letter of the words.
    * @return an array of definitions, or null if either no
    * definition is found or the letter is invalid.
    */
   public String[] getDefinitions(char alpha);
}

The service interface should be placed in a dedicated package, because it is to be exported to and accessible by other bundles. It should be annotated with ample comments to define unambiguously the semantics of what the methods are supposed to do. Whenever possible, document all parameters, return values, and exceptions of the methods using the appropriate javadoc tags. We often call the interface a contract, so let's make it detailed enough to read like one.

4.1.2. Implement the Service

The following class provides a trivial implementation of the previous service interface. We return the Devil's definitions authored by Ambrose Bierce (circa 1881–1906) [11].

package com.acme.impl.dictionary;
import java.util.*;
import com.acme.service.dictionary.DictionaryService;

class Devil implements DictionaryService {
   private Properties defs;

   Devil() {
      defs = new Properties();
      defs.put("accuse", "To affirm another's guilt or unworth; " +
            "most commonly as a justification of ourselves for " +
            "having wronged him.");
      defs.put("admire", "Our polite recognition of " +
            "another's resemblance to ourselves");
      defs.put("calamity", "Misfortune to ourselves, " +
            "and good fortune to others.");
      defs.put("kill", "To create a vacancy without nominating " +
            "a successor.");
   }

   public String getDefinition(String word) {
      return (String) defs.get(word);
   }

   public String[] getDefinitions(char alpha) {
      Vector v = new Vector();
      for (Enumeration e = defs.keys(); e.hasMoreElements(); ) {
         String w = (String) e.nextElement();
         if (w.length() > 0 && w.charAt(0) == alpha)
            v.addElement(w + ": " + defs.get(w));
      }
      String[] result = null;
      if (v.size() > 0 ) {
         result = new String[v.size()];
         v.copyInto(result);
      }
      return result;
   }
}

The implementation class should reside in a different package from that of the interface and should be made package private. Hiding the implementation leaves the service interface as the only contact point between the service-providing bundle and the service-using bundles, and is critical for successful bundle update. We explore this theme in more depth later.

4.1.3. Register the Service in the Activator

We register the service in the bundle activator's start method, so that our service becomes available when the bundle is started. We unregister it in the bundle activator's stop method, so that it is withdrawn when the bundle is stopped. The bundle activator is defined as

package com.acme.impl.dictionary;
import java.util.Properties;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import com.acme.service.dictionary.DictionaryService;

public class Activator implements BundleActivator {
   private ServiceRegistration devilReg;

   public void start(BundleContext ctxt) {
      DictionaryService devil = new Devil();
      Properties devilProps = new Properties();
      devilProps.put("author", "Ambrose Bierce");
      devilProps.put("description", "The Devil's Dictionary");
      devilReg = ctxt.registerService(
         "com.acme.service.dictionary.DictionaryService",
         devil, devilProps);
   }

   public void stop(BundleContext ctxt) {
      if (devilReg != null)
         devilReg.unregister();
   }
}

BundleContext ctxt is passed to the start and stop methods by the framework. The bundle interacts with the framework through its bundle context. The start method instantiates an instance of Devil, and registers the service object under the name of its service interface (com.acme.service. dictionary.DictionaryService) with the framework using the registerService method of the BundleContext instance. The service is also registered with the properties of author and description to describe its attributes further. The stop method unregisters the service using the ServiceRegistration object returned at the time of registration.

As a convenience, the framework automatically unregisters any services registered by the bundle when it is stopped. Therefore, the stop method may be equivalently defined as

public void stop(BundleContext ctxt) {
   // nothing needs to be done; the framework will unregister
   // the Devil dictionary service automatically.
}

We always explicitly unregister our services in this chapter, but keep this framework feature in mind.

4.1.4. Define the Manifest Headers

The manifest is defined as follows:

						Exported-Package: com.acme.service.dictionary;
 specification-version=1.0.0
Bundle-Activator: com.acme.impl.dictionary.Activator

We declare here that the service interface in the com.acme.service.dictionary package is to be made available to other bundles using the Export-Package header. We also specify that the version of the exported package is 1.0.0. If the specification-version clause is omitted, the version number is 0.0.0 by default.

4.1.5. Create the Bundle

Compile the code, and package everything in a JAR file, with contents that look like

META-INF/
META-INF/MANIFEST.MF
com/acme/service/dictionary/DictionaryService.class
com/acme/impl/dictionary/Devil.class
com/acme/impl/dictionary/Activator.class

The bundle is ready. Launch the Java Embedded Server software as described in Chapter 2, and install and activate the bundle. You will see the following output from the bundles, services, and exportedpackages commands:

> bundles
ID  STATE     LOCATION
--  ---------  -------------------------
1   ACTIVE    file:C:/users/joe/bundles/dictionary.jar
> services
[com.acme.service.dictionary.DictionaryService]
   author=Ambrose Bierce
   description=The Devil's Dictionary
> exportedpackages
Package: com.acme.service.dictionary (1.0.0)
  Exported by: 1 (file:C:/users/joe/bundles/dictionary.jar)

This output tells us that the dictionary bundle is active in the framework. It has registered DictionaryService with the properties author and description, and it has exported the package com.acme.service.dictionary, version 1.0.0, in which the service interface DictionaryService resides.

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

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