A little while ago I released JBoss AOP 1.5.2 which is a
maintenance release - the main aim of this was to unify the integration of
JBoss AOP 1.5.x and JBoss AOP 2.x with JBoss 4. JBoss AOP 1.5.2 will be
included in the upcoming JBoss 4.0.5 release, and is the current production
release.
More interestingly JBoss AOP 2.0.0.alpha1 was released last
week. It contains some interesting new features, many of which are enabled by
our brand new weaving model. More features are scheduled for alpha2. These releases can be downloaded from the main JBoss AOP download page,
please try the 2.0.0 alpha and give feedback via our user
forum. Special thanks to Ståle W Pedersen for all his help with tasks that
have gone into this release.
I'll now talk a little about the most interesting new
features in JBoss AOP 2.0.0 alpha1.
Improved Instance API
The JBoss AOP model is completely dynamic. As long as your
classes are "prepared" (i.e. have the hooks for AOP woven into them), you can
add new aspects, bindings, metadata, annotation overrides etc. on the fly. This
is done on the AspectManager singleton, which is the central storage for all
this information. Whether you deploy your AOP using a jboss-aop.xml file or
annotation, the AspectXmlLoader or AspectAnnotationLoader will translate the
configuration data into calls to add the bindings, aspect definitions etc to
the AspectManager singleton. All instances of woven classes which match some of
these bindings then have their advice chains etc. updated.
JBoss AOP 1.x has a coarse-grained per
instance API, which allows you to add interceptors (An interceptor
is a special kind of aspect) either before or after the interceptor chain
created by the data from the AspectManager, but that is more or less what you
can do. This is what the Pojo Cache
does when you add an object instance to the cache; for all fields that are
prepared, it adds some interceptors to that instance to access the data from
the cache instead of from the fields, effectively giving us a POJO with
clustered state.
In JBoss AOP 2.0, each aspectized class has its own Domain.
A domain is a sub-AspectManager. What is deployed in the main AspectManager is
visible to the class's domain, but not vice versa. Furthermore each advised
instance has its own Domain again which is a child of the class's domain. The
Domain class is a sub-class of the AspectManager, meaning you can add ANYTHING
supported by JBoss AOP to it, you are not limited to just interceptors. An
example probably illustrates this better.
<!-- Weave in the hooks into our POJO class and add the interceptors -->
<aop>
<aspect class="MyAspect"/>
<prepare expr="all(POJO)"/>
</aop>
POJO pojo1 = new POJO();
POJO pojo2 = new POJO();
pojo1.someMethod();
At this stage, our POJO has the hooks woven in for AOP, but now bindings are deployed, so our call to POJO.someMethod() is not
intercepted. Next let us add a binding to POJO's class domain.
//All woven classes implement the Advised interface
Advised classAdvisor = ((Advised)pojo1);
//Get the domain used by all instances of POJO
AspectManager pojoDomain = classAdvisor._getAdvisor().getManager();
/*
* Add a binding with an aspect for that class this is similar to
*
*
*
*/
AdviceBinding binding1 = new AdviceBinding("execution(* POJO->someMethod*(..))", null);
AspectDefinition myAspect = AspectManager.instance().getAspectDefinition("MyAspect");
binding1.addInterceptorFactory(new AdviceFactory(myAspect, "intercept"));
//Add the binding to POJO's domain
pojoDomain.addBinding(binding1);
pojo1.someMethod();
pojo2.someMethod();
Now we have added a binding to POJO's class Domain. Both calls to someMethod() get intercepted by MyAspect
//Create an annotation introduction
AnnotationIntroduction intro = AnnotationIntroduction.createMethodAnnotationIntroduction(
"* POJO->someMethod()",
"@MyAnnotation",
true);
//Create another binding
AdviceBinding binding2 = new AdviceBinding("execution(* POJO->@MyAnnotation)", null);
binding2.addInterceptor(MyInterceptor.class);
//All woven instances have an instance advisor
InstanceAdvisor instanceAdvisor1 = ((Advised)pojo1)._getInstanceAdvisor();
//The instance advisor has its own domain
Domain pojo1Domain = instanceAdvisor1.getDomain();
//Add the annotation override and binding to the domain
pojo1Domain.addAnnotationOverride(intro);
pojo1Domain.addBinding(binding2);
pojo1.someMethod();
pojo2.someMethod();
We have added an annotation override and a new binding matching on that annotaton to pojo1's domain, so when calling
pojo1.someMethod() this gets intercecpted by MyAspect AND MyInterceptor. pojo2.someMethod() still gets intercepted by
MyAspect only
Scoped AOP
We have had some support for this since JBoss AOP 1.5.0, but
the way this works has been improved a lot.
Background
As you may know JBoss Application Server has a class
loading model that gives you total visibility of classes across the whole
application server. So if you have two ear files deployed, classes in one ear
files use classes from the other ear file. In most cases this is great news
since this means that you only need to make classes available once and
everybody can see them. Also it means you avoid the overhead of call by value
when making calls between the two ear files, which is great for performance.
In JBoss every single top-level deployment gets its own
class loader, and this class loader is part of a class loader domain.
All class loaders within the same class loading domain can see classes from
loaders belonging to the same class loading domain. The way this works is that
a class loading domain is backed by a repository. When a class loader is
asked to load a class it checks with the repository for the class definition
first to see if the class has already been loaded by another class loader
within the domain.
In some cases, though, this global visibility is not what
you want. You may want to for example deploy two different versions of your
application, with the same classes but with say a development version and a
production version. To do this you need to scope
your deployment. The class loader created for a scoped deployment belongs to a
child domain of the global class loader domain. It can still access classes
from the global domain, but the classes in the scoped deployment are invisible
from outside that deployment.
We can deploy development.ear and production.ear, which
contain different versions of the same classes, as scoped deployments. If they
both contain a class called Person, whenever we try to load up Person from
within development.ear we get the copy deployed in development.ear, and
whenever we try to load up Person from with in production.ear we get
production.ear's Person copy.
The real deal
We can do exactly the same for aspects. Up until now,
whenever aspects and bindings are deployed they have been deployed globally to
the whole application server instance meaning that the aspects and bindings are
applied to all matched classes within all deployments on the server. If you
deploy your aspects directly into the server/xxx/deploy folder this is still
what will happen.
However, if you package your aspects into a .aop archive
containing a jboss-aop.xml file (we still need to make this work for the
annotation based deployment model), and package that .aop file into a scoped
deployment we get scoped aop. Meaning the aspects and bindings contained in the
scoped deployment only apply to that particular deployment. So, you could have
two scoped deployments being aspectized in different ways. And of course the
scoped deployments can use the "global" aspects and bindings.
Same rules for inherited methods as for overridden methods
Consider the following case:
public class Base{
void test(){}
}
public class Child{
}
public class ChildTest{
void test(){}
}
---
<aop>
<bind pointcut="execution(* Base->test())">
<interceptor class="BaseInterceptor"/>
</bind>
<bind pointcut="execution(* Child*->test())">
<interceptor class="ChildInterceptor"/>
</bind>
</aop>
---
Base base = new Base(); //1
Child child = new Child(); //2
ChildTest childTest = new ChildTest(); //3
base.test(); //4
child.test(); //5
childTest.test(); //6
With the "old" weaving we needed an exact match on methods for
advices to get bound, meaning that:
- Call 4 would get intercepted by BaseInterceptor
- Call 5 would get intercepted by BaseInterceptor
- Call 6 would get intercepted by ChildInterceptor
The discrepancy is between calls 5 and 6, we get different
behaviour depending on if we have overridden the method or are just inheriting
it, which in turn means we have to have some in-depth knowledge about our
hierarchy of classes and who overrides/inherits what in order to have predictable interception.
The new weaving model matches differently, and treats inherited
methods the same as overridden methods, so:
- Call 4 would get intercepted by BaseInterceptor
- Call 5 would get intercepted by ChildInterceptor
- Call 6 would get intercepted by ChildInterceptor
Upcoming features
I'll keep this section short and post more once these features
have bee have been released properly - some of these exist in the current
alpha 1 release, but need polishing.
Before/After/Throwing support
JBoss AOP has always
used around advices, and you can get all the functionality offered by
before/after/throwing from an around advice. The main overhead of an around
advice is that for every called aspectized joinpoint we need to allocate an
instance of an invocation object to drive the chain of advices. With Before/After/Throwing
advices there is no invocation object needed, so there will be some performance
gains here.
Microcontainer integration
A lot of work has been
done this year to integrate the upcoming JBoss Microcontainer 2.0 with JBoss AOP.
Part of what this allows is:
- Managed aspects - These are aspects managed as beans by the
microcontainer, which can have dependencies on other MC beans, and can
have these dependencies injected.
- Dependencies - If a bean has aspects applied to it,
and those aspects have dependencies, those dependencies become part of
the bean's dependencies
- Per-instance metadata - MC beans can have metadata
attached to them at instance-level and this can be used to determine
whether a particular bean instance should be aspectized or not.
The Microcontainer will be the core of JBoss 5