« July 2004 | Main | September 2004 »
August 27, 2004
When is a POJO not a POJO? .... when it's an APOJO.
I've been thinking a lot about Java 5 and how we should extend AspectJ to support it. (More on that shortly - there's a whole book's worth of material probably!). One of the most interesting and topical questions is how AspectJ should work with annotations. Yes, of course we'll extend the pointcut language to match based on annotation provided metadata at join points, but we're also thinking about doing something else too.
It all comes down to this: "When is a POJO not a POJO?" A. "When it's an APOJO". (That's an annotated POJO). To be more accurate, I think some annotations are fine. If you're writing say a retail application, and your POJO uses some retail annotations from your business domain, then that's still a POJO in my book. But if your retail application uses annotations specific to say the MegaServ application server, then that's not a POJO anymore (even if those annotations would be 'harmless' outside of the MegaServ environment). For one thing, it won't compile without the MegaServ libraries around. The compiler is a pretty good test of coupling.
So want if you want to write a real POJO, and not have it tied to any one environment - maybe you need to target multiple environments from the same common components, or maybe you just prefer the modularity that offers, then AspectJ will support declare annotation. This will let you write AF-POJOs (annotation-free POJOs) and then customize them for a given environment using aspects. For example:
aspect MegaServing { declare annotation : org.xyz.myapp..* : @MegaServed(published="true"); declare annotation : org.xyz.myapp.PaymentProcessing.*(..) : @MegaSecure(role="procurement"); }
and so on.
Or how about this trivial example for when your company mandates it:
aspect Notices { declare annotation : org.xyz.myapp..* : @Copyright("(C) XYZ 2004. All Rights Reserved."); }
...cross-cutting concerns come in all shapes and sizes!
The specification for declare annotation is by no means done yet, and the syntax given is for illustrative purposes only, so if you have feedback on this language feature then let me know. But rest assured - when annotation hell arrives (and it will), AspectJ will be able to offer you a little comfort at least.
Posted by adrian at 10:50 AM [permalink] | Comments (2)
August 25, 2004
Demos, presentations, testimonials,...
We had some IBM execs doing the rounds today and visiting us in Hursley. One of the things we got to talk about was aspects (what else? ;). In preparation for the session, we'd previously sent emails out to a number of our internal users asking them what they're doing with AspectJ, getting them to quantify the benefits where possible, and talk about their future plans. Regrettably, I can't share with you the details of those projects - but I can say that I'm really bowled over with the progress that project teams are making. We're seeing real benefits in improving the modularity of real programs, and many of those internal users are just getting on with it without needing any great hand-holding from our team either. Very encouraging.
We've also been putting together some Flash-based streaming demos of AJDT in action. I recorded them at home, and then the media team converted my avi files. I got the results back yesterday and I'm really pleased - they've done a great job and the demos show off the tools to good effect. Of course, you have the disadvantage of listening to my voice whilst the demos are running. but then you can't have everything ;). I hope to get these put up somewhere public shortly, and as soon as I do I'll post a link.
However, if you can't wait that long to see me make a fool of myself, then I've good news for you: the streaming versions of the presentations I gave to the Belgium JUG have just been uploaded to their wiki - and the team have done a super job capturing my somewhat crazed presentation. I'd forgotten just how much fun that event was until I watched the video again tonight. You can see the talks at the BeJUG Wiki site (and Rod Johnson's AOP in Spring talk too) at http://www.bejug.org/confluenceBeJUG/display/Events/Home. Enjoy!
Posted by adrian at 09:11 PM [permalink] | Comments (4)
August 23, 2004
I don't want to know that... (writing robust pointcut expressions)
The topic of this post is writing robust pointcut expressions, by which I mean pointcuts that stand the maximum chance of continuing to match the intended join points, and only the intended join points, as a program evolves.
The first rule of thumb, is simply "don't tell me anything I don't need to know." For example, in a simple banking application we could write:
pointcut accountOperation() : execution(public * *(..)) && this(Account);
or we could write:
pointcut accountOperation() : execution(public Money Account.withdraw(Money)) || execution(public Money Account.getBalance()) || execution(public void Account.deposit(Money)) || execution(public Money SavingsAccount.getInterest()) || ... ;
Assuming we intend to match the execution of any public method on an Account object, I hope it's obvious why the first pointcut is much better than the second - even though they might match exactly the same set of join points when first written. The second pointcut is much more tightly coupled to implementation details of the Account hierarchy that may change. The issue crops up more subtley though in many more cases. For example, suppose you're only interested in the execution of the deposit and withdraw methods. Is it reasonable to write:
?poincut accountTransaction() : (execution(public Money withdraw(Money)) || execution(public void deposit(Money)) ) && this(Account);
On first glance this looks ok, but let's push a little....
- If the withdraw method was changed to take a second parameter, would we still want the pointcut to match?
- If deposit was to return a value, would we still want the pointcut to match?
- If either method had its visibility reduced, should the pointcut match?
Suppose we answer yes, yes and no. A better pointcut specification would therefore be:
pointcut accountTransaction() : ((execution(public * withdraw(..)) || execution(public * deposit(..)) ) && this(Account);
This pointcut is less tightly coupled to the details of the Account class, and will continue to match even if the arguments or return types are changed for the two methods.
For each piece of information you provide in a pointcut expression, ask yourself whether you would still want the pointcut to match at a join point where the value was slightly different. If you do, loosen (or remove) the condition. By doing this, you reduce coupling between the aspect and the rest of the application, and you have a more robust pointcut specification that will continue to match as you intended as the program evolves.
What about the possibility of other methods being introduced into the Account hierarchy that support other types of deposit and withdrawals - for example, depositWithReceipt(....) or withdrawUpTo(....). Should these hypothetical methods be matched too? I don't know - you decide. But if you decide they should match, how should you change the pointcut expression?
So if the first rule is "don't tell me anything I don't need to know," the second rule is "tell me everything that matters to you."
The goal here is to avoid unintended join point matches as the program evolves. Here's a classic example:
after() throwing(Exception ex): execution(public * *(..)) { // log exception... ... // and re-throw throw (ex); }
Do you really mean this advice to be in effect for the throwing of exceptions after the execution of any public method *anywhere*? Even if this is correct your application today, what if this aspect is included in a build with a wider set of types exposed to the weaver - do you want to match all of those too? Are you sure? Typically you might want to use a scoping pointcut here. I like to define a class SystemArchitecture
in my applications that contains handy pointcut definitions for the application, the components in the application, and so on. Using something like that, a better advice specification might be:
after() throwing(Exception ex): execution(public * *(..)) && SystemArchitecture.inMyApplication() { // log exception... ... // and re-throw throw (ex); }
(or for extra points, do away with the anonymous pointcut expression altogether and give it a name).
Here's a more subtle example, from an application tracking the 'dirty' state of objects:
pointcut dirtyingAction(DAO dao) : set(* *) && target(DAO);
On first glance this looks pretty good, we haven't tied to any details about the names and types of fields - but there's one piece of information we do care about that we haven't specified, and that will result in possible unwanted join point matches in the future. Can you spot what it is?
Here's an improved version:
pointcut dirtyingAction(DAO dao) : set(!transient * *) && target(DAO);
presumably we don't want to treat a persistent object as dirty if a transient field is updated...
To complete a robust pointcut definition, you must ask yourself "are there any join points that I can envisage now or in the future, that would be matched by this pointcut expression, and that I don't want to match?" If the answer is yes, you need to narrow the pointcut expression to be more precise so as to avoid unwanted join point matches during program evolution.
That's really all there is to it. To write a robust pointcut expression make sure that you :
- Don't tell me anything I don't need to know.
- Tell me everything that matters to you.
Posted by adrian at 05:48 PM [permalink] | Comments (4)
August 17, 2004
Good things happening to AJDT....
Hot on the heels of AJDT 1.1.11, AJDT 1.1.12 was released last week. It's more than just a bug fix release with some good new features, including at long last support for showing the crosscutting relationships from aspects in binary libraries. Check out the AJDT 1.1.12 New-and-noteworthy document at the AJDT site. Congratulations to Matt Chapman who won his commit rights on the AJDT project earlier this month and oversaw the development of this AJDT release - freeing up more time for me to spend on AspectJ itself.
Speaking of AspectJ, it's coming on at a pace. Three weeks ago I posted to the dev list that Andy (Clement) and I were about to put a focused effort onto AspectJ development. I just asked bugzilla - turns out we've resolved 78 bugs and enhancement requests in that time, and also upgraded the JDT compiler level, made major improvements to the structure model used by the IDE tools, and revamped portions of our testing strategy. You can track our progress towards AspectJ 1.2.1 by looking at AspectJ bugs on Bugzilla with a 'target milestone' field value of "1.2.1". It's great to be back doing core development work again - long may it last.
On a final note - I spent a day last week chasing down as many references as I could to papers giving either qualitative or quantitative assessments of the benefits of using AspectJ (vs. OOP) on development projects (these were academic papers). There's a steadily growing body of work in this area and the results unanimously point to the fact that AspectJ improves modularity and separation of concerns. I'll post a set of links / references for these papers shortly so that those of you who are so inclined can read them for yourselves and draw your own conclusions.
Posted by adrian at 08:45 PM [permalink] | Comments (0)
August 06, 2004
Your Money or Your Life?
I've been doing an overhaul of the AspectJ test suite this week as part of a sustained round of bug-fixing and enhancements being made by Andy Clement and myself. The AspectJ test suite is an incredibly valuable asset - without it, it would be nigh on impossible to put out a new release of AspectJ without inadvertently breaking some AspectJ programs out there in the wild. So valuable in fact, that it got me to thinking: if I had to choose between keeping the source code for the AspectJ test suite, or for the AspectJ compiler implementation - which one would I keep?
I've been going round and round on this question (obviously, both are vitally important, but still - which one?). If I keep the implementation, I could use a copy as a reference for any question I want to ask it - but finding out all the right questions to ask would be tough. If I keep the test suite, I'll have to rebuild a new implementation - but at least I'll know when I've got it right.
Believe it or not, my current leaning would be towards keeping the test suite. But give me five minutes, and maybe I'll change my mind again...
What would you choose for your project?
Posted by adrian at 01:03 PM [permalink] | Comments (1)