Community
Participate
Working Groups
The following program shows the advice that fires after constructing a request context is running TWICE after a single matching join point, always from the same line. Notice also that only one instance of the request context class is ever constructed. However, if you comment out the second concrete subaspect that matches, then it correctly runs once. The second subaspect is never actually executed in this program. It must have something to do with handling non-static inner classes in concrete subaspects. This bug is affecting the code for my AOP@Work article and (when you see the article), you'll see there is an important use case... (whew) Program: public abstract aspect WorkerExample { after() returning (RequestContext newContext) : call(RequestContext+.new (..)) { System.out.println("constructing "+newContext+" at "+thisJoinPoint.toLongString()+" from "+thisEnclosingJoinPointStaticPart+":"); } public abstract class RequestContext { public RequestContext() { System.out.println("new rc"); } public final Object execute() { return doExecute(); } /** template method */ public abstract Object doExecute(); } public static void main(String args[]) { new Runnable() { public void run() {} }.run(); }; } aspect ConcreteAlpha extends WorkerExample { Object around(final Object runnable) : execution(void Runnable.run()) && this(runnable) { System.out.println("monitoring operation: "+runnable+" at "+thisJoinPoint+", for "+thisJoinPoint.getThis()); RequestContext requestContext = new RequestContext() { public Object doExecute() { return proceed(runnable); } }; return requestContext.execute(); } } aspect ConcreteBeta extends WorkerExample { Object around() : call(void awqeyuwqer()) { RequestContext requestContext = new RequestContext() { public Object doExecute() { return proceed(); } }; return requestContext.execute(); } } Actual output: monitoring operation: WorkerExample$1@1b54de at execution(void WorkerExample.1.run()), for WorkerExample$1@1b54de new rc constructing ConcreteAlpha$1@17963c at call(ConcreteAlpha.1(ConcreteAlpha, WorkerExample, java.lang.Object, org.aspectj.runtime.internal.AroundClosure)) from execution(ADVICE: Object ConcreteAlpha.ajc$around$ConcreteAlpha$1$1a7ddbed (Object, AroundClosure, JoinPoint)): constructing ConcreteAlpha$1@17963c at call(ConcreteAlpha.1(ConcreteAlpha, WorkerExample, java.lang.Object, org.aspectj.runtime.internal.AroundClosure)) from execution(ADVICE: Object ConcreteAlpha.ajc$around$ConcreteAlpha$1$1a7ddbed (Object, AroundClosure, JoinPoint)): Expected output (achieved with ConcreteBeta not included): monitoring operation: WorkerExample$1@1b54de at execution(void WorkerExample.1.run()), for WorkerExample$1@1b54de new rc constructing ConcreteAlpha$1@17953c at call(ConcreteAlpha.1(ConcreteAlpha, WorkerExample, java.lang.Object, org.aspectj.runtime.internal.AroundClosure)) from execution(ADVICE: Object ConcreteAlpha.ajc$around$ConcreteAlpha$1$1a7ddbed (Object, AroundClosure, JoinPoint)):
for investigation in M4
Created attachment 27502 [details] testcase patch The attached patch is to be applied to the tests project and reproduces the failure with a simplified testcase.
concrete advice in abstract aspects is always run once for each concrete sub-aspect - this has been the way in AspectJ for ever AFAIR. The program below is working as designed. Concrete advice in abstract aspects should be used with care!
Of course, you are right. What I need to do here is wrap the advice in a static inner concrete aspect, so it will run just once!