Snippets / Tracker aQute - Software Consultancy
Search
*

Track a Set of Services

Purpose

Show how you can track a set of services in the service registry while they are being registered and unregistered dynamically. This snipppet uses the Service Tracker to simplify handling the dynamics's.

Prerequisites

Instructions

The snippet tracks the Http Services in the registry and adds a small servlet to any Http Service present, now or in the future. To track what is happening, we will also print a message in the log at important moments.

The first part we need to write is a Bundle Activator. In this case we will create a simple ServiceTracker that we will use for the Log Service. It tracks a log service so that we do not have to get the service reference and then the service. This makes it easier to use. We track the Http Service with a subclass of the ServiceTracker. Subclassing the ServiceTracker class is a good way to use the ServiceTracker. This HttpTracker will override the addingService method to register a MiniServlet as well as printing a message in the log.

  Source:  aQute.tracker.Tracker
  package aQute.tracker;

  import org.osgi.framework.*;
  import org.osgi.service.log.*;
  import org.osgi.util.tracker.*;

  public class Tracker implements BundleActivator {
    ServiceTracker logTracker;
    HttpTracker    httpTracker;

    public void start(BundleContext context) throws Exception {
      try {
        logTracker = new ServiceTracker(context,
            LogService.class.getName(), null);
        httpTracker = new HttpTracker(context, logTracker);
        httpTracker.open();
        logTracker.open();
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }  
    public void stop(BundleContext context) throws Exception {
      httpTracker.close();
      logTracker.close();
    }  
  }

For the tracking the Http Service we subclass the Service Tracker and override the three key methods: addingService, modifiedService, and removedService.

  Source:  aQute.tracker.HttpTracker
  package aQute.tracker;

  import org.osgi.framework.*;
  import org.osgi.service.http.*;
  import org.osgi.service.log.*;
  import org.osgi.util.tracker.*;

  class HttpTracker extends ServiceTracker {
    int            count;
    ServiceTracker logTracker;

    HttpTracker(BundleContext context,
        ServiceTracker logTracker) {
      super(context, HttpService.class.getName(), null);
      this.logTracker = logTracker;
    }

    public Object addingService(ServiceReference reference) {
      try {
        HttpService http = (HttpService) super
            .addingService(reference);
        http.registerServlet("/hello", new MiniServlet(),
            null, null);
        log("Added http service #" + ++count);
        return http;
      } catch (Exception nse) {
        log("Failed to add http service: " + nse);
      }
      return null;
    }

    public void removedService(ServiceReference reference,
        Object service) {
      super.removedService(reference, service);
      count--;
      log("Removed http service, left #" + count--);
    }

    public void modifiedService(ServiceReference reference,
        Object service) {
      super.modifiedService(reference, service);
      log("Modified http service");
    }

    void log(final String message) {
      LogService log = (LogService) logTracker.getService();
      if (log != null) log.log(LogService.LOG_INFO, message);
      else System.err.println(message);
    }
  }

The MiniServlet class is a straightforward Http Servlet that prints "Hello World".

  Source:  aQute.tracker.MiniServlet
  package aQute.tracker;

  import java.io.*;
  import javax.servlet.*;
  import javax.servlet.http.*;

  public class MiniServlet extends HttpServlet implements
      Servlet {  
    public void doGet(HttpServletRequest rq,
        HttpServletResponse rsp) throws IOException {
      rsp.getWriter().println("Hello World");
    }
  }

The Bnd file necessary to create the bundle is as follows:

  Bnd:  aQute.tracker.bnd
  Private-Package:   aQute.tracker
  Bundle-Activator:  aQute.tracker.Tracker

Experiences When Writing this Snippet

I first wrote this example quite different but along the way I ran into several problems that I had to solve.

  • I first used the ServiceTracker waitForService method and waited up to ten seconds before I gave up. BJ Hargrave pointed out that this could significantly increase start up time when the Log Service bundle is started after the this bundle. The ServiceTracker methods that are overridden are synchronous. This means that starting the HttpTracker in the activator could potentially add ten seconds to the start time. Waiting in a Bundle Activator is very bad behavior, the start and stop methods must return asap.
  • I forgot to call the open method of the two Service Tracker at first. Really, the exercise is futile if you do not open the Service Tracker.
  • I started inlining the HttpTracker class when I created the instance. Bad idea, the Java inlined classes syntax is way too convoluted.
  • I originally did not do anything with the Http Service that was tracked. This meant the only reference was the HttpTracker.class which showed a subtle shortcoming of bnd: it will not find this reference if you compile to Java class code < 1.4. The Xyz.class construct only puts a String reference in the bytecodes and bnd does not recognize it as a class reference. It therefore did not import the appropriate package. This is only a problem when you use the Class.class construct and not use the class itself in a variable, field, cast, or method call.

Links

Copyright 2006 aQute SARL, All Rights Reserved