aries-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From David Bosschaert <david.bosscha...@gmail.com>
Subject Re: SPI-Fly and provider-configuration file names that are different than the abstract service class
Date Mon, 14 Jun 2010 09:52:40 GMT
Hi all,

On 11 June 2010 16:25, David Bosschaert <david.bosschaert@gmail.com> wrote:
> The main trick around the FactoryFinder/ServiceLoader is to set the
> Thread Context classloader to the right classloader when
> FactoryFinder.find()/ServiceLoader.load() is invoked. Although
> ServiceLoader.load() has an overload that accepts a classloader we
> can't assume that all clients use it.
> You have already provided a fairly painful idea to how an
> implementation could work (or not as the case might be;). Another idea
> would be to declare in the bundle manifest what SPIs are being used
> (remember we *can* modify the bundle manifest, we might be writing an
> OSGi bundle or bundelizing a non-OSGi jar). Maybe some entity could
> intercept the ServiceLoader.load() calls using some AOP tricks and set
> the Thread context classloader to the one specified in the bundle
> manifest for the duration of that call. The bundle would have to have
> the right set of imports and this would possibly bind the bundle to
> one particular SPI impl but then again that would highlight the
> benefit of using the OSGi Service Registry instead...
> Again the above is just another wild idea to add to the mix... I might
> do some experimentation to see how far I can get this...

I did some experiments with AOP and ended up using AspectJ for my
prototype because it works with Equinox. Weaving is not supported in a
standard way yet across all the OSGi frameworks but that's something
that is being worked on in the OSGi alliance through RFP 139.

Here's what I was able to do:
I wrote a simple SPI for use with the JRE java.util.ServiceLoader
(which are typically realized via an abstract class):
  public abstract class SPIProvider {
    public abstract void doit();
  }

I put an implementation of this SPI in a bundelized jar that
advertises this SPI as any JRE SPI would: via a
META-INF/services/...SPIProvider file which holds the name of the impl
class.

Then in the activator of my client bundle I was able to obtain the
service using normal ServiceLoader calls:
  public class Activator implements BundleActivator {
    public void start(BundleContext context) throws Exception {
      ServiceLoader<SPIProvider> ldr = ServiceLoader.load(SPIProvider.class);
      for (SPIProvider spiObject : ldr) {
        spiObject.doit(); // invoke the SPI object
  } } }
So you can see, nothing special here. Existing clients can load the
SPI the way they have always been, which is essential if you're
integrating a library for which you don't have the source.

To get ServiceLoader to load the the service through the right
classloader I wrote an aspect that wraps the actual
ServiceLoader.load() call and sets the ThreadContext for the duration
of the call. What it should set the classloader to is figured out via
an OSGi Service that I introduced: SPIClassloaderAdviceService. The
aspect invokes on the service to ask it what classloader should be
used for a particular SPI class. I can get to the Service Registry
from the aspect through the BundleReference API.

aspect BundleAspect {
  pointcut serviceloader(Class cls) :
    args(cls) && call(ServiceLoader ServiceLoader.load(Class));

  ServiceLoader around(Class cls) : serviceloader(cls) {
    /* details ommitted, use cls.getClassLoader() as
       BundleReference to get to ServiceRegistry... */
    SPIClassloaderAdviceService svc = ... // obtain from OSGi Service Registry
    ClassLoader targetLoader = svc.getServiceClassLoader(cls);
    ClassLoader prevCl =
Thread.currentThread().getContextClassLoader();
    try {
      Thread.currentThread().setContextClassLoader(targetLoader);
      return proceed(cls);
    } finally {
      Thread.currentThread().setContextClassLoader(prevCl);
} } }

The SPIClassloaderAdviceService needs to be configured somehow. In my
prototype I let the SPI wrapper bundle register it, a bit like
OSGiLocator in the SMX bundles, but it could also be configured purely
declaratively through an extender that reads specific instructions
from the manifests of wrapped bundles.

I can see two disadvantages to the approach:
1. It requires weaving support in OSGi. But hopefully that will be
there in a standard way by the next OSGi release.
2. My current strategy is global. However it should be possible to
create a strategy that takes the calling bundle into account so you
can say for SPI x bundle a should use x1 and bundle b should use x2...

Thoughts anyone?

Best regards,

David

Mime
View raw message