Chapter 7. Dynamic AOP

7.1. Hot Deployment

With JBoss AOP you can change advice and interceptor bindings at runtime. You can unregister existing bindings, and hot deploy new bindings if the given joinpoints have been instrumented. Hot-deploying within the JBoss application server is as easy as putting (or removing) a *-aop.xml file or .aop jar file within the deploy/ directory. There is also a runtime API for adding advice bindings at runtime. Getting an instance of org.jboss.aop.AspectManager.instance(), you can add your binding.

      org.jboss.aop.advice.AdviceBinding binding = 
                              new AdviceBinding("execution(POJO->new(..))", null);
      binding.addInterceptor(SimpleInterceptor.class);
      AspectManager.instance().addBinding(binding);

First, you allocated an AdviceBinding passing in a pointcut expression. Then you add the interceptor via its class and then add the binding through the AspectManager. When the binding is added the AspectManager will iterate through ever loaded class to see if the pointcut expression matches any of the joinpoints within those classes.

7.2. Per Instance AOP

Any class that is instrumented by JBoss AOP, is forced to implement the org.jboss.aop.Advised interface.

public interface InstanceAdvised
{
   public InstanceAdvisor _getInstanceAdvisor();
   public void _setInstanceAdvisor(InstanceAdvisor newAdvisor);
}

public interface Advised extends InstanceAdvised
{
   public Advisor _getAdvisor();
}

The InstanceAdvisor is the interesting interface here. InstanceAdvisor allows you to insert Interceptors at the beginning or the end of the class's advice chain.

public interface InstanceAdvisor
{
   public void insertInterceptor(Interceptor interceptor);
   public void removeInterceptor(String name);
   public void appendInterceptor(Interceptor interceptor);

   public void insertInterceptorStack(String stackName);
   public void removeInterceptorStack(String name);
   public void appendInterceptorStack(String stackName);

   public SimpleMetaData getMetaData();

}

So, there are three advice chains that get executed consecutively in the same java call stack. Those interceptors that are added with the insertInterceptor() method for the given object instance are executed first. Next, those advices/interceptors that were bound using regular binds. Finally, those interceptors added with the appendInterceptor() method to the object instance are executed. You can also reference stacks and insert/append full stacks into the pre/post chains.

Besides interceptors, you can also append untyped metadata to the object instance via the getMetaData() method.

7.3. Preparation

Dynamic AOP cannot be used unless the particular joinpoint has been instrumented. You can force intrumentation with the prepare functionality

7.4. Improved Instance API

As mentioned, you can add more aspects to a woven class using the org.jboss.aop.InstanceAdvisor. This API is limited to adding interceptors to the existing intereptor chains, so it is a bit limited.

The new default weaving mode introduced in JBoss AOP 2.0.0 still allows you access to the InstanceAdvisor interface, but also offers a fuller instance API, which allows you to add bindings, annotation overrides etc. via the normal dynamic AOP API. This is underdocumented, but for a full overview of the capabilites take a look at how org.jboss.aop.AspectXmlLoader interacts with org.jboss.aop.AspectManager. We are working on a new tidier API for the next version of JBoss AOP. Normally, for dynamic AOP you add things to the top level AspectManager, which means that all instances of all woven classes can be affected.

In JBoss AOP 2.0.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. In the following example we prepare all joinpoints of the POJO class and declare an aspect called MyAspect

   <!-- 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.

7.5. DynamicAOP with HotSwap

When running JBoss AOP with HotSwap, the dynamic AOP operations may result in the weaving of bytecodes. In this case, the flow control of joinpoints matched only by prepare expressions is not affected before any advices or interceptors are applied to them via dynamic aop. Only then, the joinpoint bytecodes will be weaved to start invoking the added advices and interceptors and, as a result, their flow control will be affected.

On the other hand, if HotSwap is disabled, the joinpoints matched by prepare expressions are completely instrumented and the flow control is affected before classes get loaded, even if no interceptors are applied to them with dynamic aop.

To learn how to enable HotSwap, refer to the "Running Aspectized Application" chapter.