aries-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From David Bosschaert <>
Subject Re: SPI-Fly and provider-configuration file names that are different than the abstract service class
Date Wed, 16 Jun 2010 16:04:41 GMT
I just committed my prototype as discussed in this thread to a contrib
dir in the spi-fly tree:

I didn't have the time yet to mavenize it so these are just Eclipse
projects - main thing is that I wanted to share the code.
You'll find 4 bundles in there:
* SPIConsumerBundle - the bundle that consumes the SPI using plain
ServiceLoader calls. This bundle also contains the bundle aspect.
* MyServiceSPI - an arbitrary SPI wrapped in a bundle
* MyServiceImpl - an arbitrary implementation of that SPI complete
with META-INF/services file wrapped in a bundle
* ServiceLoaderAPI defines an OSGi Service that is internally used by
the aspects and possibly a future extender. This API could well turn
out to be an implementation detail and may not need to be public

One thing that we really want is the aspect to be 'invisible' - added
automatically to any consumer bundle that opts in using its opt-in
header (e.g. SPI-Consumer). I just haven't figured out how to do this

Best regards,


On 15 June 2010 07:41, David Bosschaert <> wrote:
> Hi Bartek,
> Regarding your comment 1 - yes the AOP approach needs to list exactly
> which APIs to wrap.
> I also agree with your comment 2 - in general the non-OSGi approach to
> using SPIs has drawbacks in terms of lifecycle. Generally there will
> be no support for dynamic replacement of the SPI - this limitation
> goes away when clients use the OSGi way of accessing the SPIs: through
> the service registry :)
> I'll try to find some time to add my experimental code to the spi-fly
> code base so we can maybe improve it in a larger group.
> One thing that I haven't figured out is whether it is possible to
> dynamically add aspects to the system whenever a bundle gets
> installed. This would probably be necessary if we want the aspects to
> have different mappings per client bundle as I haven't yet found a way
> to otherwise figure out in the aspect who the client bundle is.
> Best regards,
> David
> On 14 June 2010 21:56, Bartosz Kowalewski <> wrote:
>> Hi David,
>> Your approach is much cleaner than my headache-causing fragment-based
>> one. It doesn't leave any resources that could be messed up by the
>> user, while in the approach that I described anybody could uninstall
>> those fragments. Moreover, the AOP-based solution enables one to use
>> different implementations simultaneously. This would not be possible
>> with the fragment based approach which would cause a single provider
>> to be attached to the API bundle.
>> A few thoughts off the top of my head (these might potentially be drawbacks):
>> 1. There are many different mechanisms that would need to be wrapped
>> with aspects: ServiceLoader, FactoryFinders in several packages,
>> ContextFinder (JAXB), javax.mail.Session which (as far as I know)
>> loads resources directly from a classloader - without any finder
>> class. The ugly fragment-based approach would probably handle all
>> these cases in the same (uniform) way.
>> 2. A general comment to libraries using SPI: Some of them might not be
>> prepared to cleanly handle a dynamic environment where classes
>> (bundles, providers) come and go. I think that the javamail library
>> uses some kind of a caching mechanism to optimize loading
>> configuration the second time it is called. As far as I remember it
>> keeps a mapping from context classloader <-> some class.
>> Best regards,
>>  Bartek
>> 2010/6/14 David Bosschaert <>:
>>> Hi all,
>>> On 11 June 2010 16:25, David Bosschaert <> 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

View raw message