Before and After Advices

Overview

JBoss AOP provides several types of advices. The advices seen on the previous section, as well as JBoss AOP interceptors, provide around join point interception. In this example we introduce before and after advices, and compare their roles to around advices' role.

Writing Before/After Advices

Before and after advices are lightweight advices when compared to around advices. JBoss AOP invokes these advices before or after the joinpoint execution.

As we will see in the next example, these advices can have several different signatures. However, in our first example, we will use signatures that are closest to what has been shown on previous examples. So, we introduce before and after advices as being of the form:

public void <any-method-name>(@JoinPoint <any Joinpoint type>)

Differently from around advices and interceptors, throwing a java.lang.Throwable is not necessary, because these advices do not wrap a joinpoint.

Instead of Invocation instances, before/after advices receive instances of org.jboss.aop.joinpoint.JoinpointBean and subinterfaces as parameters.

Furthermore, one can notice the presence of the parameter annotation @org.jboss.aop.advice.annotation.JoinPoint. This is necessary because before/after advices can receive values with several semantic roles as parameters. As we will see next, this annotation is used to distinguish this parameter type from the other ones, so we can achieve flexibility on the type, order and presence of advice parameters.

Binding Before/After Advices

To bind a before and after advices to a pointcut, you need to follow this structure:

<aspect class="aspect class name" scope="scope of the aspect">

<bind pointcut="pointcut expression">
      <before name="advice name" aspect="aspect class name"/>
</bind>
   
<bind pointcut="pointcut expression">
      <after name="advice name" aspect="aspect class name"/>
</bind>

As you can see, it is as simple as declaring a usual <bind> tag, and adding to it <before> and <after> tags for before and after advices respectively. Not forgetting that we need to declare each aspect class with the <aspect> tag (in the same way shown before). We can also mix interceptor declarations and around, before, and after advices declarations in a single binding, like follows:

<bind pointcut="pointcut expression">
      <interceptor class="interceptor class name"/>
      <advice name="advice name" aspect="aspect class name"/>
      <before name="advice name" aspect="aspect class name"/>
      <after name="advice name" aspect="aspect class name"/>
</bind>

The tag <advice> above has been introduced by the previous example, and refers to advices of the default type, around. As an alternative, you can specify explicitly the type of an advice to be around by using the tag <around> as follows:

<bind pointcut="pointcut expression">
      <around name="advice name" aspect="aspect class name"/>
</bind>

For clarity, we have opted to use the tag <around> instead of <advice> on all examples of this tutorial that involve typed advices.

Open up jboss-aop.xml to see an example of binding with typed advices:

<bind pointcut="execution(public void $instanceof{Transaction}->run())">
      <before name="beforeAdvice" aspect="mypackage.MutexAspect"/>
      <after name="afterAdvice" aspect="mypackage.MutexAspect"/>
</bind>

For more examples on typed advices bindings, refer the jboss-aop.xml file of our next example

Around, Before and After Advices

As seen on previous sections, around advices wrap the joinpoint execution. An around advice replaces the joinpoint execution in the base system, and is responsible for forwarding execution to the joinpoint itself, as well as for returning a value to the base system.

Around advices can be composed by four parts at most: a before joinpoint execution block, a proceed to joinpoint step, an after joinpoint execution block, and, finally, a return value step. To see an example, consider this PER_VM scoped aspect:

public class SynchronizationAspect
{
   public Object aroundAdvice(Invocation invocation) throws Throwable
   {
      Object result;
       
      // part 1: retrieve lock before joinpoint execution
      synchronized(this)
      {
         System.out.println(">>> Retrieved concurrency lock");
          
         // part 2: proceed to joinpoint execution
         result = invocation.invokeNext();
       
      // part 3: release lock after joinpoint execution
         System.out.println("<<< Releasing concurrency lock");
      }
       
      // part 4: return result
      return result;
   }
}

SynchronizationAspect synchronizes all intercepted joinpoints (in a concurrent programming application), avoiding that two or more intercepted joinpoints run at the same time.

As we can see, SynchronizationAspect.aroundAdvice() is composed of four steps:

Notice that this advice does not alter joinpoint execution (the advice does not skip it) nor its return result (the advice could overwrite this value, but returns the joinpoint return value itself instead). Hence, we can say that the relevant part of it is only what is performed before and after the joinpoint (parts 1 and 3). To illustrate the before/after concept, we wish to replace this single around advice by a couple of before/after advices. However, we cannot do that by using the previous example. A synchronized block cannot be splitten into two parts and still achieve the same effect. So, lets first replace this around advice by one that uses a mutex intead of a synchronized block:

public class MutexAspect
{
   private Object lock = new Object();
   private boolean locked = false;

   public Object aroundAdvice(Invocation invocation) throws Throwable
   {
      Object result;
       
      // part 1: retrieve lock before joinpoint execution
      synchronized(lock)
      {
         while (locked)
         {
            try
            {
               lock.wait();
            }
            catch(InterruptedException e)
            {
               Thread.currentThread().interrupt();
               return;
            }
         }
         locked = true;
         System.out.println(">>> Retrieved concurrency lock");
      }
       
      // part 2: proceed to joinpoint execution
      result = invocation.invokeNext();
       
      // part 3: release lock after joinpoint execution
      synchronized(lock)
      {
         locked = false;
         lock.notify();
         System.out.println("<<< Releasing concurrency lock");
      }
       
      // part 4: return result
      return result;
   }
}

As much as SynchronizedAspect, MutexAspect avoids simultaneous access to the intercepted joinpoint, and is also composed of the four steps (getting a lock, proceeding to joinpoint execution, releasing the lock, and returning a result).

Now we can easily split this around advice into two advices: one that retrieves the lock, and another one that releases it. We wish to run the first one before a joinpoint, and the other one, after it.

The example that follows is a copy of the previous one, except for that, now, MutexAspect.aroundAdvice() has been splitten into MutexAspect.beforeAdvice() and MutexAspect.afterAdvice():

public class MutexAspect
{
   private Object lock = new Object();
   private boolean locked = false;

   public void beforeAdvice(@JoinPoint Joinpoint joinPoint)
   {
      synchronized(lock)
      {
         while (locked)
         {
            try
            {
               lock.wait();
            }
            catch(InterruptedException e)
            {
               Thread.currentThread().interrupt();
               return;
            }
         }
         locked = true;
         System.out.println(">>> Retrieved concurrency lock");
      }
   }

   public void afterAdvice(@JoinPoint Joinpoint joinPoint)
   {
      synchronized(lock)
      {
         locked = false;
         lock.notify();
         System.out.println("<<< Releasing concurrency lock");
      }
   }
}

Notice that, in this version, parts 2 and 4 are gone (proceeding to joinpoint execution and returning its result). This is due to the fact that before and after advices don't wrap a joinpoint, they are just executed before or after it.

Around vs Before/After

As is shown in this example, around advices can generally be broken into two related before and after advices. The reverse is also true. However, there are some subtleties that can help you to choose one form or the other.

First of all, these are features that are available only to around advices:

When you need one of those advanced features, you should use an around advice or interceptor. Besides, there are aspects that cannot be implemented as a pair of before and after advices. An example would be the SynchronizedAspect shown before. A synchronized block cannot be broken into two blocks and achieve the same result as one single synchronized block. In the example, we wrote a similar, more complex aspect to illustrate the before/after advice concept. In real applications, however, this aspect would not be replaced by a more complex one, unless this could bring advantages to the application.

On the other hand, before and after advices also provide some advantages. A pair of related before/after advices:

Since they are more lighweight, prefer using a pair of before/after advices, unless you have a reason to do otherwise. Regarding the second item in the list, it can be extremely useful in the case of MutexAspect. Instead of controling the multi-threaded access to a single joinpoint, we could obtain the lock before a joinpoint, and release it later, after a different joinpoint execution. This allows the synchronization of a sequence of joinpoints as if they were a single unit.

Lets remark that replacing the joinpoint return value is not a feature exclusive to around advices and interceptors. An after advice can also return a value (for more on this, refer to the Return Types example).

Unrelated Before and After Advices

Despite the application of before and after advices shown in this example, these advices can also be used in an unrelated, independent manner.

An aspect whose code needs to run only before the joinpoint execution should contain only before advices. The same applies to an aspect that needs to execute after a joinpoint execution, regarding after advices. Furthermore, an aspect that provides functionalities that need to be run before a joinpoint execution, and functionalities that need to be run after, should contain a mix of unrelated before and after advices.

Examples of unrelated before and after advices will be shown in the next examples of this tutorial.

Applying MutexAspect

In our example, we apply MutexAspect to a classic example of synchronization and concurrent programming application.

Suppose you have bank accounts and operations on accounts can be performed concurrently. If we don't synchronize those operations we can end up with an invalid account balance.

An example would be two operations on account A. One of them is a $30.00 deposit, while the other one is a $10.00 withdrawal. If these operations are run in an unsynchronized manner, like follows:

The final balance of the account would be $40.00, and the deposit of $50.00 would have been completely ignored, since its final balance has been overwritten by the withdrawal operation.

Running

To compile and run (for further detail, refer to our Compiling and Running Examples Guide):

  $ ant run.aopc

It will javac the files and then run the AOPC precompiler to manipulate the bytecode, then finally run the example. The output should be similar to this:

_run.aopc:

     [java] SETUP
     [java] =====
     [java] Creating account 'A' with initial balance of $30.0
     [java] Creating account 'B' with initial balance of $50.0
     [java] Creating account 'C' with initial balance of $0.0

     [java] TRANSACTIONS
     [java] ============
     [java] >>> Retrieved concurrency lock
     [java] Depositing US$ 50.00 to account A
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Transfering US$ 100.00 from account B to account C
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 10.00 from account A
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Transfering US$ 89.11 from account C to account A
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Depositing US$ 51.00 to account B
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 0.11 from account C
     [java] <<< Releasing concurrency lock
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 5.00 from account B
     [java] <<< Releasing concurrency lock

     [java] FINAL BALANCE
     [java] ===== =======
     [java] A: US$ 159.11
     [java] C: US$ 10.78
     [java] B: -US$ 4.00

You can add accounts and transactions to this example application by editing the input.txt file.