<< Back to main.

Java 8 and OSGi ServiceTracker

OSGi is built on three main principles: component separation, lifecycle management, services. For services one of the major demanding tasks is to handle the visibility and existence of services from client side. That means a service client (service consumer) must know when a service gets available and when it gets removed or disabled. While DS (Declarative Services) is one way to overcome difficulties with respect to that, we will be looking at the more traditional way of using Service Trackers (DSs have their advantages and disadvantages, I will not cover this here), more precisely using Java 8 functional capacities.

This is not an introduction into OSGi. If you need one, please make yourself comfortable using one of the tutorials or text books.

Service Provider

The service provider is a standard programmatic service provider. In the activator of some service bundle, add

    private ServiceRegistration<MyService> sreg;
	
    @SuppressWarnings("unchecked")
    public void start(BundleContext context) throws Exception {
      sreg = (ServiceRegistration<MyService>) context.registerService(MyService.class.getName(), 
             new MyServiceImpl(), null);
      System.out.println("Registered " + sreg);
    }

    public void stop(BundleContext context) throws Exception {
      sreg.unregister();
    }
MyService is just any Java interface with service methods, and MyServiceImpl an implementation (nothing special there, just a plain Java class).

Service Consumer

I present a general purpose service tracker class which uses Java 8 functional interface method parameters to react on starting and stopping services (it is also possible to react on changing services, but I'll leave this open for the reader's experimenting pleasure):

public class MyServiceTracker<S, T> implements ServiceTrackerCustomizer<S, T> {
  private BundleContext ctx;
  private Consumer<ServiceReference<S>> addingConsumer;
  private Consumer<ServiceReference<S>> removedConsumer;

  public MyServiceTracker(BundleContext ctx, Consumer<ServiceReference<S>> addingConsumer,
                          Consumer<ServiceReference<S>> removedConsumer) {
    this.ctx = ctx;
    this.addingConsumer = addingConsumer;
    this.removedConsumer = removedConsumer;
  }

  @SuppressWarnings("unchecked")
  @Override
  public T addingService(ServiceReference<S> sref) {
    T cs = (T) ctx.getService(sref);
    addingConsumer.accept(sref);
    return cs;
  }

  public void modifiedService(ServiceReference<S> sref, T servc) {
    System.out.println("modifiedService: " + sref);
  }

  public void removedService(ServiceReference<S> sref, T servc) {
    removedConsumer.accept(sref);
  }
}

The activator bundle class which administers the tracker then would read, using lambda notation:

  private ServiceTracker<ServiceReference<MyService>, MyService> tracker = null;

  @Override
  public void start(BundleContext ctx) throws Exception {
    tracker = new ServiceTracker<>(ctx, MyService.class.getName(), new MyServiceTracker<>(ctx, sref -> {
        System.out.println("Added: " + sref);
        MyService servc = (MyService) ctx.getService(sref);

        // --- do something with the service: ---
        System.out.println(servc.someMethod());

        ctx.ungetService(sref);
      } , sref -> {
        System.out.println("Removed: " + sref);
      }));
      tracker.open();
  }
  @Override
  public void stop(BundleContext ctx) throws Exception {
    tracker.close();
  }

The code just after the -> signs will be getting executed when the service gets available (or, at the beginning, if it is available) or gets deactivated. Using functional notation thus makes the service tracker approach much more readable and cleaner.