Finally Advices

Overview

The fifth type of adivce that JBoss AOP provides is the finally advice. In this example we will see how to write finally advices and when they are invoked.

Advices Invocation in JBoss AOP

When intercepting a joinpoint, one can write after and after-throwing advices to perform an interception after the joinpoint execution. However, the execution of these advices is not assured. If the joinpoint executes normally, an after advice will be invoked, but the after-throwing one will not. On the other hand, when the joinpoint throws an exception, all after advices are skipped while advices of after-throwing type will be invoked.

Finally advices are a mixture of after and after-throwing advices. As in the case of both advices, finally advices are also invoked after the joinpoint execution. But finally advices are invoked after joinpoint execution independently of its outcome. Since finally advices are invoked from inside a finally block, they will always be executed.

To understand better this mechanism, refer to the following:


0   try
1   {
2      call before advices
3      call around advices -> joinpoint
4      call after advices
6   }
7   catch(Throwable t)
8   {
9      call after-throwing advices
10     throw t;
11  }
12  finally
13  {
14     call finally advices
15  }

This is a simplified view of how JBoss AOP invokes the several types of advices. Before advices are executed before everything else. Next, around advices are called. Since around advices are responsible for triggering the joinpoint execution, this execution is wrapped in line 3. After advices are called next. After-throwing advices follow inside a catch block and, finally advices, inside a finally block. As we can see, the advices that are called after a joinpoint execution depend on how the joinpoint returns. If the joinpoint throws an exception, after advices will be skipped and the control flow will be directed to the catch block, where after-throwing advices are invoked. And, after that, we follow to the finally block, where finally advices are executed. Lastly, the Throwable t is rethrown. But if, on the other hand, the joinpoint execution returns normally, the control flow will follow from around to after advices. Next, the catch block is skipped and the finally block executed.

Writing Finally Advices

Finally advices must follow the annotated-parameters signature:

public <return-value> <any-method-name>(@<Annotation> <any type> arg0, @<Annotation> <any type> arg1, ... , @<Annotation> <any type> argN) throws <Exception1>,<Exception2>,...,<ExceptionN>

The annotated parameters can be of all types previously seen.

To bind a finally advice, just use the finally tag as below:

<bind pointcut="execution(* POJO->*(..))">
      <finally name="finallyAdvice" aspect="Aspect"/>
</bind>

Mutex Example Revisited

To illustrate the usability of finally advices, lets revisit the example of before/after advices. In that example, we wrote a MutexAspect to assure that several concurrent bank transactions could be run in a synchronized manner. The MutexAspect was composed by a pair of related before/after advices. The before advice would retrieve the mutex lock, that would be later released by the after advice:

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");
      }
   }
}
___________________________

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

But after advices are not assured to be executed always. If an exception is thrown during a transaction execution, the after advice will not be invoked, impeding the lock from being released, and locking the other threads. To avoid that situation, we should replace afterAdvice by a finally advice. Differently from the first, the finally advice will be executed always, avoiding a deadlock caused by an exception in one thread. Replacing the after advice in the example above is simple:

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

   public void beforeAdvice(@JoinPoint JoinPointBean 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 finallyAdvice(@JoinPoint JoinPointBean joinPoint)
   {
      synchronized(lock)
      {
         locked = false;
         lock.notifyAll();
         System.out.println("<<< Releasing concurrency lock");
      }
   }
}
___________________________

<bind pointcut="execution(protected void $instanceof{Transaction}->execute())">
   <before name="beforeAdvice" aspect="MutexAspect"/>
   <finally name="finallyAdvice" aspect="MutexAspect"/>
</bind>

To show the finally mechanism in action, we slightly changed the bank application, by throwing an exception when a transaction results in an negative balance.

@Return and @Thrown Annotated-Paramaters

In the same way as other advices, finally advices can receive annotated-parameters. The semantics of those parameters have been explained in the annotated-parameters and after-throwing examples.

Besides everything we have seen, there are some subtleties regarding @Return and @Thrown. Firstly, if a finally advice that receives a @Thrown parameter intercepts a joinpoint that returns normally, without errors, this parameter value will be null. An example of this follows:

public class FinallyAspect
{
   public void finallyAdvice1(@Thrown Throwable thrownException)
   {
      System.out.println(">>> finallyAdvice1 Exception has been thrown: " + (thrownException != null));
   }
}
___________________________

<bind pointcut="execution(private Account Bank->getAccount(java.lang.String))">
   <finally name="finallyAdvice1" aspect="FinallyAspect"/>
</bind>

The finally advice above intercepts Bank.getAccount(String) method, that throws a NoSuchAccountException if the account name is invalid. It prints a message telling whether Bank.getAccount(String) threw an exception, which happened only if thrownException is not null. Notice that the @Thrown-annotated advice parameter above is not of type NoSuchAccountException, it is of type Throwable. The reason for this is that @Thrown-annotated parameters must always be of type Throwable when declared at a finally advice.

A second consideration we need to make is that, if a finally advice receives a @Return parameter, the use of @Thrown is compulsory. This is so because the @Thrown parameter indicates the validity of the @Return parameter. This way:

The following advice also intercepts Bank.getAccount(String) method execution, but this time it prints the result if no exception has been thrown.

public class FinallyAspect
{
   public void finallyAdvice2(@Return Account account,
         @Thrown Throwable thrownException, @Arg String queriedName)
   {
      if (thrownException == null)
      {
         System.out.println(">>> finallyAdvice2 Account retrieved: " + account);
      }
      else
      {
         System.out.println(">>> finallyAdvice2 Account '" + queriedName +
               "' not found");
      }
   }
}
___________________________

<bind pointcut="execution(private Account Bank->getAccount(java.lang.String))">
   <finally name="finallyAdvice2" aspect="FinallyAspect"/>
</bind>

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] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: A: US$ 30,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: A: US$ 30,00
     [java] >>> finallyAdvice1 Exception has been thrown: true
     [java] >>> finallyAdvice2 Account 'D' not found
     [java] ERROR invalid transaction: Account named 'D' does not exist.
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: B: US$ 50,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: B: US$ 50,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: C: US$ 0,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: C: US$ 0,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: A: US$ 30,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: C: US$ 0,00
     [java] >>> finallyAdvice1 Exception has been thrown: false
     [java] >>> finallyAdvice2 Account retrieved: B: US$ 50,00

     [java] TRANSACTIONS
     [java] ============
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 10,00 from account A
     [java] <<< Releasing concurrency lock
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Transfering US$ 100,00 from account B to account C
     [java] <<< Releasing concurrency lock
     [java] ERROR can't perform operation because it results in: negative balance not allowed -US$ 100,00
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Depositing US$ 50,00 to account A
     [java] <<< Releasing concurrency lock
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Transfering US$ 89,11 from account C to account A
     [java] <<< Releasing concurrency lock
     [java] ERROR can't perform operation because it results in: negative balance not allowed -US$ 89,11
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Depositing US$ 51,00 to account B
     [java] <<< Releasing concurrency lock
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 0,11 from account C
     [java] <<< Releasing concurrency lock
     [java] ERROR can't perform operation because it results in: negative balance not allowed -US$ 0,11
     [java] ---
     [java] >>> Retrieved concurrency lock
     [java] Withdrawing US$ 5,00 from account B
     [java] <<< Releasing concurrency lock
     [java] ---

     [java] FINAL BALANCE
     [java] ===== =======
     [java] A: US$ 70,00
     [java] C: US$ 0,00
     [java] B: US$ 96,00


As before, you can add accounts and transactions to this example application by editing the input.txt file.

Note that you can retrieve different values by running this example, since the order of operations varies (they are concurrent after all) and an operation with negative outcome will be invalidated.