JBoss Metadata

Overview

JBoss Metadata is alot like annotations except it is driven and applied through XML. JBossAOP has the ability to define metadata in XML. You can use the same pointcut annotation expressions to reference JBossAOP XML metadata expressions. This metadata is different than regular JDK annotations in that they are untyped.

Example code

The example code applies 2 separate interceptors via tags supplied in a Java source file. One of the interceptors is a tracing interceptor that is trigger by a @Trace annotation, the other is B2B functionality that will bill/charge a user for each access of the api. This is also triggered by an annotation.

Declaring Metadata

Open up metadata-aop.xml. This is the source file for where our annotations will be declared. This file is composed of a list of metadata tags. Each such tag must have two attributes: tag and class. The tag attribute is the name of the metadata, equivalent to the name of an annotation. The class attribute must contain a class expression. Every class matching that expression will be bound to the metadata. Take a look at the two first tags of metadata-aop.xml:
    <metadata tag="Trace" class="POJO">
      <constructor expr="POJO()"/>
   </metadata>
   <metadata tag="Billable" class="POJO">
      <constructor expr="POJO()">
         <amount>0.01</amount>
      </constructor>
   </metadata>

The first one represents a metadata @Trace and the second one represents the metadata @Billable. Both metadata will be applied to class POJO, as specified in the class attribute. The contents of a metadata tag indicate to which elements of POJO class this metadata should be applied. In the example, the metadata @Trace will be applied to the default constructor of POJO(). The metada @Billable will be also applied to the same constructor, and has an attribute, amount, whose value is 0.01. Following this same format you can define as many attributes and values you need. In similar ways, a metadata can be applied to the fields, methods and to the class itself, just like Java annotations. The next metadata tags apply metadata to constructors, methods and fields:

   <metadata tag="Trace" class="POJO">
      <field name="field"/>
      <constructor expr="POJO(int)"/>
      <method expr="void someMethod()"/>
   </metadata>
   <metadata tag="Billable" class="POJO">
      <constructor expr="POJO(int)">
         <amount>0.01</amount>
      </constructor>
      <method expr="void someMethod()">
         <amount>0.05</amountgt;
      </method>
   </metadata>
If we wanted to apply @Trace to POJO class, we could have added an empty class XML tag to a metadata element:
   <metadata tag="Trace" class="POJO">
      <class/>
   </metadata>
The example bellow states that every constructor, field, and method of POJO class will be bound to the @Trace metadata:
   <metadata tag="Trace" class="POJO">
      <default/>
   </metadata>

Annotations in pointcut expressions

Annotations can be referenced by an '@' sign in pointcut expressions. They can only be used in the class expressions for a method, field, or constructor for execution and caller pointcuts. They can also be used in substitute for 'new' in constructor land, and for a method or field name. Take a look at jboss-aop.xml

   <bind pointcut="execution(POJO->@Billable(..))">
       <interceptor class="BillingInterceptor"/>
   </bind>

   <bind pointcut="execution(* POJO->@Billable(..))">
       <interceptor class="BillingInterceptor"/>
   </bind>

The first binding above says that for every constructor tagged as @Billable apply the BillingInterceptor. The second binding states that for any method tagged as @Billable apply the BillingInterceptor. Let's now take a look at applying the tracing advice.

   <bind pointcut="all(@Trace)">
       <interceptor class="TraceInterceptor"/>
   </bind>

The above states that for any field, constructor, or method tagged as @Trace, apply the TraceInterceptor.

Accessing metadata at runtime

You can access metadata through the org.jboss.aop.Advised interface through the _getAdvisor() or _getInstanceAdvisor() methods, or you can use the indirection that the Invocation object provides you. You can use the Invocation object to resolve metadata based on the context of the execution. BillingInterceptor.java gives an example of this. This interceptor intercepts different kinds of things (methods and constructors), but it doesn't care about the thing it is intercepting, only the metadata.

   public Object invoke(Invocation invocation) throws Throwable
   {
      System.out.println("billing amount: $" + invocation.getMetaData("Billable", "amount"));
   }

Running

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

  $ ant run.aopc
It will run the annotationc compiler on the source files to generate metadata in metadata-aop.xml, then javac the files and then run the AOPC precompiler to manipulate the bytecode, then finally run the example. Note that there are two XML aop deployment descriptors: metadata-aop.xml and jboss-aop.xml. The System Property jboss.aop.path can accept a list of files delimited by the platform classpath separator. ';' on windows ':' on unix. Running the example should produce:

run.aopc:
     [java] --- new POJO(); ---
     [java] billing amount: $0.01
     [java] <<< Trace : executing constructor public POJO()
     [java] empty constructor
     [java] >>> Leaving Trace
     [java] --- new POJO(int); ---
     [java] billing amount: $0.01
     [java] <<< Trace : executing constructor public POJO(int)
     [java] int constructor
     [java] >>> Leaving Trace
     [java] --- pojo.someMethod(); ---
     [java] billing amount: $0.05
     [java] <<< Trace : executing method public void POJO.someMethod()
     [java] someMethod
     [java] >>> Leaving Trace
     [java] --- pojo.field = 55;  ---
     [java] <<< Trace : write field name: public int POJO.field
     [java] >>> Leaving Trace