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:

  <aspect class="MyAspect"/>
  <bind pointcut="public int POJO->someMethod(..)">
    <before aspect="MyAspect" name="myAdvice"/>
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:

  <aspect class="MyAspect">
  <bind pointcut="">*(..)">
    <after aspect="MyAspect" name="myAdvice"/>
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!

Monday, March 24, 2008

Typed Advices Tutorial - Part 1

As we get closer and closer to our 2.0.0.GA release, I found it would be interesting to post a tutorial on typed advices, one of the new features of the next GA release.

The tutorial, targeted to users already familiar with the basics of JBoss AOP, will be split into parts. The plan is to post a new part every Monday. Enjoy!

Typed Advices Tutorial - Part 1

In this part, we will see what are the new types of advices and how to declare a binding using typed advices.

5 Different Types

JBoss AOP now supports 5 types of advices:

  • before: advices of this type are executed before the joinpoint.
  • around: this is equivalent to the single advice type previously supported by JBoss AOP. Around advices work like the interceptors, wrapping the joinpoint execution. They are invoked by JBoss AOP as if they were the original joinpoint, and it is up to them to proceed execution to the joinpoint itself, through the method invokeNext() of the class Invocation.
  • after: advices of the type after are executed after the joinpoint returns normally.
  • throwing: advices of this type are invoked only after the joinpoint throws an exception. If the joinpoint returns normally, these advices will not be called.
  • finally: these advices are invoked after the joinpoint execution, regardless of the way it returns.

The before, after, throwing and finally advices are more lightweight when compared with around advices. Hence, they must be chosen whenever possible over around advices.

Binding Typed Advices

In the previous versions of JBoss AOP, the XML tag advice was the only choice to declare an advice. This tag must be inside a bind tag, like the example below:

<bind pointcut="execution(* *->(..))">
   <advice aspect="Aspect" name="myAdvice"/>

Notice that "Aspect" must have been declared before the bind tag, in an aspect tag.

To declare a typed advice, the XML would be similar to the above, except for the fact that the tag advice will be replaced by a tag with the name of the type of the advice you want to bind.

The example below binds five advices, one of each type. As you can see, the tags before, after, around, throwing, and finally have the same attributes as the tag advice.

<bind pointcut="execution(* *->(..))">
   <before aspect="Aspect" name="myBeforeAdvice"/>
   <around aspect="Aspect" name="myAdvice"/>
   <after aspect="Aspect" name="myAfterAdvice"/>
   <throwing aspect="Aspect" name="myThrowingAdvice"/>
   <finally aspect="Aspect" name="myFinallyAdvice"/>

Notice that the tag advice is also supported in the new JBoss AOP version. As this tag does not indicate the type of the advice being declared, the default type, around, will be used. This keeps compatibility with previous versions, garanteeing that no AOP code will be broken when upgrading to the new release.

To allow declaration of typed advice bindings through annotations, a new attribute has been added to the annotation @Bind:

@Bind(pointcut="execution(* *->(..))", type=AdviceType.BEFORE)

The example above shows an advice annotation that binds the annotated advice (hidden in the example) to the same pointcut we saw in the previous examples. It also specifies the type of the advice as being before. Similarly, we can declare advices of all the other four types. The annotation parameter type is optional and its default value is AdviceType.AROUND.

Notice that the class that contains the advice must be annotated with @Aspect in order to be recognized as an aspect by JBoss AOP.

An Example of Typed Advice

The following Java method is a simple example of typed advice:

public void myAdvice()
   System.out.println("Hello world!");

The signature of typed advices is pretty flexible, allowing even void advices with no parameters at all. The only type of advice that cannot have the above signature is the throwing advice. As we will see, this advice has a mandatory parameter. This means that the method above could be a before, around, after, or finally advice.

In the next part of this tutorial we will see more about the signature rules, showing how an advice can receive pretty much any joinpoint information as a parameter.

See you next week!