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 sections walks through implementing aspects.

2.2. Invocation Object

Invocation objects are the runtime encapsulation of their joinpoint. They contain runtime information of their joinpoint (args, java.lang.reflect.*, etc..), and they also drive the flow of aspects.

Table 2.1.  Invocation class types

Class Description
org.jboss.aop.joinpoint.MethodInvocation Method execution. Created and used when a method is intercepted.
org.jboss.aop.joinpoint.ConstructorInvocation Constructor execution. Created and used when a constructor is intercepted.
org.jboss.aop.joinpoint.FieldInvocation Field execution. This is an abstract base class that encapsulates field access.
org.jboss.aop.joinpoint.FieldReadInvocation Field read access. Extends FieldInvocation. Created when a field is read.
org.jboss.aop.joinpoint.FieldWriteInvocation Field modification. Extends FieldInvocation. Created when a field is written to.
org.jboss.aop.joinpoint.MethodCalledByMethod Caller pointcuts. This invocation object is allocated when you are using "call" pointcut expressions. This particular class encapsulates a method that is calling another method so that you can access the caller and callee.
org.jboss.aop.joinpoint.MethodCalledByConstructor Caller pointcuts. This invocation object is allocated when you are using "call" pointcut expressions. This particular class encapsulates a constructor that is calling another method so that you can access the caller and callee.
org.jboss.aop.joinpoint.ConstructorCalledByMethod Caller pointcuts. This invocation object is allocated when you are using "call" pointcut expressions. This particular class encapsulates a method that is calling a constructor so that you can access the caller and callee.
org.jboss.aop.joinpoint.ConstructorCalledByConstructor Caller pointcuts. This invocation object is allocated when you are using "call" pointcut expressions. This particular class encapsulates a constructor that is calling a constructor so that you can access the caller and callee.

2.3. 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.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.4. 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.

Method names can be overloaded for different invocation types. 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 concreate invocation type.

public class Aspect
 {
   public Object trace(MethodInvocation invocation) throws Throwable {
      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()");
      }
   }
}

2.5. Resolving Annotations

JBoss AOP provides an abstraction for resolving JDK 5.0 annotations (and JDK 1.4 annotations if you use our Annotation Compiler). 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);
}