Monday, March 31, 2008

Typed Advices Tutorial - Part 2

Today we will see what are the signature rules a valid typed advice must follow.


Introduction to Annotated Parameters

Typed advices can receive as parameters several context values, besides reflective objects. In this introduction, we will talk only about parameters that receive the joinpoint target and the argument values. The other options will be seen in the next part of this tutorial.


Take a look at the following example:

<aop>
  <aspect class="MyAspect"/>
  <bind pointcut="public int POJO->someMethod(..)">
    <before aspect="MyAspect" name="myAdvice"/>
  </bind>
</aop>
----
public class MyAspect
{
  public myAdvice(@Target POJO pojo, @Arg int arg1, @Arg long arg2)
  {
    System.out.println("Hello world!");
  }
}

This example binds a before advice with the pointcut expression "public int POJO->someMethod(..)". The advice receives as parameters the target of POJO.someMethod execution, and two arguments which should be of type int and long. Now, take a look at the class POJO below:

public class POJO
{
  public void someMethod(int arg1, long arg2){}
  public void someMethod(int arg1, int arg2, long arg3) {}
  public void someMethod(String arg1, int arg2, long arg3) {}
  // public void someMethod(String arg1, int arg2) {}
}

This is what would happen to our advice when it intercepts each one of the methods above:

- someMethod(int arg1, long arg2)
JBoss AOP invokes the advice passing as parameters the POJO target, and the two arguments received by someMethod

- someMethod(int arg1, int arg2, long arg3)
The same as before, but the advice will receive the values of the first and third arguments, skipping int arg2.

- someMethod(String arg1, int arg2, long arg3)
Now the advice will receive the values of arg2 and arg3 as the @Arg-annotated parameters.

Would the last method of POJO, someMethod(String, int), be uncommented, JBoss AOP will throw an InvalidAdviceException telling you that it cannot find a way of applying your advice to a method that does not receive a long-typed argument.

Now, look at the second version of our advice below:

public class MyAspect
{
  public myAdvice(@Target POJO pojo, @Args Object[] args)
  {
     if(args.length > 0 && args[0] instanceof Integer)
     {
       // the intercepted method will receive a new int argument value
       args[0] = Integer.valueOf(args[0].intValue() - 1);
     }
  }
}

This advice receives the list of the intercepted method arguments regardless of the number and type of arguments declared by this method. This would allow us to uncomment POJO.someMethod(String, int) without getting an InvalidAdviceException. Besides, any changes performed to the values contained in the args array will be propagated to the joinpoint. This allows myAdvice to edit the value of a joinpoint argument, as shown above.

Flexibility and Parameter Annotations

So what you are probably asking yourself is why do you have to use the annotations @Target, @Arg and @Args to indicate the meaning of each of your advice's parameters.

The answer to this question is flexibility. Take a look at the two different advices below:

public void advice1(@Target Object target)
public void advice2(@Arg Object arg)

As you can see, you do not have to specify the exact type of the target of the joinpoint, as we did before. Instead of @Target POJO, our example could have used @Target Object as the advice parameter, which is useful when your advice is going to be applied to several different target types. This is what advice1 above does: it does not specify the type of the target. JBoss AOP will successfully apply this advice to any joinpoint, regardless of the type of the target.

The second advice, advice2, also does not specify the type of the argument it wants to receive. So it will receive as parameter the first non-primitive argument of the intercepted joinpoint.

Now, if we remove the parameter annotations from advice1 and advice2, we will not be able of differentiating between those advices, except for their names. Hence, JBoss AOP will not be able of knowing what value it should provide to the advice parameter. This gets more complex when you have more than one advice parameter, as the example below:

public void advice1(@Target Object target, @Arg Object arg)
public void advice2(@Arg Object arg, @Target Object target)

Both advices receive the joinpoint target and the first non-primitive joinpoint argument value. How could we differentiate between them and decide what value to pass to each of those advice parameters without the annotations?

This makes the parameter annotation usage essential to achieve total flexibility regarding what to receive as parameter value, and in which type and order. So, when writing your typed advices you must follow the rule below:

The parameters of a typed advice must be annotated.

Advice Return Type

All the typed advices we have seen so far had a void return type.

This is not mandatory. After and finally advices are allowed to have a non-void return type. Take a look at the following example:

<aop>
  <aspect class="MyAspect">
  <bind pointcut="">*(..)">
    <after aspect="MyAspect" name="myAdvice"/>
  </bind>
</aop>
----
public MyAspect
{
  public String myAdvice()
  {
     return "Hello world!";
  }
}

The advice above intercepts all methods of POJO that return a String and it overwrites the method return value, by returning the string "Hello World".

In practice, this is useful for aspects that perform extra actions on the joinpoint return type. For example, an aspect that implements a remote call layer would want to deserialize the return value of a remote call before providing the result to the caller.

Throwing Exceptions

Your advice is allowed to declare to throw any exceptions you like. JBoss AOP will wrap that exception inside a RuntimeException if this exception is not declared by the intercepted joinpoint. On the other hand, if the joinpoint declares to throw that exception type, JBoss AOP will just throw the exception as is. Either way, the basis application will get the exception as if the intercepted joinpoint had thrown it.

Part III Preview

In the next part, we will see the complete list of parameter annotations available. Besides, we will talk about overloaded advices. See you!

No comments: