Hi there,

due to my involvement with the Apache XML Graphics project (FOP/Batik)
where we make heavy use of META-INF/services, I've got a big interest in
how best to bring FOP and Batik into OSGi. My particular requirement
here is that FOP and Batik have to continue working in plain Java but
profit from the OSGi service registry for plug-in discovery when
possible.

So, two years ago I came up with this:
http://www.jeremias-maerki.ch/development/osgi/jar-services.html

That works half-way good enough although I've never felt comfortable
enough to push it back to the XML Graphics project as I've had a few
flaws in the above implementation. So, by now SPI Fly does a lot of
things much better than my approach and it is following an RFC that I
find very useful.

However, on the client side, RFC 167 is a bit heavy on J2SE-1.6's
ServiceLoader. Apache XML Graphics is still on J2SE-1.5 (yes, I know, I
know) so it contains its own Services.java to find plug-ins via
META-INF/services. Furthermore, with ServiceLoader you basically have to
poll for changes.

Finally getting to my point: I'd like to offer a little API that brings
some of the service dynamics you get with OSGi services. This can
already be seen on the page indicated above although I've slightly
modified the API. Essentially, to get notified about new or disappearing
plug-ins/services, you can register a service listener. And that would
look like this (client code):

        ServiceTracker<ImageWriter> tracker = 
Plugins.getServiceTracker(ImageWriter.class);
        tracker.addServiceListener(new ServiceListener<ImageWriter>() {

            public void added(ImageWriter writer) {
                register(writer);
            }

            public void removed(ImageWriter writer) {
                unregister(writer);
            }

        });

Where Plugins is:

public class Plugins {

    /**
     * This is the services singleton.
     */
    private static final Services SERVICES = new Services();

    public static void setServicesBackend(ServicesBackend backend) {
        SERVICES.setServicesBackend(backend);
    }

    public static <T> ServiceTracker<T> getServiceTracker(Class<T> 
providerIntf) {
        return SERVICES.getServiceTracker(providerIntf);
    }

}

And the BundleActivator for the client looks like this:

public class Activator implements BundleActivator {

    private volatile ServicesOSGi services;

    /** {@inheritDoc} */
    public void start(BundleContext context) throws Exception {
        this.services = new ServicesOSGi(context);
        Plugins.setServicesBackend(this.services);
    }

    /** {@inheritDoc} */
    public void stop(BundleContext context) throws Exception {
        Plugins.setServicesBackend(null);
        if (this.services != null) {
            this.services.close();
        }
    }

}

What happens is this: by default "Services" starts up with a plain-Java
backend that simply looks up services through classic means. When the
BundleActivator is called, the backend is replaced by ServicesOSGi that,
instead, gets the plug-ins from the service registry. In the background,
any previously discovered plug-ins are "removed()" and the new ones from
the registry "added()".

In the case of classic Java, the "removed()" method is never called
(probably doesn't ever need to be), i.e. no dynamics there.

To conclude: the goal is to profit from OSGi service dynamics when
discovering plug-ins while preserving the possibility to run without
OSGi API runtime dependencies and still in J2SE-1.5. Furthermore, the
OSGi-specific code shall be as minimal as possible to shield those with
no OSGi knowledge in the team from the learning curve.

And now, I'm wondering if you're interested to adopt this as a new part
in SPI Fly as a useful alternative to using ServiceLoader. If you don't
want it, I'm going to publish it under Apache Extras.

Thanks,
Jeremias Maerki

Reply via email to