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 Tue, 15 Jun 2010 06:41:41 GMT
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 <kowalewski.bartosz@gmail.com> 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 <david.bosschaert@gmail.com>:
>> 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