Chapter 4. Advices

Advices are aspect methods that are invoked during specific joinpoint executions.

JBoss AOP provides five types of advice.

The default one is the around advice, and it can be used on all execution modes. This advice wraps the joinpoint, in a way that it replaces the joinpoint execution in the base system, and is responsible for proceeding execution to the joinpoint.

Besides around advices, you can write advices that, instead of wrapping the joinpoint, are executed before or after it. In this category, JBoss AOP provides before, after, after-throwing and finally advices. These advices are available only when using the generated advisor mode (this is the default mode in JBoss AOP, to learn how to select another weaving mode, refer to Chapter X).

The next sections will explain in detail the binding and signature rules for JBoss AOP advices.

4.1. Around Advices

An around advice can follow this template:

public Object [advice name]([Invocation] invocation) throws Throwable
{
   try{
      // do something before joinpoint execution
      ...
      // execute the joinpoint and get its return value
      Object returnValue = invocation.invokeNext();
      // do something after joinpoint has executed successfully ...
      // return a value
      return returnValue;
   }
   catch(Exception e)
   {
      //handle any exceptions arising from calling the joinpoint
      throw e;
   }
   finally
   {
      //Take some action once the joinpoint has completed successfully or not
   }
}

In the template above, Invocation refers to one of the Invocation beans, and can be the class org.jboss.aop.joinpoint.Invocation or one of its subtypes.

Since an around advice wraps a joinpoint, it must proceed execution to the joinpoint itself during its execution. This can be done by calling the method invokeNext() on invocation. This method will proceed execution to the next around advice of that joinpoint. At the end of this chain this invokeNext() will proceed to the joinpoint itself. The value returned by the around advice will replace the joinpoint return value in the base system.

For example, in the case where there are two around advices bound to a joinpoint, the first around advice will trigger the second around advice by calling invokeNext(). The second advice will trigger the joinpoint execution by calling the same method. As a result of the invokeNext() execution, the second advice will receive the joinpoint return value. The value returned by this second advice will be received as a result by the first around advice. Finally, the value returned by this advice will replace the joinpoint return value in the base system execution. Normally though, around advices will simply return whatever value the joinpoint returned! This is shown in the preceding template example.

If an around advice wants to completely replace the joinpoint execution, it can skip the call to invokeNext(). This will also skip execution of any subsequent around advices in the chain. As a third alternative, the around advice can call the method invokeTarget() instead of invokeNext(). This method will invoke the target joinpoint directly, skipping any subsequent advices.

The presence of the Invocation parameter is optional. If an around advice does not have this parameter, it can replace the call to invokeNext() with a call to org.jboss.aop.joinpoint.CurrentInvocation.proceed().

The signature described before is the default around advice signature rule. In addition to it, the around advice signature can also be of this form (only in generated advisor mode):

public [return type] [advice name]([annotated parameter],[annotated parameter],...[annotated parameter]) throws Throwable

This signature is joinpoint dependent. The return type of the advice must be a type assignable to the the return type of the joinpoint to be intercepted (i.e. be the same type; a subclass, if the return type is class; or a subinterface or an implementing class, if the return type is an interface). In case the joinpoint being intercepted does not have a return type, this advice return type must be void.

An around advice can have zero or more annotated parameters. The annotated parameters will be covered in detail in Section 4.3, “Annotated Advice Parameters”.

Finally, JBoss AOP also features a special type of around advice: Interceptor. An interceptor class implements org.jboss.aop.Interceptor, and is described in Section 2.4, “Interceptors”.

4.2. Before/After/After-Throwing/Finally Advices

These advices are more lightweight in the JBoss AOP framework, since they do not wrap a joinpoint, avoiding the creation of the Invocation objects per joinpoint execution.

Instead of Invocation objects, JBoss AOP provides JoinPointBean beans for these advices. As described in Section 3.10.1, “Joinpoint Beans”, these beans contain all information regarding a joinpoint, like an Invocation would do. However, since JoinPointBean objects are not used on around advice types, they do not provide proceeding methods, like invokeNext(). They also do not allow you to attach metadata for a particular invocation.

The rules for before, after, after-throwing and finally advices are quite similar. All of them can have zero or more annotated advice parameters in their signature, which will be described in the next subsection.

4.2.1. Before Advice Signature

A before advice is executed before the joinpoint. The signature for a before advice must be of this form:

public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

4.2.2. After Advice Signature

Since an after advice is executed after a joinpoint, it can return a value to replace the joinpoint return value in the base system. So, they can follow one of these signatures:

public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

public [return type] [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

In the first signature, the after advice does not overwrite the joinpoint return value. On the other hand, when using the second signature, the after advice return value will replace the joinpoint return value. As with around advices, this return type must be assignable to the joinpoint return type.

4.2.3. After-Throwing Advice Signature

The fourth type of advice provided by JBoss AOP is the after-throwing type. This advice is invoked only after the execution of a joinpoint that has thrown a java.lang.Throwable or one of its subtypes.

The signature of such an advice is the same as the one for before advices:

public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

Different from the other advice types, an after-throwing advice has a mandatory annotated parameter. This parameter is the exception thrown by the joinpoint execution, as we will see in the next subsection.

4.2.4. Finally Advice Signature

Lastly, JBoss AOP provides the finally advice type. It is invoked from inside a finally block, after the joinpoint execution.

This advice is the only one that is called after a joinpoint execution in a deterministic way. Calls to after and after-throwing advices take place depending on the joinpoint execution outcome. After advices are not called when the joinpoint execution terminates abruptly with an exception. After-throwing ones, on the other hand, are not called when the joinpoint execution returns normally, since no exception is thrown this time. So, if an advice needs to be run no matter what is the outcome of the joinpoint, it should be a finally advice.

Pretty much as after advices, finally advices can follow one of the signatures below:

public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

public [return type] [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

The last signature shows that finally advices can also overwrite the joinpoint execution return value by returning a value themselves. But notice that this return value will not be received by the base system if an exception has been thrown. However, it is easy to know whether this condition is met, by making use of annotated parameters.

4.3. Annotated Advice Parameters

This section lists the annotated parameters that can be used on JBoss AOP advices (available only in generated advisor execution mode). Table 4.1, “Annotated Parameters Table” lists all annotations and their semantics.

Except for the @JoinPoint annotation, used to refer to joinpoint beans, all other annotations are used on parameters that contain joinpoint context values.

Notice that the types of annotated parameters are dependent on the joinpoint being intercepted by the advice.

JBoss AOP will accept any type that is assignable from the type referred by that parameter, as shown in the Type Assignable From column of the table below. For example, for a joinpoint whose target is of type POJO, the annotated parameter that receives the target must be of POJO type, one of POJO's superclasses, or one of the interfaces implemented by POJO.

Regarding the type of joinpoint bean parameters, the rules are the same for the default signature of around advices (without annotations). For example, an around advice that intercepts a method execution, can receive either a MethodInvocation, or an Invocation (the complete list of joinpoint beans and their relationship with joinpoint types was shown in Table 3.1, “ Joinpoint Types Table ”). As already explained, around advices use Invocation instances, while the other advices use JoinPointBean objects.

Notice also that only one annotated parameter can be mandatory: @Thrown. This will be further explained in Section 4.3.1, “@Thrown annotated parameter”.

Except for @Arg, all annotations are single-enforced, i.e., there must be at most only one advice parameter with that annotation per advice.

Table 4.1. Annotated Parameters Table

AnnotationSemanticsType assignable fromMandatoryAdvice type
BeforeAroundAfterAfter-ThrowingFinally
@JoinPointJoinPoint beanJoinpoint invocation typeNoNoYesNoNoNo
JoinpointBean interface typeNoYesNoYesYesYes
@TargetJoinpoint targetJoinpoint target typeNoYesYesYesYesYes
@CallerJoinpoint callerJoinPoint caller type (only for call joinpoints)NoYesYesYesYesYes
@ThrownJoinpoint thrown exceptionjava.lang.Throwable

If used on an after-throwing advice, this parameter can also be:

- assignable from any exception declared to be thrown by the joinpoint

- java.lang.RuntimeException or any subtype of this class

Yes:

- for after-throwing advices

- for finally advices only if @Return is present

No: otherwise

NoNoNoYesYes
@ReturnJoinpoint return valueJoinPoint return typeNoNoNoYesNoYes
@ArgOne of the joinpoint argumentsJoinPoint argument typeNoYesYesYesYesYes
@ArgsAll joinpoint argumentsjava.lang.Object[]NoYesYesYesYesYes
The first column shows the annotation to be used for each parameter type. All annotations are from the org.jboss.aop.advice.annotation package. The column Semantics shows what value each annotated parameter represents. The third column shows from which type the parameter type must be assignable (notice that Throwable and Object[] belong to java.lang package). The Mandatory column indicates whether that value must be present in order for the advice to be considered valid. Finally, the Advice Type column shows for which advice types that parameter is allowed.

Due to the fact that most of these parameters represent context values, their availability depends on the joinpoint type. If an advice receives as a parameter a context value that is not available during a joinpoint execution, the parameter value will be null. The exception to this rule is @Return. If an advice has this parameter, it will not intercept joinpoints that don’t have a return value.

The only exception to this rule is @Args on field read joinpoints. Such an advice will be called with an empty arguments array, in that case.

4.3.1. @Thrown annotated parameter

As shown in Table 4.1, “Annotated Parameters Table”, the presence of a @Thrown annotated parameter can be mandatory depending on the advice type and its parameters.

This annotation is available only for after-throwing and finally advices. For after-throwing advices this parameter is always mandatory:

public class Aspect
{
   public void throwing1(@Thrown RuntimeException thrownException)
   {
      ...
   }

   public void throwing2()
   {
      ...
   }
}


<aop>
   <aspect class="Aspect"/>
   <bind pointcut="...">
      <throwing aspect="Aspect" name="throwing1"/>
      <throwing aspect="Aspect" name="throwing2"/>
   </bind>
</aop>

The advice throwing1 follows this rule; advice throwing2, on the other hand, is invalid, because it does not contain the mandatory @Thrown annotated parameter.

For finally advices, the @Thrown annotation is compulsory only if a @Return annotated parameter is present. This way, a finally advice can identify whether the return value is valid or not. If the @Thrown parameter is null, it means that the joinpoint returned normally and that the value contained in the @Return annotated-parameter is valid. Otherwise, the value contained in @Return annotated parameter must be ignored (it will be null if the return type is not primitive, 0 if it is a primitive number or false if it is boolean). If the finally advice does not receive the joinpoint return value, the use of the @Thrown annotated parameter is optional and, as expected, its value will be null if the joinpoint being intercepted did not throw an exception. Take a look at the next example:

public class Aspect
{
   public void finally1(@Thrown Throwable thrownException)
   {
      ...
   }

   public void finally2()
   {
      ...
   }

   public void finally3(@Return int returnedValue, @Thrown Throwable thrownException)
   {
      if (thrownException == null)

      {

         //We returned normally, the @Return parameter is valid

         int i = returnedValue;

      }

      else

      {

         //An exception happened while invoking the target joinpoint

         //The return value is invalid

      }

    }

   public void finally4(@Return int returnedValue)
   {
      ...
   }

}


<aop>
   <aspect class="Aspect"/>
   <bind pointcut="execution(public int *->*(..))">
      <finally aspect="Aspect" name="finally1"/>
      <finally aspect="Aspect" name="finally2"/>

      <finally aspect="Aspect" name="finally3"/>

      <finally aspect="Aspect" name="finally4"/>

   </bind>
</aop>

This example binds four finally advices to the execution of all public methods that return an int value. Take note on the type of the @Thrown-annotated parameters, which must be Throwable for this type of advice.

The presence of @Thrown is not mandatory in advices finally1() and finally2(), because they do not have a @Return annotated parameter. Hence, both advices are valid. Besides, finally1() will receive a non-null exception only when the joinpoint being intercepted throws an exception.

For advice method finally3() the presence of a @Thrown annotated parameter is mandatory because this advice also has a @Return annotated parameter. If an exception happens when invoking the target joinpoint, this advice will receive a non-null @Thrown parameter, meaning that the @Return annotated parameter is invalid. If the joinpoint completes normally, the @Thrown annotated parameter will be null and the @Return annotated parameter will contain the return value of the target joinpoint.

The finally4() advice is invalid, it contains a @Return parameter, but has no @Thrown annotated parameter. Finally advices require a @Thrown parameter if a @Return annotated parameter is present.

4.3.2. JoinPoint Arguments

As we saw, an advice can receive the joinpoint arguments as annotated parameters. This can be achieved with the use of two different annotations: @Arg and @Args.

There is a great difference between these two approaches, though. With @Arg, each parameter is equivalent to a single joinpoint parameter. With @Args, one single parameter, of type Object[], receives an array containing all joinpoint arguments. This last possibility is more generic than the first one, since it can be used independently of the joinpoint argument types. Plus, it allows changes to the argument values. Any changes performed on the values of this array will be perpetuated to the joinpoint execution. However, the use of @Args parameters on a join point interception means the arguments array needs creation. The same happens with the use of getArguments() and setArguments() methods on Invocation classes. So the use of @Arg annotated parameters is more lightweight, and should be used whenever there is no need to changing the joinpoint arguments.

When using @Arg annotated parameters, the types of these parameters depend on the joinpoint being intercepted. Not all the target joinpoint arguments need to be included as parameters to the advice method. An advice can receive only the argument values that are relevant to its execution.

Given all the possibilities in the usage of @Arg, JBoss AOP will match the advice parameters with the joinpoint ones, to infer to which joinpoint argument each advice parameter refers to. This matching process consists of the following steps:

  • Each advice parameter will be matched to the first unmatched joinpoint argument that has the same type. This is done in the order that the advice parameters appear in the advice method.

  • If any advice parameter is left unmatched, we proceed to an additional step. Each advice parameter will be matched to the first unmatched joinpoint argument that is assignable to it. This is done in the order that the advice parameters appear in the advice method declaration.

To illustrate this mechanism, consider the following scenario:

public class POJO
{
    void method(Collection arg0,  List arg1, int arg2, String arg3){}
}


<aop>
   <aspect class="MyAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <before aspect="MyAspect" name="advice"/>
   </bind>
</aop>

The example above shows a xml-declared binding. We will use examples with those to illustrate signature concepts from now on. Detailed syntax of xml bindings is shown in Chapter 5, XML Bindings.

Class POJO is a plain java old object that contains only one method. When calling this method, we want to trigger MyAspet.advice() before this method is called. POJO.method() receives four distinct arguments, all of them can be available to an advice by using @Arg annotated parameters. If MyAspect.advice() has the following signature:

public class MyAspect
{
   public void advice(@Arg Collection param0, @Arg List param1, @Arg int param2, @Arg String param3)
   {
      ...
   }
}

MyAspect.advice() parameters will be trivially matched to POJO.method() arguments as follows:

param0 <- arg0
param1 <- arg1
param2 <- arg2
param3 <- arg3
         

The matching outcome will be the same if MyAspect.advice() signature changes slightly in the following manner, since Collection is assignable from List for param2:

public class MyAspect
{
   public void advice (@Arg Collection param0, @Arg Collection param1, @Arg int param2, @Arg String param3)
   {
      ...
   }
}

If MyAspect.advice() receives only one parameter, of type java.lang.Object:

public class MyAspect
{
   public void advice(@Arg Object param0)
   {
      ...
   }
}

The parameter matching outcome will be:

param0 <- arg0
         

Since there is no joinpoint argument of type Object, we proceed to the additional matching step in this case. Because arg0 is the first unmatched argument that is assignable to Object, we assign this argument to param0.

Notice that JBoss AOP will match all parameters correctly if we invert the order of parameters:

public class MyAspect
{
   public void advice(@Arg int param2, @Arg Collection param0, @Arg String param3, @Arg List param1)
   {
      ...
   }
}

If one writes an advice whose unique parameter is a Collection, and we want to refer to the second joinpoint argument:

public class MyAspect
{
   public void advice (@Arg Collection param1)
   {
      ...
   }
}

It will not work as desired. JBoss AOP will assign arg0 to param1:

param1 <- arg0
         

In cases like this, it is possible to enforce the correct matching of joinpoint arguments and advice parameters. The annotation @Arg has an attribute, index, whose purpose is to define the index of the argument to which that parameter refers.

So, in this case, the MyAspect.advice() parameter list below:

public class MyAspect
{
   public void advice (@Arg(index=1) Collection param1)
   {
      ...
   }
}

Will have the desired matching, which is:

param1 <- arg1
         

In the example just shown in this section, MyAspect.advice() was a before advice, but the same rules are used for all advices using @Arg annotated parameters.

4.4. Overloaded Advices

Method names can be overloaded for interception in different joinpoint scenarios. For instance, let's say you wanted to have a different trace advice for each invocation type. You can specify the same method name trace and just overload it with the concrete invocation type.

public class AroundAspect
{
   public Object trace(MethodInvocation invocation) throws Throwabl
   {
      try
      {
         System.out.println("Entering method: " + invocation.getMethod()");
         return invocation.invokeNext(); // proceed to next advice or actual call
      }
      finally
      {
         System.out.println("Leaving method: " + invocation.getMethod()");
      }
   }
   
   public Object trace(ConstructorInvocation invocation) throws Throwable
   {
      try
      {
         System.out.println("Entering constructor: " + invocation.getConstructor()");
         return invocation.invokeNext(); // proceed to next advice or actual call
      }
      finally
      {
         System.out.println("Leaving constructor: " + invocation.getConstructor()");
      }
   }
}

As you can see, the selection of the advice method is very dynamic. JBoss AOP will select the most appropriate advice method for each joinpoint interception. For the following setup:

class POJO
{
   public POJO(){}
   public someMethod(){}
}

<aop>
   <aspect class="AroundAspect"/>
   <bind pointcut="all(POJO)">
      <advice aspect="AroundAspect" name="trace"/>
   </bind>
</aop>

When calling POJO’s constructor:

pojo.someMethod();

JBoss AOP will call the trace() method taking a ConstructorInvocation, and when calling:

pojo.someMethod();

JBoss AOP will call the trace() method taking a MethodInvocation.

This examples shows that JBoss AOP will select the most appropriate advice method for each joinpoint interception. The capability of selecting overloaded advices is available for all types of advices. And its impact in the system performance is minimal since this selection is done once.

In this section, we will describe every rule JBoss AOP uses to select an advice method when this one is overloaded.

4.4.1. Annotated-parameter Signature

Let's start with the selection of advices when all of them use the annotated-parameter signature. As we will see later, very similar rules are used for selecting advices with the default signature.

The process of selection of advices that follow the annotated-parameter signature depends on the priority of each kind of parameter:

@JoinPoint > @Target > @Caller > @Throwable = @Return > @Arg > @Args

This priority is used in two different criteria:

  • presence of the annotated parameter

  • assignability degree of the annotation parameter

4.4.1.1. Presence priority

This rule is quite simple, it means that an advice that receives only a joinpoint bean (@JoinPoint) as its parameter will have a higher priority than another advice that receives all other annotated parameters available (notice we are following the annotation priority order just described).

In other words, the first OneAspect.after() advice method will be chosen when calling POJO.someMethod() in this example:

public class POJO
{
   String someMethod(String s){}
}

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->someMethod(..))">
      <after aspect="OneAspect" name="after"/>
   </bind>
</aop>


public class OneAspect
{
   public void after(@JoinPoint MethodJoinPoint mjp){} //1
   public String after(@Target POJO pojo, @Return String ret, @Arg String arg0){} //2
}

Again in the following example, the first OneAspect.after() advice method will be chosen when calling POJO.someMethod(). The first after() advice method’s highest priority parameter is @Target, the second advice parameter’s highest priority parameter is @Return, and @Target has a higher priority than @Return:

public class POJO
{
   String someMethod(String s){}
}

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->someMethod(..))">
      <after aspect="OneAspect" name="after"/>
   </bind>
</aop>


public class OneAspect
{
   public void after(@Target POJO pojo){} //1
   public String after(@Return String ret, @Arg String arg0){} //2
}

In cases where the highest priority annotated parameter of two advice methods is the same, we move on to the next highest priority annotated parameter of both advices. In the following scenario, both OneAspect.after() methods have the @JoinPoint parameter as the highest priority parameter. The first one has a @Target as its second-highest priority parameter while the second one has @Return as its second-highest priority parameter. Since @Target has a higher priority than @Return, the first OneAspect.after() is chosen for POJO.someMethod().

public class POJO
{
   String someMethod(String s){}
}

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->someMethod(..))">
      <after aspect="OneAspect" name="after"/>
   </bind>
</aop>


public class OneAspect
{
   public void after(@JoinPoint MethodJoinPoint mjp, @Target POJO pojo){} //1
   public String after(@JoinPoint MethodJoinPoint mjp, @Return String ret){} //2
}

In the next example, the first OneAspect.before() advice is chosen over the second one when calling POJO.someMethod(). The reason is that, all else being equal, the first one matches more parameters:.

public class POJO
{
   String someMethod(String s, int i){}
}

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->someMethod(..))">
      <before aspect="OneAspect" name="before"/>
   </bind>
</aop>


public class OneAspect
{
   public void before(@Arg String s, @Arg int i){} //1
   public String before(@Arg String s){} //2
}

If the priority of annotated parameters using the presence criterion is the same on more than one advice, the next criterion, the assignability degree, is used.

4.4.1.2. Assignability Degree

The assignability degree rule will select the advice with the lowest assignability degree on the highest priority parameter. The assignability degree is simply the distance in the class hierarchy between the parameter type, and the type it must be assignable from.

As an example, let us look at the following class hierarchy:

public interface POJOInterface{}

public class POJOSuperClass extends java.lang.Object{}

public class POJO extends POJOSuperClass implements POJOInterface
{
   void method(){}
}

And this advice binding:

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <before aspect="OneAspect" name="before"/>
   </bind>
</aop>

public class OneAspect
{
   public void before(@Target POJO target){} //1
   public void before(@Target POJOInterface target){} //2
   public void before(@Target POJOSuperClass target){} //3
   public void before(@Target Object target){} //4
}

With POJO as the target of a joinpoint, the parameter list fo the first OneAspect.before() advice method has an assignability degree 0 on @Target.

The parameter lists for the second and third OneAspect.before() advice methods both have an assignability degree of 1 for @Target, since it takes one step through the hierarchy to reach the desired type, POJO.

Finally, the parameter list for the fourth OneAspect.before() advice method has an assignability degree of 2 on @Target.

Hence, JBoss AOP will select the first advice in the example above, since it has the lowest asignability degree on @Target.

The assignability degree rule is, similarly to the presence rule, applied on the highest priority annotated parameter, which is @JoinPoint. In case there is a match using this criteria (i.e., either both advices lack a @JoinPoint annotated parameter, or they both have the same type on the @JoinPoint parameter), we move to the next highest priority annotated parameter, which is @Target. The same rule is applied until we can find an advice with the highest priority.

Notice that the assignability degree of an advice on @Arg is the sum of the assignability degree on all @Arg parameters. In the following scenario:

public class POJO
{
   public void method(POJO argument0, String argument1, int argument2)
}


<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <before aspect="OneAspect" name="before"/>
   </bind>
</aop>


public class OneAspect
{
   public void before(@Arg POJO p, @Arg String s, @Arg int i){} //1
   public void before(@Arg POJOSuperClass p, @Arg String s, @Arg int i){} //2
   public void before(@Arg POJO p, @Arg Object s, @Arg int i){} //3
   public void before(@Arg Object p, @Arg Object s, @Arg int i){} //4
}

The first advice has assignability degree of 0 (for POJO) + 0 (for String) + 0 (for int). Notice how primitive types don’t have superclasses, and, hence, have always a 0 value of assinability degree.

The second advice has a larger assignability degree, since POJOSuperClass is the superclass of POJO, @Arg POJOSuperClass p has assignability degree of 1. Hence, this advice assignability degree on @Arg is: 1 + 0 + 0 = 1.

The third one also has an assignability degree of 1, since Object is the superclass of String.

Finally, the last advice has assignability degree of 3 on @Arg. The first parameter, @Arg Object p, refers to POJO and has assignability degree of 2. The second one, assignability degree of 1, since it refers to String. And, since @Arg int refers to the int argument of POJO.method(), we have 2 + 1 + 0 = 3.

In the above example, JBoss AOP would select the first advice to intercept POJO.method() execution.

4.4.1.3. Return Types

For annotated parameters typed around advices, there is a third rule, which is the return type. This rule also applies to after and finally advices. If the joinpoint has a non-void return type, the assignability degree of the advice return type is analyzed, pretty much in the same way we do with annotated parameters. So, for overloaded around advices, these three criteria are applied:

  • presence of annotated parameter

  • assignability degree of annotated parameter

  • assignability degree of return type

If two advices have the same ranking on the first two criteria, we check their return types and pick the advice with the lowest assignability degree:

public class POJO
{
   public Collection method(int arg0, boolean arg1, short arg2) {…}
}


<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <advice aspect="OneAspect" name="around"/>
   </bind>
</aop>


public class OneAspect
{
   public Collection around(@JoinPoint Invocation inv, @Arg int param0) throws Throwable
   {...} //1

   public List around(@JoinPoint Invocation inv, @Arg boolean param1) throws Throwable
   {...} //2

}

In OneAspect above, we have two around advices. Both of them are equal when compared using the presence criteria. When comparing them using the assignability of annotated parameter, both of them have the same degrees on @JoinPoint and on @Arg parameters. In this case, we will compare their return type assignability degree.

Notice that, when it comes to return types, it is the return type that must be assignable to the joinpoint type, and not the contrary. This is due to the fact that JBoss AOP will assign the advice return value to the joinpoint return result in the base system. Hence, in the example above, the caller of POJO.method() expects a Collection return value. So, it is ok to receive either a Collection from the first advice, as the more specific type List from the second advice. But JBoss AOP will complain if your advice returns an Object (Object return type is allowed only in the default signature; here we are discussing the annotated-parameter signature), because we can’t give an Object to the base system when it is expecting a Collection.

So, in the above example, the first advice has an assignability degree of 0 on the return type, becase it takes 0 steps in the hierarchy to go from Collection to Collection. In the second advice, this value is 1, because it takes 1 step to go from List to Collection. JBoss AOP would select the first advice.

On overloaded after and finally advices, we also have a return type rule. But, since the return type is optional (these advices can return a value, but is not enforced to it), we have a total of four rules for this advice:

  • presence of annotated parameter

  • assignability degree of annotated parameter

  • presence of non-void return type

  • assignability degree of return value type

The third rule, presence of non-void return type, states that JBoss AOP will give preference to an after advice that returns a value:

<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <after aspect="OneAspect" name="around"/>
   </bind>
</aop>


public class OneAspect
{
   public Collection after(@Arg int param0) {...} //1
   public List after(@Arg boolean param1) { ... } //2
   public void after(@Arg short param2) { ... }   //3
}

Considering the same POJO class defined previously (with public void method(int, boolean, short)), all three overloade versions of OneAspect.after() advice wil be considered equivalent in the first two criteria. Hence, we move to the third rule, that states that JBoss AOP prefers an after advice that returns a value over another one that is void. So, in the example above, the third advice is ruled out, and JBoss AOP still has two advices to select. Moving to the next rule, he assignability degree of the return type, we have the same result as the OneAspect.around() advice: the first one has a 0 degree, and the second one, a 1 degree value. As a conclusion of these degrees, JBoss AOP will select the first advice, with the lowest return assignability degree.

4.4.1.4. A Match

Notice that, iIf JBoss AOP cannot find an advice with highest priority, it just selects one of the methods arbitrarily. This would be the case of the following advice method scenario:

public class POJO
{
   public void method(int arg0, long arg1) {…}
}


<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <before aspect="OneAspect" name="before"/>
   </bind>
</aop>


public class OneAspect
{
   public void advice(@Arg int arg0) {}
   public void advice(@Arg long arg1) {}
}

4.4.1.5. Lowest Priority

There are exceptions for the rules we’ve seen. Advices with one or more of the following characteristics will be considered lowest priority, regardless of any other criteria:

  • an advice that receives @Target parameter to intercept a joinpoint with no target available

  • an advice that receives @Caller parameter to intercept a joinpoint with no caller available

  • an advice that receives @Arg parameter to intercept a field read joinpoint

4.4.2. Default Signature

For the default around advice signature (i.e., without annotated parameters), there is only one parameter to analyze, the invocation. So, the priority rules are very simple:

  • presence of the invocation parameter

  • assignability degree of the invocation parameter.

Lets revisit the example given in the beginning of this section, in augmented version:

class POJO
{
   public int field;
   public POJO(){}
   public someMethod(){}
}


public class OneAspect
{
   public Object trace(MethodInvocation invocation) throws Throwable {...} //1
   public Object trace(ConstructorInvocation invocation) throws Throwable {...} //2
   public Object trace(Invocation invocation) throws Throwable {...} //3
   public Object trace() throws Throwable {...} //4
}


<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="all(POJO)">
      <advice aspect="OneAspect" name="trace"/>
   </bind>
</aop>

The fourth advice above will never be called, considering the presence rule. It is the only one that lacks the Invocation parameter, and would be called only if all others were considered invalid in a scenario, which won’t happen in this example. By ruling out this advice with the presence rule, all other advices are equivalent: the invocation parameter is present in all of them. So, we need to move on to the assignability degree rule to select one of them. However, the assignability degree needs to be calculated accordingly to the joinpoint being intercepted. JBoss AOP needs to evaluate each joinpoint type to be intercepted to do the correct selection for each case.

Consider the interception of the constructor of POJO. In that case, the first advice is considered invalid, becase a MethodInvocation is not assignable from the invocation type that JBoss AOP will provide, ConstrucorInvocation. We are now left with the second and third advices. The second one has assignability degree of 0 on the invocation type. The third one, assignability degree of 1 (it takes one step in the hierarchy to go fom ConstructorInvocation to Invocation). So, in this case, JBoss AOP will select the second advice, because it is the valid advice with the lower assignability degree on the invocation.

Similary, to intercept the execution of POJO.someMethod(), JBoss AOP will consider the second advice invalid, because it is supposed to receive an invocation whose type is assignable from MethodInvocation. Since the first advice has an assignability degree of 0 on the invocation, and the third one, assignability degree of 1, JBoss AOP will select the first one.

Given that Invocation will always be the super class of the expected invocation type, JBoss AOP will select this advice, whose assignability degree will always be 1, only when the other two advices are invalid. That would be the case of a field read, where the invocation type is FieldReadInvocation.

4.4.3. Mixing Different Signatures

Finally, when we mix default signature methods with annotated parameter ones, an advice in one of the forms:

public Object [advice name]([Invocation] invocation) throws Throwable

public Object [advice name]([Invocation] invocation) throws Throwable

public Object [advice name]() throws Throable

Has the highest priority over all annotated-parameter advices. If there is more than one with the default signature, the criteria described in the previous section will be used to select one of them..

Notice that mixing different signatures is possible only with around advices, since only these ones can follow the default signature.

4.5. Common Mistakes

While writing advices and bindings, it is possible to make some mistakes, like, for example, mistyping the advice name, or writing an advice with an invalid signature.

Whenever there is a mistake in the advice name or signature, JBoss AOP will throw an exception with a message stating the cause of the error. The exception thrown is a runtime exception and should not be treated. Instead, it indicates a mistake that must be fixed.

There are two types of exceptions JBoss AOP can throw on those cases:

  • org.jboss.InvalidAdviceException

    This exception indicates that an advice's signature is considered invalid for the type used on the binding.

    This can happen when the advice is mistakenly declared to be of the wrong type, or when one of the signature rules was not followed.

  • org.jboss.NoMatchingAdviceException

    This exception is thrown when JBoss AOP can not find an advice method suitable for a specific joinpoint to be intercepted.

    A possible scenario is when there is no advice method with the name used on the bind declaration. To solve it, just fix the advice name on the declaration or add a method with the declared advice name.

    When there is one or more methods with the advice name, this exception indicates that JBoss was not able to find an advice with a signature that suits the joinpoint to be intercepted. In this case, the solution can be to alter the signature of one of the existent advice methods, or to add an overloaded advice method that matches the joinpoint to be intercepted.