Earlier this year the Java blog world got very excited about Aspect Oriented Programming (AOP) and Java based frameworks
to enable it. At the time the only AOF mature enough to be used was AspectJ, but now there are at least three other frameworks
around designed to make AOP doable. I'm interested in this because when the idea first came up I implemented and threw-away
my own framework, because I wanted to see what others were doing.
With the recent release of the JBoss 4 Devlopers Release, I decided it was time to have a play. This is my initial
impressions after attempting to do the same thing in each framework. I didn't compare AspectJ, although I may at a later date.
The three frameworks I tried were AspectWerkz, JBoss 4 AOP and Nanning. I attempted to implement a program that would
use a Mixin to output "Hello World" and would trace calls to the helloWorld method on the Mixin. While I
agree this is a pretty basic program, I hoped it would let me understand how each framework implemented
each of the four main principles of AOP:
Interception (Advice): The tracing is done using an Interceptor (aka Advice).
Introduction: The output is done using an Introduction (aka Mixin).
Inspection: The interceptor works out what method is being called. It doesn't use any meta data at this stage, though.
Modularization: The "object" that is run is composed of a number of "things" - the Mixin and the Interceptor, each of which is a standalone module.
Overview
Probably the most surprising thing to me is how similar all three systems are.
All can use XML descriptors to setup the Aspects, and the syntax was quite similar, too.
All had the same requirements to use a Mixin - it should be a Plain Old Java Object (POJO) that implements an interface.
Infact, all frameworks let me use exactly the same code for the Mixins:
IHelloWorld.java
package example;
public interface IHelloWorld {
public void helloWorld();
}
HelloWorldImpl.java
package example;
public class HelloWorldImpl implements IHelloWorld {
public void helloWorld() {
System.out.println( "Hello World!");
}
}
Both AspectWerkz and JBoss AOP had "launcher frameworks" that allow use from the command line. AspectWerkz
provided a batch file (and shell script) that would setup the classpath and run a program through AspectWerkz.
Unlike Nanning and JBoss AOP, it requires a separate "weave" step after compilation if Mixins are used, and this
was also done from the command line:
src>java org.codehaus.aspectwerkz.metadata.SourceFileMetaDataCompiler /
aspectwerks.xml . C:\java\aof\mycode\AspectWerkz\meta
compiling weave model...
weave model for classes in . have been compiled to C:\java\aof\mycode\AspectWerkz\meta
bin>aspectwerkz -Daspectwerkz.metadata.dir=C:\java\aof\mycode\AspectWerkz\meta /
example.Pojo
JBoss AOP requires its dependancies to be added to the classpath manually, and then to set the system ClassLoader
when a Java program is run:
For both these systems, I was able to use the same base object (which I added my Mixin to):
package example;
public class Pojo {
public static void main(String[] args) {
Pojo pojo = new Pojo();
((IHelloWorld)pojo).helloWorld();
}
}
Nanning required setup code to be run in order to setup the Aspect System. This was fairly trivial, but made the
code not quite as neat as the other two systems. I also created an IPojo interface for the Pojo object to implement.
(This wasn't strictly required, but if I didn't make it do that the code looks like I'm just creating an
implementation of the IHelloWorld interface, not another object with a IHelloWorld mixin).
package example;
import java.io.IOException;
import com.tirsen.nanning.AspectInstance;
import com.tirsen.nanning.Aspects;
import com.tirsen.nanning.config.AspectSystem;
import com.tirsen.nanning.xml.AspectSystemParser;
public class Pojo implements IPojo {
public static void main(String[] args) throws IOException {
// initialize Nanning
AspectSystemParser aspectSystemParser = new AspectSystemParser();
// load my config file
AspectSystem aspectSystem = aspectSystemParser.parse(
Pojo.class.getResource("nanning.xml"));
// create the Aspect
IPojo o = (IPojo) aspectSystem.newInstance(IPojo.class);
AspectInstance instance = Aspects.getAspectInstance(o);
// get the IHelloWorld interface from the aspect
IHelloWorld helloWorld = (IHelloWorld) instance.getProxy();
// call the method
helloWorld.helloWorld();
}
}
Configuration
All three frameworks can use an XML file for configuration. For JBoss AOP it is almost a requirement - there is very
little documentation for not using one (although it is possible, as JBoss 4.0 uses JMX for configuration). AspectWerkz may be
configured in code or via the XML file. Nanning can use an XML file for configuration, but most of the
documentation is for configuring it in Java.
Of the three, I found JBoss AOP the easiest to get to work. AspectWerkz confused me with the difference between
advices-def and advice-def (I only just worked this out), and while Nanning had a clean XML syntax, it took some time to
work out how much I could do in XML and how much I needed to do in code.
If you look closely at the configuration files you will notice that there are differences in functionality between
these configurations - in particular, the Pointcut the interceptors are applied on is specified quite differently.
AspeckWerkz uses a simple regular expression like syntax for matching classes and methods, JBoss uses JDK 1.4 RegExp
to match classes (I am unsure how to filter based on method names?). I couldn't work out exactly how rich Nanning's
pointcut specifications are. JBoss can intecept on Constructors, Methods, and Field access, AspectWerkz on Method and Fields
(I am unclear about constructors) while Nanning intercepts on Method invocation (again, I am unclear about constructors).
The output of the tracing interceptor was fairly similar for each framework.
AspectWerkz:
Entering method: public void example.Pojo.___originalMethod$helloWorld$1()
Entering method: public void example.HelloWorldImpl.___originalMethod$helloWorld$1()
Hello World!
Leaving method: public void example.HelloWorldImpl.___originalMethod$helloWorld$1()
Leaving method: public void example.Pojo.___originalMethod$helloWorld$1()
JBoss AOP:
Entering public void example.Pojo.helloWorld()
Hello World!
Leaving public void example.Pojo.helloWorld()
Nanning:
Entering method: public abstract void example.IHelloWorld.helloWorld()
Hello World!
Leaving method: public abstract void example.IHelloWorld.helloWorld()
Notice the different stack traces on the tracing output? This provides a pointer to how each
system is implemented. AspectWerkz and JBoss AOP use ByteCode manipulation, while Nanning uses
dynamic proxies.
Byte code modification at runtime using BCEL. A post compilation "Weave" step is required in some
configurations.
Build:
Both Ant and Maven build files are included. The Ant build failed because it was
set to use jikes as the compiler. Once I removed that, it compiled. The Maven build
was successful, but maven all copied all docs to c:\tools\Apache2\htdocs after
creating that directory.