Skip navigation

 

JBoss AOP 1.5.3.GA has been relased. It is a maintenance release and contains a number of useful bug fixes. You can find the release notes here and it can be downloaded from here. This is also the version of AOP to go into the planned JBoss AS 4.2.0 release. As usual it is usable standalone, and also works in older JBoss AS 4.0x releases.

 

I'd also like to introduce our new permanent team member Flavia Rainone to the community. She joined in November, and has helped out a lot with this release. Her main focus at the moment is working on the upcoming AOP 2.0 release. If you are interested in contributing to AOP 2.0 there is still a fair amount of work left. Please browse our JIRA, and if you find something you would like to take on, contact me. You can find my address on the main JBoss AOP project page.

I will be speaking about the work going on here at JBoss integrating different JBoss projects with JBoss AOP at the Aspect Leadership Program, which looks like it will be a very interesting event. It is held in Vancouver on 14th and 15th March alongside AOSD, and will feature talks from several of the leading people in the AOP field, covering AOP from all angles. Hope to see you there!

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

    * <bind pointcut="execution(* POJO->someMethod*(..))"/>

    *    <advice aspect="MyAspect" name="intercept"/>

    * </bind>

    */

   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