8.4. Writing DA Services

In this section we learn how to develop device services, driver services, and driver locator services for the serial port. These services are fully operational, which will help you understand DA in concrete terms.

The example we are about to develop consists of a serial service bundle and a driver bundle. Figure 8.4 shows how they are packaged.

Figure 8.4. The serial service bundle contains a serial device service, a driver locator that knows where to find the driver bundle—inside its own bundle. The blowup on the right shows that the driver bundle contains a modem device service and driver service.


The serial port is a 25-pin or 9-pin D-shaped socket usually found at the back of the gateway. It transmits data one bit at a time asynchronously. The serial port is often used to connect to peripherals like mice, modems, and digital cameras.

The hardware requirement for our experiment is simple: You need a computer equipped with serial ports and a NULL modem. A NULL modem is a converter or a cable that allows two serial ports to talk to each other directly by mapping the transmitting pins on one end to the receiving pins on the other. It is readily available from most computer hardware stores. Figure 8.5 shows the hardware setup.

Figure 8.5. You can connect one serial port to another on the same machine through the NULL modem, or you can connect two serial ports on two different machines through the NULL modem. Make sure you get the proper cables with matching genders.


In the Solaris system, use the tip(1) command to verify that they are connected properly. Use the HyperTerminal program in Windows to do the same thing.

8.4.1. The Base Driver

The device driver from the operating system and Java Communications API[3] together make up the base driver for the serial port. It is obviously outside the environment of the OSGi framework. To start the framework using the serial base driver on Windows, do the following:

[3] You can download an implementation of the API from http://java.sun.com/products/javacomm/index.html.

set CLASSPATH=comm_dircomm.jar;jes_pathlibframework.jar
java[4] -Djava.library.path=comm_dir com.sun.jes.impl.framework.Main

[4] On the Solaris system, you need to launch the Java virtual machine using native threads by specifying the -native switch after java (not necessary in Windows because native threads are used by default).

where comm_dir is where you installed the Java Communications API files, and jes_path is where you installed the Java Embedded Server software. You also need to tell the Java runtime environment where to load the native shared library win32com.dll (libSolarisSerialPallel.so on the Solaris system) with either the java.library .path system property or the appropriate environment variable in the operating system (LD_LIBRARY_PATH for UNIX, PATH for Windows).

Once the framework is up and running, you can install and start the log and DA bundles from the console:

> start jes_path/bundles/log.jar, jes_path/bundles/device.jar

The bundle device.jar contains the implementation of DA, which depends on the Log service.

8.4.2. Device Detection

We first develop a bundle that contains a device service representing the serial port. The service is registered when the NULL modem is plugged into the serial port, and it is unregistered when the NULL modem is unplugged, automatically.

Like any service in the framework, we begin with the service interface definition:

package com.acme.service.device.serial;
import org.osgi.service.device.Device;

public interface SerialService extends Device {
   /**
    * Gets the SerialPort object for the port.
    */
   public javax.comm.SerialPort getPort();

   /**
    * Adds an event listener for SerialPortEvents.
    */
   public void addEventListener(
      javax.comm.SerialPortEventListener l);

   /**
    * Removes the event listener for SerialPortEvents.
    */
   public void removeEventListener(
      javax.comm.SerialPortEventListener l);
}

As specified by DA, SerialService extends the org.osgi.service .device.Device interface. An instance of this service represents a serial port available to bundles in the framework. If multiple serial ports are available, each corresponds to an instance of the service. The getPort method gives you access to the serial port itself, whereas the other two methods allow you to add and remove listeners for serial port events. The javax.comm.SerialPort API allows you to add only one serial event listener, and internally we have used it for detecting devices. To work around this restriction, we make it possible to add/remove listeners through the SerialService interface. No other operations need to be defined because the Java Communications API has already served that purpose. It does not make sense to reinvent APIs (see the discussion in Chapter 6).

The activator enumerates all known serial ports on the host and finds the ones that are not owned by other applications. For each available port, we add a serial event listener:

package com.acme.impl.device.serial;
import javax.comm.CommPortIdentifier;
import javax.comm.SerialPort;
...  // other imports

public class Activator implements BundleActivator {
   private Vector ports = new Vector(4);

   public void start(BundleContext ctxt)
      throws Exception
   {
      for (Enumeration enum =
         CommPortIdentifier.getPortIdentifiers();
         enum.hasMoreElements(); )
      {
         CommPortIdentifier portId =
            (CommPortIdentifier) enum.nextElement();
         if (portId.getPortType() ==
            CommPortIdentifier.PORT_SERIAL) {
            try {
               SerialPort port = (SerialPort)
                  portId.open("SerialService", 2000);
               port.notifyOnCTS(true);
               SerialListener serialListener =
                  new SerialListener(ctxt);
               port.addEventListener(serialListener);
               ports.addElement(port);
            } catch (PortInUseException e) {
                  // the serial port has been occupied; skip it
            } catch (TooManyListenersException e) {
                  // can't happen
            }
         }
      }
   }
   public void stop(BundleContext ctxt)
      throws Exception
   {
      for (int i=0; i<ports.size(); i++) {
            SerialPort port = (SerialPort) ports.elementAt(i);
            port.removeEventListener();
            port.close();
      }
   }
}

Because we are interested in the signal on the Clear To Send (CTS) pin of the serial interface, we tell the serial port not to mask any event on that pin but to send it to our listener:

port.notifyOnCTS(true);
port.addEventListener(serialListener);

The activator's stop method is self-explanatory: When the bundle is deactivated, we clean up by removing the serial event listener and close the serial ports. The complete source of the activator can be found in Appendix A, section A.2.1.5.

The serial event listener is at the heart of device detection, and its implementation is as follows:

class SerialListener implements SerialPortEventListener {
   private BundleContext context;
   private ServiceRegistration serialReg = null;
   private SerialServiceImpl serialService = null;
   private String[] deviceClazzes = {
      "com.acme.service.device.serial.SerialService",
      "org.osgi.service.device.Device" };

   SerialListener(BundleContext ctxt) {
      this.context = ctxt;
   }

   public synchronized void serialEvent(SerialPortEvent e) {
      SerialPort port = (SerialPort) e.getSource();
      int t = e.getEventType();
      if (t == SerialPortEvent.CTS) {
            // listen to CTS pin of the serial interface
            boolean plugged = e.getNewValue();
            if (plugged) {
               // register a serial device service when a
               // device is connected to the serial port
               Properties props = new Properties();
               props.put("DEVICE_CATEGORY", "serial");
               props.put("DEVICE_MAKE", "Acme");
               props.put("Port", port.getName());
               serialService = new SerialServiceImpl(port);
               serialReg = context.registerService(deviceClazzes,
                  serialService, props);
            } else {
               // unregister the serial device service when
               // the device is disconnected from the serial
               // port
               if (serialReg != null) {
                  serialReg.unregister();
                  serialReg = null;
                  serialService = null;
            }
         }
      }
   }

SerialListener's serialEvent method is called if an event occurs on the serial port. If the CTS pin is turned on (getNewValue returns true), a device is connected to the port; if it goes off (SerialEvent's getNewValue method returns false), the device is disconnected from the port.

Once we discover that a device is connected to the serial port, we register an instance of SerialService to represent the port, along with the service properties DEVICE_CATEGORY, DEVICE_MAKE, and Port. Conversely, if the device is found to be disconnected, we unregister the service accordingly.

The SerialServiceImpl class is a straightforward implementation of the SerialService interface. Its source is listed in Appendix A, section A.2.1.2, and is not shown here.

The manifest is defined as follows. Because we reference DA interfaces, we need to import the DA package:

Bundle-Activator: com.acme.impl.device.serial.Activator
Import-Package: org.osgi.service.device

Compile the source and pack the classes into a bundle named serial.jar. Its contents should look like

META-INF/MANIFEST.MF
com/acme/service/device/serial/SerialService.class
com/acme/impl/device/serial/Activator.class
com/acme/impl/device/serial/SerialListener.class
com/acme/impl/device/serial/SerialServiceImpl.class

It's time to see things in action. From the console, install and start the serial.jar bundle, then examine the list of registered services:

> start serial.jar
> services
[org.osgi.service.log.LogService]
      description=The standard OSGi Log service
[org.osgi.service.log.LogReaderService]
      description=The standard OSGi LogReader service

This output shows that only the services from the log bundle are registered within the framework.

Plug the NULL modem into one of the serial ports at the back of the machine, and reissue the services command in the console:

> services
[org.osgi.service.log.LogService]
      description=The standard OSGi Log service
[org.osgi.service.log.LogReaderService]
      description=The standard OSGi LogReader service
[org.osgi.service.device.Device,com.acme.service.device.serial.SerialService]
						Port=COM2
						DEVICE_CATEGORY=serial
						DEVICE_MAKE=Acme
					

A new SerialService has been automatically registered! Unplug the serial cable and verify that the service is unregistered.

Obviously, every type of device has its own way of detecting whether a device is connected. This example presents the mechanism tailored for the serial interface made available to us from the Java Communications API. This is why, in many situations, device detection hinges on the capability from the native drivers.

8.4.3. Device Refinement

Registering a device service kicks off a device refinement process controlled by the device manager. To many applications, bare-bone SerialService provides functionality that is at an inappropriately low level. For example, a service of a bulletin board system would be much more comfortable with ModemService, which communicates through the serial port, than dealing with SerialService directly.

8.4.3.1. The Confidence Level for Matching a Device

First, we need to add a few constants to the SerialService interface to indicate how well the device can be matched by a driver:

public interface SerialService extends Device {
   public static final int MATCH_OK = 1;
   // the rest of the interface remains the same
}

You may specify multiple levels of matching values from a minimum to a maximum integer. In our example, the scale is binary: Either match or not. Thus, we only define one constant here. These constants are used by the Driver service, which is explained in Section 8.4.3.3.

8.4.3.2. The Driver Locator Service

We next add a DriverLocator implementation to our serial bundle. The DriverLocator service knows where to find and download drivers capable of refining SerialService:

package com.acme.impl.locator;
// imports
public class DriverLocatorImpl implements DriverLocator {

   // map a device category to IDs of refining drivers
   private Hashtable categoryMap;
   // map driver IDs to their URLs
   private Hashtable driverMap;
   static final String DRIVER_ID_PREFIX =
      "com.acme.device.drivers";

   public String[] findDrivers(Dictionary props) {
      String make = (String) props.get("DEVICE_MAKE");
      String category = (String) props.get("DEVICE_CATEGORY");
      if (!"Acme".equalsIgnoreCase(make))
      return null;
      String[] ids = (String[]) categoryMap.get(category);
      return ids;
   }

   public InputStream loadDriver(String id) throws IOExcep-
   tion {
      URL u = (URL) driverMap.get(id);
      if (u != null) {
            return u.openStream();
      }
      return null;
   }

   public DriverLocatorImpl() {
      categoryMap = new Hashtable(3);
      categoryMap.put("serial", new String[] {
         DRIVER_ID_PREFIX + ".serial.modem.0" });
      driverMap = new Hashtable(3);
      driverMap.put(DRIVER_ID_PREFIX + ".serial.modem.0",
        this.getClass().getResource("/drivers/driver.jar"));
   }
}

First, the driver locator knows where to find any refining drivers for a given set of device service properties. The drivers are identified by their driver IDs. In the previous example, we use a very specific naming convention for driver IDs:

com.acme.device.drivers.<origin device category>.<refined device
							category>.<version number>

where com.acme.device.drivers is the prefix for all drivers written by Acme, <origin device category> refers to the device category as the basis for refinement, <refined device category> indicates the target of refinement, and <version number> distinguishes various incarnations of the same driver. For example, the driver with an ID of com.acme.device.drivers.serial.modem.0 indicates that the driver written by Acme can refine the serial device category to the modem category. To find refining drivers for a device service, the findDrivers method first obtains the device category from the service properties, and checks whether it can be used as the basis for any refinement based on the naming convention of the driver IDs.

Second, the driver locator service knows where to download the driver if any is found. In our example, the driver bundle is stored as a resource in the serial bundle. The internal data structure driverMap maps the driver ID to the URL of the bundle.

The policy of how and where to locate drivers in this example is admittedly simplistic, yet it represents only one of numerous strategies. You are free to implement as many driver locator services as you need. Your drivers may be stored at a centralized server accessible through URLs, or you may have elaborate matching criteria to determine to which device categories a given category can be refined. These aspects are intentionally left undefined in the standard to allow you the maximum flexibility.

The serial bundle's activator needs to register the newly added driver locator service when it is started. The code in bold type shows the new additions; other code remains the same:

package com.acme.impl.device.serial;
// imports
public class Activator implements BundleActivator {
   ...
   public void start(BundleContext ctxt) {
   Properties locatorProps = new Properties();
							locatorProps.put("description",
							"Locate refining drivers for the serial device category.");
							ctxt.registerService(
							"org.osgi.service.device.DriverLocator",
							new DriverLocatorImpl(), locatorProps);

      ...
   }

   public void stop(BundleContext ctxt) {
      ...
   }
}

The contents of the new serial bundle then show the following layout:

META-INF/MANIFEST.MF
com/acme/service/device/serial/SerialService.class
com/acme/impl/device/serial/Activator.class
com/acme/impl/device/serial/SerialServiceImpl.class
com/acme/impl/locator/DriverLocatorImpl.class
							drivers/driver.jar
						

The entry drivers/driver.jar is a mystery to us at this point. This is the driver that is used to refine serial device category services and is retrieved by the driver locator during refinement. But what exactly is involved? Let's leave the serial bundle aside for a moment and delve into the world of refining drivers.

8.4.3.3. The Driver Service

Many drivers may be available to provide refinement for a device category. The most appropriate driver is selected by the device manager by calling its match method. Once a winner is selected, its attach method is called to register the new device service.

Here is the implementation of the driver service in our example:

package com.acme.impl.driver;
// imports
class DriverImpl implements Driver {
   private BundleContext context;
   private ServiceRegistration reg = null;

   public int match(ServiceReference sr) throws Exception {
      String category =
         (String) sr.getProperty("DEVICE_CATEGORY");
      if ("serial".equals(category))
            return SerialService.MATCH_OK;
      return Device.MATCH_NONE;
   }

   public String attach(ServiceReference sr) throws Exception {
      String[] deviceClazzes = new String[] {
            "org.osgi.service.device.Device",
            "com.acme.service.device.modem.ModemService" };
      Properties props = new Properties();
      props.put("DEVICE_CATEGORY", "modem");
      props.put("DEVICE_MAKE", "Acme");
      reg = context.registerService(deviceClazzes,
         new ModemServiceImpl(), props);
      return null;
   }

   DriverImpl(BundleContext ctxt) {
      this.context = ctxt;
   }
}

When this driver is asked how well it can match the device service (represented by the ServiceReference instance sr on entering the match method), it first checks to which device category the device belongs. The driver knows that it can refine the serial category well, therefore it returns the match_OK value if the service reference represents a serial device. Otherwise, it reports that it can match none, with Device.MATCH_NONE as the return value.

After gleaning match values from all drivers (in our case, the driver service is the only one), the device manager selects the highest bidder and calls its attach method. In our example, we register the refined ModemService under the “modem” device category, and therefore complete our mission of refining SerialService to the next level.

The driver bundle's activator performs the task of registering the driver service when the bundle is started, and unregistering it when it is stopped:

package com.acme.impl.driver;
// imports
public class Activator implements BundleActivator {
   private ServiceRegistration reg;

   public void start(BundleContext ctxt) {
      Properties props = new Properties();
      props.put("DRIVER_ID",
         "com.acme.device.drivers.serial.modem.0");
      props.put("description", "The driver that refines " +
         "a serial device to a modem device.");
      reg = ctxt.registerService("org.osgi.service.device.Driver",
               new DriverImpl(ctxt), props);
   }
   public void stop(BundleContext ctxt) {
      if (reg != null)
            reg.unregister();
   }
}

ModemService exposes operations expected of a modem. The main part of its interface is as follows:

package com.acme.service.device.modem;
import org.osgi.service.device.Device;

public interface ModemService extends Device {
   /**
    * The data connection to a connected modem.
    */
   interface DataConnection { ... }

   /**
    * Dials up a modem with the given phone number.
    */
   public DataConnection dialup(String phoneNumber) throws IOException;

   /**
    * Gets information about the modem.
    */
   public String getInfo() throws IOException;

   /**
    * Hangs up the modem.
    */
   public void hangup() throws IOException;
   /**
    * Configures the volume of the modem's speaker.
    */
   public void configureSpeaker(int mode) throws IOException;

   /**
    * Gets the value of the given S register.
    */
   public byte getSRegister(int r) throws IOException;
   /**
    * Sets the value for the given S register.
    */
   public void setSRegister(int r, byte value) throws IOException;
}

The implementation class ModemServiceImpl is shown in Appendix A, section A.2.2.4.

Now we are ready to show the complete layout of the driver bundle:

META-INF/MANIFEST.MF
com/acme/service/device/modem/ModemService.class
com/acme/impl/device/modem/ModemServiceImpl.class
com/acme/impl/driver/
com/acme/impl/driver/DriverImpl.class
com/acme/impl/driver/Activator.class

With the changes in place, we can now witness the refinement process in action. Unplug the NULL modem from the serial port and restart the framework. From the framework console, update the serial bundle to incorporate the new contents:

> update serial.jar

Plug the NULL modem back into the serial port. This act causes the following chain of events to take place:

  1. The base driver notices that a device is plugged into the serial port via SerialPortEventListener.

  2. The listener registers SerialService with the framework.

  3. Because SerialService is also registered under the org.osgi.service.device.Device type, the device manager provided by the DA bundle is notified.

  4. The device manager looks into the framework's service registry for driver locator services. It finds the one registered by the serial bundle.

  5. The device manager invokes the driver locator's findDrivers method, passing in the SerialService's service reference. The service property DEVICE_CATEGORY shows that its device category is “serial,” so the method returns the driver ID com.acme.device.drivers.serial.modem.0.

  6. The device manager then asks the driver locator for the location of the driver bundle corresponding to the ID. Because the driver is included within the serial bundle, a bundle resource URL is returned to obtain the input stream to the driver.

  7. The device manager downloads the driver from the input stream and installs and starts the driver bundle, at which time a Driver service is registered with the framework.

  8. The device manager calls the Driver service's match method, passing in the SerialService's service reference. Based on the serial device service's category, a SerialService.MATCH_OK value is returned, indicating that the driver is capable of refining the serial service.

  9. The device manager then calls the Driver service's attach method, which registers the ModemService included in the driver bundle with the framework.

  10. As in step 3, the device manager is notified of the ModemService registration, because it is also registered under the org.osgi.service.device.Device type. As a result, the preceding process is repeated.

  11. The device manager again checks the driver locator, which has no driver to install for the modem service.

  12. The device manager then consults the only driver service by calling its match method. Having discovered that the device category is “modem,” the method reports Device.MATCH_NONE to indicate that it is clueless as how to refine ModemService further. This ends the refinement process.

At the console, issue the following command:

> bundles
ID  STATE   LOCATION
--  ------  ----------------------------
1   ACTIVE  file:jes_path/bundles/log.jar
2   ACTIVE  file:jes_path/bundles/device.jar
6   ACTIVE  file:/home/joe/bundles/serial/serial.jar
7  ACTIVE  com.acme.device.drivers.serial.modem.0
						

Bundle 7 is the driver bundle that is found by the driver locator service and is installed by the device manager automatically in step 7.

Now check the service registration:

> services
[org.osgi.service.device.Driver]
							DRIVER_ID=com.acme.device.drivers.serial.modem.0
							description=The driver that refines a serial device to a
							modem device.
							[org.osgi.service.device.Device,
							com.acme.service.device.modem.ModemService]
							DEVICE_CATEGORY=modem
							DEVICE_MAKE=Acme
							Port=COM2
							[org.osgi.service.device.DriverLocator]
							description=Locate refining drivers
							for the serial device category.
[org.osgi.service.device.Device,com.acme.service.device.serial.
SerialService]
      DEVICE_CATEGORY=serial
      DEVICE_MAKE=Acme
      Port=COM2
[org.osgi.service.log.LogService]
      description=The standard OSGi Log service
[org.osgi.service.log.LogReaderService]
      description=The standard OSGi LogReader service

The output makes it clear that in response to the attachment of the serial cable, a driver service is registered in step 7, and a refined device service, ModemService, is also registered with the framework in step 9.

8.4.3.4. A Smarter Refining Driver

Our example so far assumes that whenever a device is attached, it must be a modem. What if it is not a modem but a serial connection used simply to transfer data? In this section, we make our refining driver smarter not only by trusting the device category from the device service properties, but also by talking to the underlying device briefly to make sure. If the driver satisfies itself that the device is indeed a modem, ModemService is registered.

To be certain that the device attached is indeed a modem, we modify the driver's match method to send an AT command through the serial port. If we get OK back as the response, we know it is a modem:

package com.acme.impl.driver;
// imports
class DriverImpl implements Driver {
   private BundleContext context;
   ...
   public int match(ServiceReference sr) throws Exception {
      String category =
         (String) sr.getProperty("DEVICE_CATEGORY");
      if ("serial".equals(category)) {
         if (modemAccessed(sr)) {
            return SerialService.MATCH_OK;
         }
      }
      return Device.MATCH_NONE;
   }

   // The rest of the code remains the same

   private boolean modemAccessed(ServiceReference sr) {
      boolean isModem = false;
      SerialService ss = (SerialService) context.getService(sr);
      SerialPort port = ss.getPort();
      try {
         // send AT command to the serial port
         // if we get OK as reply, set isModem to true
      } finally {
         context.ungetService(sr);
      }
      return isModem;
   }
}

With this enhancement, if you simply attach a NULL modem, SerialService is registered, but the device refinement process does not go any further. Only when a full-featured modem is connected to the serial port is ModemService registered.

Note that calling SerialService is a temporary act. Therefore, we must be sure to “unget” the service after we are done in the modemAccessed method.

8.4.3.5. Disconnecting the Device

In our discussion so far, we know that when the serial cable is disconnected from the port, SerialListener is notified and unregisters SerialService. However, the driver has registered ModemService as the refinement for SerialService. It is logical to withdraw ModemService too when SerialService is gone.

The responsibility lies with the refining driver, which monitors service events to find out whether SerialService is about to be unregistered. The following code in bold type shows the new additions; the remaining code is the same:

package com.acme.impl.driver;
// imports
class DriverImpl implements Driver {
   private BundleContext context;
   private ServiceRegistration modemReg = null;
   private ServiceReference serialRef;

   public int match(ServiceReference sr) throws Exception {
      ...
   }

   public String attach(ServiceReference sr) throws Exception {
      serialRef=sr;
   }

   DriverImpl(BundleContext ctxt) {
      this.context = ctxt;
      try {
     String filter = "(objectClass=" +
							"com.acme.service.device.serial.SerialService)";
							this.context.addServiceListener(new ServiceListener() {
							public void serviceChanged(ServiceEvent e) {
							if (e.getType() == ServiceEvent.UNREGISTERING) {
							if (e.getServiceReference().equals(serialRef)
							&&modemReg != null) {
							modemReg.unregister();
							modemReg = null; }
							}
							}
							}, filter);
      } catch (InvalidSyntaxException e) {
         // can't happen
      }
   }
}

With the revised driver, when you unplug the serial cable and examine the registered services, you will find that both SerialService and ModemService are unregistered.

8.4.4. The Reconfiguration Process

Many devices have composite configurations. For instance, a home theater system consists of video and audio components. It can function as one entity when you play a movie on DVD with surround sound. Its components, however, may also function independently: Only the stereo system is used when you listen to a music CD.

DA is designed with such scenarios in mind. It allows a comprehensive device representation (home theater) to be registered first. If no other driver is found to refine the device, the driver that has registered the comprehensive device service may withdraw the service and attempt to register devices representing its subcomponents (a video service and a stereo service).

Let's look at the driver designed to handle reconfigurations:

import org.osgi.service.device.*;
class ReconfigDriver implements Driver {
   private ServiceRegistration reg = null;
   private BundleContext context;

   ReconfigDriver(BundleContext ctxt) {
      context = ctxt;
   }

   public int match(ServiceReference sr) throws Exception {
      String category =
         (String) sr.getProperty("DEVICE_CATEGORY");
      if ("havi".equals(category))   // can refine a HAVi service
         return HaviService.MATCH_OK;
      return Device.MATCH_NONE;
   }

   public String attach(ServiceReference sr) throws Exception {
      String[] deviceClazzes = new String[] {
         "org.osgi.service.device.Device",
         "com.acme.service.device.av.HomeTheatre" };
      // the HAVi service is refined to a HomeTheatre service
      reg = context.registerService(deviceClazzes,
         new HomeThreatreImpl(this), props);
      return null;
   }

   void changeConfiguration() {
      if (reg != null) {
         reg.unregister();  // unregister HomeTheatre
         reg=null; }
         ...
         // register a video and an audio service instead
      context.registerService(videoClazzes,
         new VideoServiceImpl(), videoProps);
      context.registerService(audioClazzes,
         new AudioServiceImpl(), audioProps);
   }
}

Here are the partial definitions of the home theater service, service interface first:

public interface HomeTheatre
   extends org.osgi.service.device.Device
{
   // methods pertaining to functions of a home theatre
   ...
}

And here is the implementation class:

class HomeTheatreImpl implements HomeTheatre {
   private ReconfigDriver driver;

  public void
						noDriverFound() {
						// No driver can further refine HomeTheatre;
						// change device configurations
						this.driver.changeConfiguration();
						}

   // implementation of the methods pertaining to a home theatre
   ...
   HomeTheatreImpl(ReconfigDriver d) {
      this.driver = d;
   }
}

The reconfiguration driver can refine the HAVi service to a refined home theater service. However, further refinement is not available. At this point the driver has two options: (1) be satisfied with the latest refinement and do nothing or (2) realize that the latest refinement is a dead end and try different configurations. Let's see how the latter is done using the previous example.

When no refining driver can be found for HomeTheatre service, the noDriverFound method on the HomeTheatre service is invoked. The method turns around and calls back to the changeConfiguration method on the reconfiguration driver. The driver unregisters the HomeTheatre service and registers two of its components: a video service and an audio service.

The device can call back to its registering driver, because a reference to the driver is passed into HomeTheatre through its constructor.

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

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