Hot questions for Using AspectJ in osgi

Question:

I want to add AOP for classes of different bundles in OSGI (Apache Felix)

I need to create Aspect and to add crosscutting functionality into methods of each implementation specific interface. (prefered with help aspectj)

Problems:

  • All implementations of aimed interface in different bundles
  • Each of bundles have self classpath.
  • We do not know witch bundles have implementation of interface

please help 8)


Answer:

The OSGi weavinghooks specification that covers weaving of classes for adding aspects.

Here is an example of how to use it. The example uses asm but I am pretty sure you can adapt it for aspectj.

Another example uses aspectj but does not have much doc and also misses a command line build.

Question:

I have a Java EE application consisting of multiple OSGi bundles running within Apache Felix container. One of these bundles is responsible for loading Spring application context. I'm using Spring AOP in my application and the following error arised in my bundle:

java.lang.IllegalArgumentException: warning no match for this type name: com.somepackage.SomeClass [Xlint:invalidAbsoluteTypeName]
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:301)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:206)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:192)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:169)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:208)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:262)
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:294)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:118)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:88)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:69)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:330)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:293)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1573)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:540)

The cause of this problem is that class(com.somepackage.SomeClass) used in pointcat of my aspect was loaded by bundle ClassLoader but AspectJExpressionPointcut passes default ClassLoader to buildPointcutExpression() method:

private void checkReadyToMatch() {
    if (getExpression() == null) {
        throw new IllegalStateException("Must set property 'expression' before attempting to match");
    }
    if (this.pointcutExpression == null) {
        this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
                ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
                ClassUtils.getDefaultClassLoader());
        this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
    }
}

which knows nothing about this class (com.somepackage.SomeClass).


Answer:

The problem was solved by changing ClassLoader in thread initializing Spring Application Context:

Thread.currentThread().setContextClassLoader(bundleClassLoader);

So method ClassUtils.getDefaultClassLoader() returns bundleClassLoader which can load class mentioned in pointcut.

Question:

I have the following method:

@OnEvent
public void onEvent(Event event) {
}

Now, I am puzzled whether AspectJ can intercept the method "declaration", i.e. neither its call nor its execution, in order to provide functionality somewhere else. The objective is to "take" that method and register it as a handler of events into the OSGi service registry:

    public void start(BundleContext bc) {
    bc.registerService(EventHandler.class, new EventHandler() {
        @Override
        public void handleEvent(Event event) {
            // TODO get a "reference" of the onEvent(...) method and call it here
            onEventJoinPoint.proceed(event);
        }
    }, null);
}

Answer:

No, this is conceptually impossible in AspectJ and probably also in any other AOP framework. But you have several alternatives:

  • How about putting marker annotations in all places where the aspect should kick in? There you could parametrise the annotation so as to mention which method to call.
  • Or if you really want to have it all in one place and avoid config files, use a configuration class similar to Spring. The class or one of its methods which you call when booting up your component, would carry all config annotations which would thus be interceptable by AspectJ.
  • You could also keep the annotation where it is and call the annotated method once after loading the class, intercept the method execution and take note of the method from an aspect, then re-use this information later.

There are other, similar options. But in each case, please note that

  • class loading / initialisation order is important,
  • if you just have a method and class name, you will need reflection in order to call it, unless the method is either static or the class is a singleton and the method does not rely on any object state, i.e. an instance can easily be obtained and the method be called as often as needed. Then you could maybe even tweak the solution into calling proceed() multiple times.

If you have follow-up questions which are too complex for simple comments, please update the main question and notify me. I cannot be any more concrete now because I do not know enough about your use case. Thus, I am not providing any sample code (yet).