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
- Log Service - org.osgi.service.log
- Http Service - org.osgi.service.http
- osgi interfaces, provides all service interfaces for the OSGi specifications.
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
waitForServicemethod 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, thestartandstopmethods must return asap. - I forgot to call the
openmethod 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
- The specification discusses services in chapter 701 of the OSGi Compendium.
- BundleContext Javadoc
- Service Tracker Javadoc
- Log Service Javadoc
- Http Service Javadoc
- aQute.tracker.jar


