« March 2005 | Main | May 2005 »
April 26, 2005
Heated discussions on the benefits and drawbacks of AOP
Following on from the Burton report, Forrester have also issued an analyst report entitled "AOP considered harmful". I don't know who Forrester spoke to in preparing this report, but I know a number of influential people in the AOP space that they didn't talk to (including any member of the AspectJ team). However, that's not the point of this post. In essence the report says that AOP is like an unstructured GOTO and therefore must be bad. The analyst quotes from a position paper by Maximilian Stoerzer et. al. Max posted on aosd-discuss to say that the analyst hadn't contacted him either, and that "the paper was written for a panel discussion, and intended to be provocative".
What the report has provoked though, is some very spirited debate on aosd-discuss about the pro's and con's of AOP, where it helps modularity, where it may harm modularity, the role that tools have to play, and the need for guidelines on AOP usage. If you can keep an open mind, it's well worth taking a look at the thread "AOP considered harmful" in the aosd-discuss archives.
In addition there was an active slashdot post on the topic (332 comments at time of writing). Like many Slashdot threads, there's a lot of noise, but also some interesting posts amongst them. I picked out this quote from "CrankyFool" as one of my favourites:
AOP can be used for great evil, but it can also be used for great good. When I first read up on it, my first reaction was "wow! This must be how God programs!" :)
Posted by adrian at 08:26 AM [permalink] | Comments (0)
April 14, 2005
Burton analyst report on AOP now available
Richard Monson-Haefel has been working hard over the last few months on an analyst report for Burtons on the state of AOP. I know that he conducted extensive research on the topic, and interviewed a large number of the participants in the AOP space. The result is a very valuable report for corporations wondering what to make of AOP and how it might impact their development processes and strategies.
This report is now available from the Burton's site (note - there is a charge for this report which represents a significant investment of time and resource on the part of Burtons).
Here's the blurb from the Burton's site:
"Ghosts in the Machine: Aspect-Oriented Programming"
Aspect-oriented programming (AOP) is a powerful modularization tool that improves development and maintenance of object-oriented software. Recent advances in visual tooling alleviate previous barriers to adoption, especially lack of visibility. Burton Group Senior Analyst Richard Monson-Haefel explains what AOP is, where it’s going, and how organizations should implement a three-phase adoption process to leverage it.
You might also be interested in Richard's blog entry " Understand AOP in 3 easy steps".
On a related topic, I was interested to see in the news today the headline AOP to Sharpen its Cutting Edge ;)
Posted by adrian at 08:42 AM [permalink] | Comments (1)
April 12, 2005
Aspects as automaton
This post is inspired by discussions on the aspectj-users list over the last month discussing the best way to implement aspects that police rules based on sequences of join points in the execution of a program. Thanks go to Eric Bodden and others for provoking the discussion. What follows is my write-up of how I would tackle the problem using AspectJ 5.
We'll need a requirement to work from. Let's take good old foobar
. Everyone knows that
foo
comes before bar
. What if we wanted to write an aspect to make sure that was true?
More precisely, let's write an aspect to enforce the following rule:
Every call to bar() within an action must be preceded by at least one call to foo(). After any call to bar(), foo() must be called at least once before bar() can be called again.
Here's a sample program to illustrate the point
public class CommandProcessor { public static void main(String[] args) { CommandProcessor cp = new CommandProcessor(); cp.doAction1(); cp.doAction2(); cp.doAction3(); } public void doAction1() { foo(); bar(); // ok, preceded by foo() foo(); } public void doAction2() { foo(); foo(); bar(); // ok, preceded by foo(); bar(); // not ok, must call foo() at least once before every bar() } public void doAction3() { fooHelper(); barHelper(); barHelper(); // not ok, you can't get away with it even if the call to bar is indirect.... } private void fooHelper() { foo(); } private void barHelper() { bar(); } private void foo() {} private void bar() {} }
The following state transition diagram shows the possible states of a state machine to implement these checks. The BAR state colored in Red is the illegal state that we don't want to allow.
Some say there's no smoke without a fire. In our case there's...
public aspect NoBarWithoutFoo { .. }
Let's use an enum to capture the states in our transition diagram:
public aspect NoBarWithoutFoo { enum FooBarStateMachine { START,FOO,BAR,FOOBAR; } ... }
In fact, we can use the enum type to capture not just the states but the transitions too. We have two kinds of transition, foo(), and bar(). Here's the expanded implementation of the enum that fully encodes the STD.
public aspect NoBarWithoutFoo { enum FooBarStateMachine { // transition events public abstract FooBarStateMachine foo(); public abstract FooBarStateMachine bar(); // states START { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }, FOO { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return FOOBAR;} }, BAR { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }, FOOBAR { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }; } ... }
Anytime we transition into the BAR state, that's bad. We can write that rule straight into the aspect by adding the following declarations:
pointcut stateChange() : execution(FooBarStateMachine FooBarStateMachine.*(..)); after() returning(FooBarStateMachine newState) : stateChange() { if (newState == FooBarStateMachine.BAR) { // take appropriate action on entering an illegal state... System.err.println("Illegal state transition - bar without foo!"); } }
The final piece of the puzzle is to wire join points in the program execution into actions that fire the state transitions. The basic process is to write one pointcut for each transition event (foo() and bar() in this example, plus one pointcut to match join points that cause us to enter the START state).
/** * An action executed by the CommandProcessor */ pointcut doAction() : execution(* CommandProcessor.do*(..)); /** * Reset the state machine any time we enter a doXXX method in the CommandProcessor * unless we are calling the action as part of processing another action. */ pointcut enterStartState() : doAction() && !cflowbelow(doAction()); /** * Foo transition is any call to foo() made during action processing */ pointcut foo() : call(* CommandProcessor.foo(..)) && cflowbelow(doAction()); /** * Bar transition is any call to bar() made during action processing */ pointcut bar(): call(* CommandProcessor.bar(..)) && cflowbelow(doAction());
Almost there, we just need a copy of a state machine inside the aspect, and some advice to trigger the transitions on it.I'm
going to use a ThreadLocal
for the state machine. For this particular use case we could have used a regular variable declaration
and made the aspect percflow but the ThreadLocal approach is a more general solution for cases when the transition events you
are interested in are not necessarily bounded by a single control flow.
private ThreadLocal<FooBarStateMachine> stateMachine = new ThreadLocal<FooBarStateMachine>(); before() : enterStartState() { stateMachine.set(FooBarStateMachine.START); } before(): foo() { FooBarStateMachine newState = stateMachine.get().foo(); stateMachine.set(newState); } before() : bar() { FooBarStateMachine newState = stateMachine.get().bar(); stateMachine.set(newState); }
That completes the aspect. If we you compile and run this little example you'll see the output:
Illegal state transition - bar without foo! Illegal state transition - bar without foo!
The complete aspect implementation is below for reference. Note that whilst I've used this aspect-driven state machine approach for policy enforcement, it could well be a useful implementation technique for many state-transition based applications.
public aspect NoBarWithoutFoo { enum FooBarStateMachine { // transition events public abstract FooBarStateMachine foo(); public abstract FooBarStateMachine bar(); // states START { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }, FOO { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return FOOBAR;} }, BAR { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }, FOOBAR { public FooBarStateMachine foo() { return FOO; } public FooBarStateMachine bar() { return BAR; } }; } pointcut stateChange() : execution(FooBarStateMachine FooBarStateMachine.*(..)); after() returning(FooBarStateMachine newState) : stateChange() { if (newState == FooBarStateMachine.BAR) { // take appropriate action on entering an illegal state... System.err.println("Illegal state transition - bar without foo!"); } } private ThreadLocal<FooBarStateMachine> stateMachine = new ThreadLocal<FooBarStateMachine>(); /** * An action executed by the CommandProcessor */ pointcut doAction() : execution(* CommandProcessor.do*(..)); /** * Reset the state machine any time we enter a doXXX method in the CommandProcessor * unless we are calling the action as part of processing another action. */ pointcut enterStartState() : doAction() && !cflowbelow(doAction()); /** * Foo transition is any call to foo() made during action processing */ pointcut foo() : call(* CommandProcessor.foo(..)) && cflowbelow(doAction()); /** * Bar transition is any call to bar() made during action processing */ pointcut bar(): call(* CommandProcessor.bar(..)) && cflowbelow(doAction()); // state transitioning before() : enterStartState() { stateMachine.set(FooBarStateMachine.START); } before(): foo() { FooBarStateMachine newState = stateMachine.get().foo(); stateMachine.set(newState); } before() : bar() { FooBarStateMachine newState = stateMachine.get().bar(); stateMachine.set(newState); } }
Posted by adrian at 08:20 PM [permalink] | Comments (1)
April 09, 2005
Printing entries from this blog
A number of people have asked for a printable version of the articles posted on this blog. I just updated the site templates to add a separate style sheet for "print" media. So the site looks just the same as it always has on the screen, but if you print then the banner and left-hand margin are stripped off. Try using the "print preview" feature of your browser and you'll see what I mean.
Obviously this only works for CSS-aware browsers, but it's a heck of a lot easier than building separate version of every page specially for printing...
Posted by adrian at 08:47 AM [permalink] | Comments (0)
April 08, 2005
Making the code look like the design
I took some time out over Easter to recharge ahead of the big AspectJ push on AspectJ 5 M3 and generics. It gave me some "fiddling around" time to catch up on those little jobs you've been meaning to get to for ages. One such job was to make some improvements to the AspectJ website (we've very recently acquired the ability to run php scripts on the Eclipse servers). In particular, keeping the AspectJ home page current and interesting is always a challenge. Since I occassionaly write interesting things in this blog (I hope ;) ), and there are several other AOP related blogs around I thought it would be nice to change the home page to include some syndicated content - that way the page could update itself without me having to do anything ;).
That change per se isn't the subject of this entry though. While I was editing the pages, I couldn't resist cleaning up the markup. The original Eclipse AspectJ site was put together in late 2002, and has just had incremental updates since then. Whilst the new site looks pretty much the same, the markup is now completely different - and what struck me as I was doing the work is that I was applying two principles that we also talk about with respect to AOP: "making the code look like the design", and "separation of concerns". In website design terms, I'm playing catch-up with the rest of the world here, but from an AOP perspective it's interesting to see how the principles we work too can help with more software development tasks than just writing programs.
Take a look at the current (at time of writing) AspectJ home page and then at the new AspectJ home page. If we concentrate on the main body of the pages, we can see that both pages have the same logical structure and the same content. What's the design of the home page? You can see that the page is broken down into sections: there's a main heading "aspectj project" under which come several sub-headings ("News and Events", "Recent Books and Articles", and "Links"). In each of these sub-sections you find a list of entries.
So both pages look the same and have the same design for the main body of the page. But as I mentioned previously, they have a very different implementation. In the case of the old home page, the (html) code does not look at all like the design. The effect is created with table markup, as was pretty common in late 2002 (and probably still is today...). Here's an extract:
... <tr> <td ALIGN=LEFT VALIGN=TOP BGCOLOR="#0080C0"><b><font color="#FFFFFF" face="Arial,Helvetica"> News and Events</font></b></td> </tr> <tr> <td> <table cellSpacing="5" cellPadding="2" width="100%" border="0"> <tr> <td vAlign="top" align="left" height="12" width="23"> <img src="http://eclipse.org/images/Adarrow.gif" border="0" width="16" height="16"> </td> <td colspan="2" valign="top"> <b>April 7th, 2005 - AspectJ 5 M2 (milestone 2) build now available. </b> The second milestone build of AspectJ 5 is now available, with full support for compiling Java 5 language features and annotation matching and binding. See the <a href="doc/README-150.html">README</a> for more details, or go straight to the <<a href="doc/ajdk15notebook/index.html">AspectJ 5 Developer's Notebook</a> for a detailed description of the new language features. </td> </tr> <tr> <<td vAlign="top" align="left" height="23" width="23"> <img src="http://eclipse.org/images/Adarrow.gif" border="0" width="16" height="16"> </td> <td colspan="2" height="23"> <b>March 14-18, 2005 </b>- <b><a target="_top" href="http://aosd.net/2005/index.php">AOSD.05</a></b>, Chicago, IL <br/> The International Conference on Aspect-Oriented Software Development with Gregor Kiczales, Cristina Videira Lopes, Mik Kersten, Adrian Colyer, Andrew Clement, Julie Waterhouse, Jonas Boner, Alexandre Vasseur, Ron Bodkin, Nicholas Lesiecki, Ramnivas Laddad, Grady Booch, et al. </td> </tr> ...
Note the use of nested tables, images embedded in (every) table row, colspans, bold markup etc. - all part of the code used to implement the design. The end result is pretty ugly. Fast forward to the new page markup. Here's the same section of home page again:
... <h2>News and Events</h2> <dl> <dt> April 7, 2005 <a href="downloads.php">AspectJ 5 M2</a> build now available</dt> <dd> The second milestone build of AspectJ 5 is now available, with full support for compiling Java 5 language features and annotation matching and binding. See the <a href="doc/README-150.html">README</a> for more details, or go straight to the <a href="doc/ajdk15notebook/index.html">AspectJ 5 Developer's Notebook</a> for a detailed description of the new language features. </dd> <dt> March 14-18, 2005 - <a target="_top" href="http://aosd.net/2005/index.php">AOSD.05</a>, Chicago, IL </dt> <dd> The International Conference on Aspect-Oriented Software Development with Gregor Kiczales, Cristina Videira Lopes, Mik Kersten, Adrian Colyer, Andrew Clement, Julie Waterhouse, Jonas Boner, Alexandre Vasseur, Ron Bodkin, Nicholas Lesiecki, Ramnivas Laddad, Grady Booch, et al. </dd> ...
This code, I argue, looks just like the design - there's a heading, followed by a list. All of the implementation noise has been removed. Dan Cederholm (author of " Web Standards Solutions") calls this a semantic markup because the "code" is describing what things are (their design), as opposed to how they should be presented. As well as being much nicer to work with, this markup has a lot of other benefits too in terms of accessibility, maintenance, support for a wide variety of devices, and so on.
The reason that I was able to make the code look like the design was by applying the principle of separation of concerns. Without a technology that helps you to do that, it's hard to get the code clean. In the case of AOP, that technology is AspectJ. In the case of web site design, that technology is CSS. Remember that both pages look the same when viewed in a standard browser. The reason I can do that in the new site is that CSS allows me to separate the presentation concern from the content (a phrase involving grandmothers, eggs, and sucking is coming to mind here for many of my readers - forgive me whilst I make a point...). Here's an extract from the style sheet that says h2 headings should have white text and a blue background bar, and list items should have the little arrow image instead of a bullet:
h2 { font-family: arial, helvetica, geneva; font-size: 12pt; font-weight: bold ; line-height: 14px; color: #FFFFFF; background-color: #0080C0; padding: 5px; } dt { background: url(images/Adarrow.gif) no-repeat 0 50%; font-family: arial, helvetica, geneva; font-size: 10pt; font-weight: bold; padding-left: 22px; padding-top: 6px; padding-bottom: 6px; } dd { line-height: 1.2em; } ...
Note also that just as with separation of concerns in AspectJ programs we get other benefits from this separation too - notably the option to "compose" the page with a variety of different style sheets: one formatting the page for printing for example. What we make modular, we can also more easily replace, extend, and reuse.
The moral of the story of course is that by understanding underlying principles we can apply them to all of our work as designers and implementors. The same principles that provide the motivation for languages like AspectJ are applicable in a wide range of situations - including I'm sure a good number that we've yet to encounter.
Posted by adrian at 12:39 PM [permalink] | Comments (0)
April 07, 2005
AspectJ 5 M2 released today
We released AspectJ 5 M2 today ( download)
The changes in AspectJ 5 M2 since M1 are numerous, and the highlights are listed below.
- Full source compilation of Java 5 programs (with the "-1.5" option)
- New Xlint warning when advice does not affect any join points (and the @SuppressAjWarnings annotation to suppress it) (-1.5 only)
- @this,@target,@args forms changed from @this(@Foo) to @this(Foo)
- Full support for annotation binding as context in @this, @target, @args, @annotation, @within, @withincode
- Declare annotation (declare @field, declare @method, declare @constructor, declare @type)
- Declare soft does not soften runtime exceptions
- pertypewithin instantiation model
- performance improvements resulting in reduced compilation times
- aspectpath has been extended to accomodate directories as well as jar/zip files
- many, many bug fixes
The AspectJ 5 Developer's Notebook has been updated to reflect the updates. So many people have contributed ideas, suggestions, and bug reports to M2 that it's impossible to list you all - but you know who you are, so thank you. Come 1.5.0 final we'll put together a roll-of-honor :)
For examples of some of the new features in action check out some of the entries in this blog:
and also the AspectJ 5 Developer's Notebook.
Posted by adrian at 08:43 PM [permalink] | Comments (0)