aries-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Bartosz Kowalewski <kowalewski.bart...@gmail.com>
Subject Re: SPI-Fly and provider-configuration file names that are different than the abstract service class
Date Fri, 25 Jun 2010 21:32:28 GMT
Hi David,

I've spent quite a lot of time playing with ApsectJ weaver inside an
OSGi container. I have never had chance to do it before, so this was
quite an interesting experience.

I managed to make Eclipse Aspects/Weaving work inside a Pax Exam test.
I can contribute this simple project with integration tests (of course
after applying some clean-up) if you find it useful. I think that
SPI-Fly requires a change in project structure anyway - it needs a
parent project and a second subproject - spifly-itests.

Some more comments on the SPI-Fly + AOP topic:
1. My understanding is that there's no single uniform mechanism for
supporting AspectJ load-time weaving that would work in all OSGi
containers. Due to the specifics of the OSGi world, container-specific
mechanism are required. Am I right? For Equinox it's Equinox
Aspects/Weaving and there's no such mechanism for Felix. This seems to
be a really important disadvantage of using LTW in SPI-Fly.
2. The problem with adding aspects to bundles is still unresolved. I'm
not sure if there's a clean solution for adding aspects to consumer
bundles (or bundles that provide the API). Of course some ugly
solutions can be applied (like my original headache causing fragment
based one), but these are more intrusive that we might wish.
3. I started implementing support for SPI-Consumer and SPI-Provider
headers that contain some data helpful whne running the aspect, i.e.
api name and provider name/version for the Provider header, and some
mechanism to define consumer constraints/hints in the SPI-Consumer
header that would help the aspect that will tweak the thread context
classloader to make decisions about providers. These mechanisms are
similar to the ones that you described in one of your e-mails.
However, I feel that we should first solve #1 and #2 above and only
then it makes sense to continue with the implementation.

Thanks,
  Bartek


2010/6/16 David Bosschaert <david.bosschaert@gmail.com>:
> I just committed my prototype as discussed in this thread to a contrib
> dir in the spi-fly tree:
>  http://svn.apache.org/repos/asf/incubator/aries/trunk/spi-fly/contrib/pilot_unmodified_consumer
>
> 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
> yet...
>
> Best regards,
>
> David
>
> On 15 June 2010 07:41, David Bosschaert <david.bosschaert@gmail.com> 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 <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