Friday, May 2, 2008

Typed Advices Tutorial - Part 3

We have been very busy with implementing the main issues left for 2.0.0.GA. But, finally, I have digged sometime to finish this tutorial. Here is the third and final part. Enjoy!

Typed Advices Tutorial - Part 3

So far I have talked about the general form of the JBoss AOP typed advices, and how to declare them in your jboss-aop.xml file. We also saw how to declare them through annotations. This is all you need to know to get started. This final part of the tutorial goes deeper into the options available for typed advices. We will show a complete list of annotated-parameters available, and talk about the possibility of overloading advices.

Annotated Parameters Revisited

In the previous part, we saw examples that used @Arg, @Target and @Args annotated-parameters. Below you will find the complete list of available parameter annotations:

  • @Target: this annotation indicates that the advice parameter receives the target of the joinpoint.

  • @Caller: available when intercepting call joinpoints, this annotation is used on the advice parameter that contains the caller.

  • @Arg: contains one of the joinpoint arguments. It has an optional attribute, index, if you need to indicate the exact joinpoint argument you are referring to (this happens when the attributes have the same type and JBoss AOP cannot infer which argument your @Arg-annotated parameter refers to).

  • @Args: this annotation is used on parameters of the type Object[], that contain the full list of the joinpoint arguments. Use this only when necessary, as there is an overhead incurred in creating this array. This annotation differs from @Arg when it comes to flexibility (your advice will be compatible with any joinpoint, regardless of the type and the number of the joinpoint arguments ) and when you need to edit the value of an argument.

  • @Return: available only for after and finally advices. Parameters with this annotation will contain the value returned by the advice.

  • @Thrown: this is available only for after-throwing and finally advices. It allows you to receive the exception thrown by the joinpoint.

  • @JoinPoint: this annotation is used on reflective parameters, containing joinpoint beans, which allow access to any reflective information regarding the joinpoint. For example, if the joinpoint is a method execution, you can have access to the Method object that represents that object, and to the Class object that represents the class declaring that method. For around advices, the joinpoint beans are the Invocation beans. For the other type of advices, the joinpoint beans are the info beans. While the first ones allow extra features, like meta-data recording, they are also heavier when compared with the info beans.


Bellow you will find an example of a finally advice that uses @JoinPoint, @Thrown, @Return and @Caller:
<aop>
  <aspect class="MyAspect"/>
  <bind pointcut="call(public int POJO1->someMethod(..)) AND within(POJO2)">
    <finally aspect="MyAspect" name="myAdvice"/>
  </bind>
</aop>
----
public class MyAspect
{
  public myAdvice(@JoinPoint MethodCall callBean, @Caller POJO2 caller, @Return int returnedValue, @Thrown Throwable thrown)
  {
    System.out.println(“The execution of joinpoint:”);
    System.out.println(callBean.toString());
    System.out.println(“Executed by caller: “ + caller);
    if (thrown == null)
      System.out.println(“Returned “ + returnedValue);
    else
      System.out.println(“Threw an exception: + thrown);
  }
}



Overloaded Advices

JBoss AOP Advices can be overloaded. This is useful when you want to apply the same advice to several different scenarios. Take a look at the example below:
<aop>
  <aspect class="MyAspect"/>
  <bind pointcut="execution(* POJO->someMethod(..) OR execution(POJO->new(..))">
    <before aspect="MyAspect" name="loggerAdvice"/>
  </bind>
</aop>
----
public class MyAspect
{
  public void loggerAdvice(@JoinPoint JoinPointBean joinPointInfo)
  {
    if (joinPointInfo instanceof MethodExecution)
    {
      System.out.println(“Intercepting method execution:” + ((MethodExecution) joinPointInfo).getMethod().getDeclaringClass().getName() + “->” + ((MethodInfo) joinPointInfo).getMethod().getName());
    }
    else if (joinPointInfo instanceof ConstructorExecution)
    {
      System.out.println(“Intercepting constructor execution:” + ((ConstructorExecution)joinPointInfo).getConstructor().getDeclaringClass().getName() + “->new” );
    }
  }
}


This advice checks the type of joinPointBean every time it gets executed, so it can do a type casting and extract the information it needs. The cost of performing these operations every time the advice gets called can be avoided with the use of overloaded advices. Look at the overloaded implementation of loggerAdvice below:

public class MyAspect
{
  public void loggerAdvice(@JoinPoint MethodExecution methodExecution)
  {
    System.out.println(“Intercepting method execution:” + ((MethodExecution) joinPointInfo).getMethod().getDeclaringClass().getName() + “->” + ((MethodInfo) joinPointInfo).getMethod().getName());
  }

  public void loggerAdvice(@JoinPoint ConstructorExecution constructorExecution)
  {
    System.out.println(“Intercepting constructor execution:” + ((ConstructorExecution)joinPointInfo).getConstructor().getDeclaringClass().getName() + “->new” );
  }
}


The overloaded version of loggerAdvice allows a more efficient interception, because JBoss AOP will define when to call which advice method only once per joinpoint, avoiding the extra cost of checking the type of the joinpoint every time it executes. As a plus, you get a cleaner code.


Sintax and Semantics Rules

There are other rules when writing your typed advices. For example, your advice must match the joinpoints it will intercept. You cannot write an advice that receives a target whose type is Collection and declare it to be String. However, you are not supposed to worry about all the rules involved in writing advices. While you can read all the rules at our documentation, there are error messages for each rule you might break. This way, if you break a rule, JBoss AOP will point you what is wrong so you can easily fix it.

As an example, I am going to do a slight change to the myAdvice example given above:

<aop>
  <aspect class="MyAspect"/>
  <bind pointcut="call(public int POJO1->someMethod(..) AND within(POJO2)">
    <finally aspect="MyAspect" name="myAdvice"/>
  </bind>
</aop>
----
public class MyAspect
{
  public myAdvice(@JoinPoint JoinPointBean callBean, @Caller POJO1 caller, @Return int returnedValue, @Thrown Throwable thrown)
  {...}
}


An error has been inserted to the advice above: it receives a caller of type POJO1, while the pointcut expression clearly states that the type of the caller is POJO2. Unless POJO2 extends POJO1, we are going to get an error from JBoss AOP like the one below:

org.jboss.aop.advice.NoMatchingAdviceException: No matching finally advice called 'myAdvice' could be found in MyAspect for joinpoint Method called by Constructor[calling=public POJO2(boolean) throws java.lang.Exception,called=public int POJO1.someMethod(boolean) throws java.lang.Exception]
  On method 'public void MyAspect.myAdvice(org.jboss.aop.joinpoint.MethodCall,POJO1,int,java.lang.Throwable)'
   @Caller-annotated parameter is not assignable from expected type class POJO2



Next steps

This tutorial introduced you to the new typed advices of JBoss AOP. You can see concrete examples in the tutorial that comes bundled with the JBoss AOP distribution. A good way of starting to write typed advices is playing with those examples, changing the code and seeing what are the results.

Besides, you can also consult our Reference Guide, that will describe typed advices in detail, including a complete description of all the rules involving them (Chapter 4. Advices). There you will also find a table of the possible parameter annotations and which types of advices support them. At the end of Chapter 3 (Joinpoint and Pointcut Expressions), you can also find a complete table containing the two joinpoint bean types available: invocation and info beans.

If you have any questions that are not answered at our documentation, open a thread in our user forum and we will answer as fast as we can.