4.2. Same Service Interface, Different Implementations

An advantage of separation of interface and implementation is that you can provide different implementations to the same interface. Let's see how we can provide another implementation of the DictionaryService interface in our dictionary bundle.

The following implementation class returns word definitions from Webster's dictionary:

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

class Webster implements DictionaryService {
   private Properties defs;

   Webster() {
      defs = new Properties();
      defs.put("accuse", "to charge with a fault or offense");
      defs.put("admire", "to marvel at or esteem highly");
      defs.put("calamity", "a state of deep distress or " +
            "misery caused by major misfortune or loss");
      defs.put("kill", "to deprive of life");
   }

   public String getDefinition(String word) {
      // code is the same as in Devil
   }

   public String[] getDefinitions(char alpha) {
      // code is the same as in Devil
   }
}

The following shows the revised activator. Code in bold type signifies what it takes to register another instance of the service:

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, websterReg;

   public void start(Bundledevil ctxt) {
      DictionaryService ds = 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);
    Properties websterProps = new Properties();
					websterProps.put("author", "Noah Webster");
					websterProps.put("description", "Webster Dictionary");
					websterReg = ctxt.registerService(
					"com.acme.service.dictionary.DictionaryService",
					new Webster(), websterProps);
   }

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

Because we have only revised the implementation portion of the dictionary service, we do not need to change anything in the manifest. We are still exporting the same package containing the service interface.

Create the bundle JAR file for the new bundle. Let's call it newdictionary.jar. It should have the following contents:

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

Because we have modified the dictionary bundle, we can update the existing version that is active in the framework to the new one.

> update 1 file:/C:/users/joe/bundles/newdictionary.jar
> services
[com.acme.service.dictionary.DictionaryService]
   author=Ambrose Bierce
   description=The Devil's Dictionary
[com.acme.service.dictionary.DictionaryService]
   author=Noah Webster
   description=The Webster Dictionary

The update command updates the old dictionary bundle with an ID of 1 to the version identified by the URL. After the update, the services command shows that there are two services registered in the framework now.

So far we have seen the option of packaging multiple implementations of a service interface into one bundle. The advantage is that the implementations may share common structures or code. In the example of the Devil and Webster dictionary services, it would be silly to duplicate the code that accesses the hash table in the getDefinition(s) methods. It can be factored out and shared by both implementations. The disadvantage is that if only one implementation is needed, both must be installed.

A second way of packaging the bundle overcomes this problem: You may split the Devil and Webster implementations into their own bundles, and include the service interface classes in both bundles. With this arrangement, you can install only the needed bundle. The layout of the Devil bundle looks 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

and the layout of the Webster bundle looks like

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

The manifest definitions for both bundles need not be changed. Apparently the activator in either bundle needs to be trivially changed to register only the corresponding implementation of DictionaryService. Its code is not shown.

Although the same com.acme.service.dictionary package in which the service interface class DictionaryService resides appears in both bundles, only one bundle is chosen to export the package, at the framework's discretion. The other bundle, having failed the bid to export the package, imports the package offered by the winner. It is critical that the service interface class, DictionaryService.class, be identical in both bundles, so that either one can be used interchangeably. We elaborate on this requirement from the perspective of class loading and type integrity in “Interbundle Dependency and Class Loading Issues” on page 64.

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

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