In the FSE/NI paper we had the following example
jpi void buying(Item item, float price, int amount, Customer cus) extends checkingOut; aspect Discount { void around checkingOut(Item item, float price, int amt, Customer cus) { int factor = cus.hasBirthday()? 0.95 : 1; proceed(item, price*factor, amt, cus); } void around buying(Item item, float price, int amt, Customer cus) { int factor = (amt > 10) ? 0.85 : 1; nextadvice(item, price*factor, amt, cus); } }
Here, nextadvice
calls the checkingOut
advice.
We observed the problem that this is assymetric: this way, one can apply the buying
advice before the checkingOut
advice but not the other way around.
Eric B. here proposes an alternative semantics, which is closer to AspectJ and executes pieces of advice in order of declaration. Consider this slightly modified code:
aspect Discount { void around checkingOut(Item item, float price, int amt, Customer cus) { int factor = cus.hasBirthday()? 0.95 : 1; proceed(item, price*factor, amt, cus); //(1) } void around buying(Item item, float price, int amt, Customer cus) { int factor = (amt > 10) ? 0.85 : 1; proceed(item, price*factor, amt, cus); //(2) } }
Here, we call proceed
in both pieces of advice. nextadvice
does not exist in this semantics. The proposed semantics of this code is as follows. First of all, we execute all pieces of advice in the order in which they are declared. Hence, if we have a checkingOut
joinpoint, then the checkingOut
advice will execute first.
At the position marked (1)
, the semantics of proceed
is such that we continue with the next declared matching piece of advice, i.e., in this case buying
. The proceed
marked (2)
proceeds to the original joinpoint since no further matching advice follows.
Now consider the case in which we dispatch a buying
joinpoint. In this case we only execute the second piece of advice.
Note that this design allows us to flexibly change the ordering in which the pieces of advice will apply. The following declaration would induce the inverse dispatch ordering:
aspect Discount { void around buying(Item item, float price, int amt, Customer cus) { int factor = (amt > 10) ? 0.85 : 1; proceed(item, price*factor, amt, cus); } void around checkingOut(Item item, float price, int amt, Customer cus) { int factor = cus.hasBirthday()? 0.95 : 1; proceed(item, price*factor, amt, cus); } }
Here, on a checkingOut
joinpoint we would apply the buying
advice first, then followed by the checkingOut
advice, then followed by the original joinpoint.
Dispatch sequence across aspects
The above semantics based on ordering is problematic when multiple aspects advise the same joinpoint type hierarchies. Which advice should then execute first? In this case Eric B. proposes to resort to a declare precedence
statement as in AspectJ. In this case, “declare precedence” would probably bind stronger than the ordering within a single advice; in other words, we would first execute all matching pieces of advice from the aspect with highest priority and then all matching pieces of advice from the next aspect (in the order declared there) and so on. Does that makes sense? I wonder…