Community
Participate
Working Groups
Consider the following code snippet from an aspect, assuming there is a class Main with methods tigger() and foo() and that the foo() method can either be called directly or from within tigger(): pointcut T() : execution(* Main.tigger()); pointcut F() : execution(* Main.foo()) && !cflow(T()); Object around() : T() || F() { // do something Object z = proceed(); // do something else return z; } As you can see, there is an around advice which does something when either tigger() or foo() gets executed. So far so good, now assume the advice body would like to know if it is being executed on behalf of pointcut T or F? I could not think of any other way than creating two advices, one for T and one for F. Unfortunately the two advices cannot share the same body, so the programmer is forced into copy-paste-and-hack style. The two advices could of course call a method (which could have a parameter indicating where it is called from), however this does not go a long way because in a method you cannot call "proceed" or have access to thisJoinPoint reflections. To solve this I suggest allowing explicit formals binding in pointcuts, like this: pointcut T(boolean flag) : execution(* Main.tigger()) && bind(true); pointcut F(boolean flag) : execution(* Main.foo()) && !cflow(T()) && bind(false); Object around(boolean flag) : T(flag) || F(flag) { if (flag) // do something else // do something different Object z = proceed(); // do something else return z; } The whole process of binding resembles functional programming to me and allowing explicit binding seems to me as a logical step.
why not just use thisJoinPoint in the body of the advice? Since T and F match different join points, this would always enable you to distinguish. If T and F *could* both match at some join point, your proposal would break - what should the value of the flag be? Advice executes at join points, not at pointcuts, so the current thisJoinPoint abstraction is appropriate imho.
not sure why this was assigned to me - sending it back to Adrian...
Hello Adrian, thanks for your response. I even reassigned the bug to Julie in the false belief that the bug report went unnoticed, sorry for that extra bother. A friend of mine met you on the JAX in Frankfurt, so I see you must be busy these days. I understand it is possible to examine thisJoinPoint in the advice body to distinguish the cases in my example. However, it means the advice must know about join point signatures, which is IMHO something that should be captured in pointcuts only. I agree the binding would be impossible if T and F both matched the same join point, but that's not really anything new or bad - there already is an "Ambigous binding" error in AspectJ. I admit my example was perhaps too abstract and not very convincing. Let me give you a real code example that I wrote just today. We are using AspectJ to do performance monitoring using Apache's StopWatch class. In some classes, I introduce the StopWatch object to the class using inter-type declaration (more precisely, I declare the class to extend a base class which has the StopWatch as a member). In other classes, I just create a new StopWatch object for every method invocation. This leads to a code with two very similar advices: Object around() : measuredMethod() { StopWatch w = new StopWatch(); try { w.start(); return proceed(); } finally { w.stop(); // output w here } } Object around(StopWatchOwner owner) : measuredMethodWithOwnWatch(owner){ try { owner.getWatch().start(); return proceed(owner); } finally { owner.getWatch().stop(); // output owner.getWatch() here } } As you see, the two advices are almost identical. A clear violation of the DRY principle, isn't it? Yet the two cannot be merged, for the sole reason that one pointcut has no arguments and the other one has one. What I would love to do is this: //redefine the pointcut that had originally no arguments pointcut measuredMethod(StopWatchOwner owner) : // whatever joinpoint definition here && bind(null); //and merge the two advices as: Object around(StopWatchOwner owner) : measuredMethod(owner) || measuredMethodWithOwnWatch(owner) { StopWatch w = (owner == null) ? new StopWatch() : owner.getWatch(); try { w.start(); return proceed(); } finally { w.stop(); // output w here } } What do you think?
This example makes a lot more sense, thanks. I don't think we'll have time to think through all of the potential issues in here in order to make the 1.5.0 release - and it's also something that no other user has asked for afaik. I'm going to leave this open as an enhancement request but with no target milestone assigned for the time being, that way we can come back and take another look once we've dealt with the 1.5.0 release.
We're not going to get to this in AJ 1.5.0. Marking as "LATER" for consideration in 1.5.1 and future release planning.
LATER/REMIND bugs are being automatically reopened as P5 because the LATER and REMIND resolutions are deprecated.
I second this (old) request. My situation is as follows: I have a logging aspect with several pointcuts and advice: aspect LoggingAspect { pointcut logMe1() : execution(...); pointcut logMe2() : execution(...); pointcut logMe3() : execution(...); Object around() : logMe1() { String logMessage = "log message #1"; logger.log("before " + logMessage); logger.indent(); Object result = proceed(); logger.dedent(); logger.log("after " + logMessage); } Object around() : logMe2() { String logMessage = "log message #2"; logger.log("before " + logMessage); logger.indent(); Object result = proceed(); logger.dedent(); logger.log("after " + logMessage); } Object around() : logMe3() { String logMessage = "log message #1"; logger.log("before " + logMessage); logger.indent(); Object result = proceed(); logger.dedent(); logger.log("after " + logMessage); } } Obviously there is code duplication just because the log message is different. (Actually it is more complicated because sometimes the message is not constant, but needs to be constructed from a "this" object's properties, but let's forget about that for now.) What I would like to have is this: aspect LoggingAspect { pointcut logMe1(String msg) : execution(...) && bind(msg="log message #1"); pointcut logMe2(String msg) : execution(...) && bind(msg="log message #2"); pointcut logMe3(String msg) : execution(...) && bind(msg="log message #3"); Object around(String msg) : logMe1(msg) || logMe2(msg) || logMe2(msg) { logger.log("before " + msg); logger.indent(); Object result = proceed(msg); // maybe even just proceed() // for bound pointcut parameters logger.dedent(); logger.log("after " + msg); } }