Chapter 2. Implementing Aspects

2.1. Overview

JBoss AOP is a 100% pure Java framework. All your AOP constructs are defined as pure Java classes and bound to your application code via XML or by annotations. This Chapter walks through implementing aspects.

2.2. Aspect Class

The Aspect Class is a plain Java class that can define zero or more advices, pointcuts, and/or mixins.

public class Aspect
 {
   public Object trace(Invocation invocation) throws Throwable {
      try {
         System.out.println("Entering anything");
         return invocation.invokeNext(); // proceed to next advice or actual call
      } finally {
         System.out.println("Leaving anything");
      }
   }
}

The example above is of an advice trace that traces calls to any type of joinpoint. Notice that Invocation objects are the runtime encapsulation of joinpoints. The method invocation.invokeNext() is used to drive the advice chain. It either calls the next advice in the chain, or does the actual method or constructor invocation.

2.3. Advice Methods

For basic interception, any method that follows the form:

Object methodName(Invocation object) throws Throwable

can be an advice. The Invocation.invokeNext() method must be called by the advice code or no other advice will be called, and the actual method, field, or constructor invocation will not happen.

JBoss AOP provides five types of advice: before, around, after, finally and after-throwing. The advice sginature above is the default one for an around advice. Advices types, signature rules and overloading will be covered in Chapter 4, Advices.

2.4. Interceptors

Interceptors are a special type of aspect that contains only one advice. This advice has its signature defined by an interface, org.jboss.aop.advice.Interceptor:

public interface Interceptor
{
   public String getName();
   
   public Object invoke(Invocation invocation) throws Throwable;
}

The method invoke(Invocation) is the unique advice contained in an interceptor. The method getName() is used for identification in the JBoss AOP framework. So, this method must return a name that is unique in the whole system. It is only really used for aspects added to the InstanceAdvisor as shown in Section 7.2, “Per Instance AOP”.

2.5. Resolving Annotations

JBoss AOP provides an abstraction for resolving annotations. In future versions of JBoss AOP, there will be a way to override annotation values on a per thread basis, or via XML overrides, or even provide VM and cluster wide defaults for annotation values. Also if you want to write a truly generic advice that takes the base Invocation type, you can still get the annotation value of the method, constructor, or field you're invoking on by calling this method:

Object resolveAnnotation(Class annotation);

That's just resolving for resolving member annotations. If your aspect needs to resolve class level annotations then this method should be called:

Object resolveClassAnnotation(Class annotation)

2.6. Metadata

2.6.1. Resolving XML Metadata

Untyped metadata can be defined within XML files and bound to org.jboss.aop.metadata.SimpleMetaData structures. This XML data can be attached per method, field, class, and constructor. To resolve this type of metadata, the Invocation object provides a method to abstract out where the metadata comes from.

Object getMetaData(Object group, Object attr)

When this method is called, the invocation will look for metadata in this order:

  1. First it looks in the Invocation's metadata ( SimpleMetaData getMetaData())

  2. Next it looks in org.jboss.aop.metadata.ThreadMetaData.instance(). ThreadMetaData allows you to override metadata for the whole thread. The metadata is managed by a ThreadLocal. ThreadMetaData is used by every single invocation object at runtime.

  3. Next it looks in either org.jboss.aop.Advisor.getMethodMetaData(), Advisor.getConstructorMetaData(), or Advisor.getFieldMetaData() depending on the invocation type.

  4. Next it looks in either Advisor.getDefaultMetaData().

2.6.2. Attaching Metadata

You can attach untyped metadata to the invocation object, or even to the response. This allows advices to pass contextual data to one another in the incoming invocation or outgoing response for instance if you had advices running on a remote client that wanted to pass contextual data to server-side aspects. This method on invocation gets you access to a org.jboss.aop.metadata.SimpleMetaData instance so that you can attach or read data.

SimpleMetaData getMetaData()

SimpleMetaData has three types of metadata, AS_IS, MARSHALLED, and TRANSIENT. This allows you to specify whether or not metadata is marshalled across the wire. TRANSIENT says, attached metadata should not be sent across the wire. MARSHALLED is for classloader sensitive contextual data. AS_IS doesn't care about classloaders. Read the Javadocs for more information.

To piggyback and read metadata on the invocation response, two methods are provided. One to attach data one to read data.

Object getResponseAttachment(Object key);
void addResponseAttachment(Object key, Object value);
         

2.7. Mixin Definition

Mixins are a type of introduction in which you can do something like C++ multiple inheritance and force an existing Java class to implement a particular interface and the implementation of that particular interface is encapsulated into a particular class called a mixin.

Mixin classes have no restrictions other than they must implement the interfaces that you are introducing.

2.8. Dynamic CFlow

Dynamic CFlows allow you to define code that will be executed that must be resolved true to trigger positive on a cflow test on an advice binding. (See <cflow-stack> for more information). The test happens dynamically at runtime and when combined with a pointcut expression allows you to do runtime checks on whether a advice binding should run or not. To implement a dynamic CFlow you just have to implement the simple org.jboss.aop.pointcut.DynamicCFlow interface. You can then use it within cflow expressions. (See XML or Annotations)

public interface DynamicCFlow
{
   boolean shouldExecute(Invocation invocation);
}