6.4. Leveraging the Service Registry

We have seen that the framework maintains a service registry to which a bundle can publish its service by calling the registerService method and from which another can get the service by calling the getServiceReference(s) then getService methods on the BundleContext interface. However, it may not be apparent that you can in fact register any type of object with the registry.

For example, the following snippet is entirely valid, although probably not useful:

for (int i = 0; i < 100; i++)
   bundleContext.registerService(
         "java.lang.Integer", new Integer(i));

And the following call would then return 100 service references to Integer objects in the array

ServiceReference[] ref = bundleContext.getServiceReferences(
      null, "(objectClass=java.lang.Integer)");

This post-and-pick-up feature (also known as the white board approach) opens up a design possibility. To illustrate, we present an example of leveraging the frameworkwide service registry to implement an event dispatching and handling mechanism among bundles.

As we have learned, the framework fires three types of events: service events, bundle events, and framework events. However, how a bundle notifies another of certain application-specific events is not specified. One possible design is as follows:

Suppose the print service wants to send out a notification when the printer malfunctions, and the report service wants to know. The EventListener interface and the Event class are defined respectively as follows:

package com.acme.event.print;
public interface MalfunctionEventListener {
   public void printFailed(MalfunctionEvent e);
}

public class MalfunctionEvent extends java.util.EventObject {
   /** The reasons for malfunction. */
   public static final int OUT_OF_PAPER = 0;
   public static final int PAPER_JAM = 1;
   public static final int TONER_LOW = 2;
   private int reason;

   public MalfunctionEvent(String q, int r) {
      super(q);  // set event source to the print queue name
      this.reason = r;
   }
   /** Gets the name of the troubled print queue. */
   public String getQueue() {
      return (String) getSource();
   }

   /** Gets the reason for the malfunction. */
   public int getReason() { return reason; }
}

The report service may register a listener with the registry like this:

Properties props = new Properties();
props.put("event source", "com.acme.service.print.PrintService");
props.put("event type", "com.acme.event.print.MalfunctionEvent");
bundleContext.registerService(
   "com.acme.event.print.MalfunctionEventListener",
      new MalfunctionEventListener() { ... }, props);

This registration indicates that it is interested in learning that com.acme.event .print.MalfunctionEvent originated in the print service.

Now when the print service detects an out-of-paper problem, it can warn all the listeners like this:

ServiceReference[] ref =
   bundleContext.getServiceReferences(
      "com.acme.event.print.MalfunctionEventListener",
      "(&(event source=com.acme.service.print.PrintService)" +
      "(event type=com.acme.event.print.MalfunctionEvent))");
for (int i=0; i<ref.length; i++) {
   MalfunctionEventListener listener =
      (MalfunctionEventListener) bundleContext.getService(ref[i]);
   listener.printFailed(new MalfunctionEvent("tree-killer",
      MalfunctionEvent.OUT_OF_PAPER));
}

By sufficiently restricting the results with the LDAP filter, we make sure that we get the exact set of listeners for our purpose from the registry.

The print service bundle needs to export the com.acme.event.print package, which is to be imported by any bundle interested in the events.

Leveraging the registry this way can be a double-edged sword. Imagine that a mischievous bundle gleans all the malfunction event listeners and invokes them just for the heck of it. The effect is more harmful than randomly calling a “normal” service, such as the print service, because the listener is designed to be called only from a specific application context (that is, the printer malfunctions), whereas a service, by design, can usually be called under any circumstances without violating its intended application logic.

You should also be very cautious in registering instances of ServiceRegistration or BundleContext that are private to your bundle. Publishing them to the service registry allows others to unregister your service or charge service use to your bundle.

Of course, from a general security perspective, no object in the service registry, be it a service or not, should be used improperly. We can ensure this by subjecting callers to permission checks. For example, we can assign a special permission to the print service bundle so that it is the only one allowed to retrieve the listeners of the specific type from the registry. More details are presented in Chapter 9 when we discuss permissions.

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

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