Chapter 4. XML Bindings

4.1. Intro

In the last sections you saw how to code aspects and how pointcut expressions are formed. This chapter puts it all together. There are two forms of bindings for advices, mixins, and introductions. One is XML which will be the focus of this chapter. The Annotated Bindings chapter discusses how you can replace XML with JDK 5.0 annotations.

4.2. Resolving XML

JBoss AOP resolves pointcut and advice bindings at runtime. So, bindings are a deployment time thing. How does JBoss AOP find the XML files it needs at runtime? There are a couple of ways.

4.2.1. Standalone XML Resolving

When you are running JBoss AOP outside of the application server there are a few ways that the JBoss AOP framework can resolve XML files.

  • jboss.aop.path This is a system property that is a ';' (Windows) or ':' (Unix) delimited list of XML files and/or directories. If the item in the list is a directory, JBoss AOP will load any xml file in those directories with the filename suffix -aop.xml
  • META-INF/jboss-aop.xml Any JAR file in your CLASSPATH that has a jboss-aop.xml file in the META-INF/ will be loaded. JBoss AOP does a ClassLoader.getResources("META-INF/jboss-aop.xml") to obtain all these files.

4.2.2. Application Server XML Resolving

When you are running JBoss AOP outside of the application server there are a few ways that the JBoss AOP framework can resolve XML files. One is to place an XML file with the suffix *-aop.xml in the deploy directory. The other way is to JAR up your classes and provide a META-INF/jboss-aop.xml file in this JAR. This JAR file must be suffixed with .aop and placed within the deploy/ directory or embedded as a nested archive.

4.3. XML DTD

<?xml version='1.0' encoding='UTF-8' ?>

<!ELEMENT aop (interceptor|introduction|metadata-loader|metadata|
               stack|aspect|pointcut|pluggable-pointcut|bind|
               prepare|cflow-stack|dynamic-cflow|annotation-introduction|typedef)+>

<!ELEMENT interceptor ANY>
<!ATTLIST interceptor name CDATA #IMPLIED>
<!ATTLIST interceptor class CDATA #IMPLIED>
<!ATTLIST interceptor factory CDATA #IMPLIED>
<!ATTLIST interceptor scope (PER_VM|PER_CLASS|PER_INSTANCE|PER_JOINPOINT) "PER_VM">

<!ELEMENT aspect ANY>
<!ATTLIST aspect name CDATA #IMPLIED>
<!ATTLIST aspect class CDATA #IMPLIED>
<!ATTLIST aspect factory CDATA #IMPLIED>
<!ATTLIST aspect scope (PER_VM|PER_CLASS|PER_INSTANCE|PER_JOINPOINT) "PER_VM">

<!ELEMENT introduction (mixin*,interfaces)>
<!ATTLIST introduction class CDATA #IMPLIED>
<!ATTLIST introduction expr CDATA #IMPLIED>
<!ELEMENT mixin (interfaces, class, construction?)>
<!ATTLIST mixin transient (true|false) "true">
<!ELEMENT interfaces (#PCDATA)>
<!ELEMENT class (#PCDATA)>
<!ELEMENT construction (#PCDATA)>

<!ELEMENT metadata-loader EMPTY>
<!ATTLIST metadata-loader tag CDATA #REQUIRED>
<!ATTLIST metadata-loader class CDATA #REQUIRED>

<!ELEMENT metadata ANY>
<!ATTLIST metadata tag CDATA #REQUIRED>
<!ATTLIST metadata class CDATA #REQUIRED>

<!ELEMENT stack (interceptor|interceptor-ref|stack-ref|advice)+>
<!ATTLIST stack name CDATA #REQUIRED>

<!ELEMENT interceptor-ref EMPTY>
<!ATTLIST interceptor-ref name CDATA #REQUIRED>

<!ELEMENT stack-ref EMPTY>
<!ATTLIST stack-ref name CDATA #REQUIRED>

<!ELEMENT advice EMPTY>
<!ATTLIST advice name CDATA #REQUIRED>
<!ATTLIST advice aspect CDATA #REQUIRED>

<!ELEMENT pointcut EMPTY>
<!ATTLIST pointcut name CDATA #REQUIRED>
<!ATTLIST pointcut expr CDATA #REQUIRED>

<!ELEMENT prepare EMPTY>
<!ATTLIST prepare expr CDATA #REQUIRED>

<!ELEMENT pluggable-pointcut ANY>
<!ATTLIST pluggable-pointcut name CDATA #REQUIRED>
<!ATTLIST pluggable-pointcut class CDATA #REQUIRED>

<!ELEMENT bind (interceptor|interceptor-ref|stack-ref|advice)+>
<!ATTLIST bind name CDATA #IMPLIED>
<!ATTLIST bind pointcut CDATA #REQUIRED>
<!ATTLIST bind cflow CDATA #IMPLIED>

<!ELEMENT cflow-stack (called|not-called)+>
<!ATTLIST cflow-stack name CDATA #REQUIRED>

<!ELEMENT called EMPTY>
<!ATTLIST called expr CDATA #REQUIRED>
<!ELEMENT not-called EMPTY>
<!ATTLIST not-called expr CDATA #REQUIRED>


<!ELEMENT dynamic-cflow EMPTY>
<!ATTLIST dynamic-cflow name CDATA #REQUIRED>
<!ATTLIST dynamic-cflow class CDATA #REQUIRED>

<!ELEMENT annotation-introduction (#PCDATA)>
<!ATTLIST annotation-introduction expr CDATA #REQUIRED>
<!ATTLIST annotation-introduction invisible (true|false) #REQUIRED>

<!ELEMENT typedef EMPTY>
<!ATTLIST typedef name CDATA #REQUIRED>
<!ATTLIST typedef expr CDATA #REQUIRED>
            
         

4.4. aspect

The <aspect> tag specifies to the AOP container to declare an aspect class. It is also used for configuring aspects as they are created and defining the scope of the aspects instance.

4.4.1. Basic Definition

<aspect class="org.jboss.MyAspect"/>

In a basic declaration you specify the fully qualified class name of the aspect. If you want to reference the aspect at runtime through the AspectManager, the name of the aspect is the same name as the class name. The default Scope of this aspect is PER_VM. Another important note is that aspect instances are created on demand and NOT at deployment time.

4.4.2. Scope

<aspect class="org.jboss.MyAspect" scope="PER_VM"/>

The scope attribute defines when an instance of the aspect should be created. An aspect can be created per vm, per class, per instance, or per joinpoint.

Table 4.1.  Aspect instance scope

Name Description
PER_VM One and only instance of the aspect class is allocated for the entire VM.
PER_CLASS One and only instance of the aspect class is allocated for a particular class. This instance will be created if an advice of that aspect is bound to that particular class.
PER_INSTANCE An instance of an aspect will be created per advised object instance. For instance, if a method has an advice attached to it, whenever an instance of that advised class is allocated, there will also be one created for the aspect.
PER_JOINPOINT An instance of an aspect will be created per joinpoint advised. If the joinpoint is a static member (constructor, static field/method), then there will be one instance of the aspect created per class, per joinpoint. If the joinpoint is a regular non-static member, than an instance of the aspect will be created per object instance, per joinpoint.
PER_CLASS_JOINPOINT An instance of an aspect will be created per advised joinpoint. The aspect instance is shared between all instances of the class (for that joinpoint).

4.4.3. Configuration

<aspect class="org.jboss.SomeAspect">
     <attribute name="SomeIntValue">55</attribute>
     <advisor-attribute name="MyAdvisor"/>
     <instance-advisor-attribute name="MyInstanceAdvisor"/>
     <joinpoint-attribute name="MyJoinpoint"/>
</aspect>

Aspects can be configured by default using a Java Beans style convention. The <attribute> tag will delegate to a setter method and convert the string value to the type of the setter method.

Table 4.2.  Supported Java Bean types

primitive types (int, float, String, etc...)
java.lang.Class
java.lang.Class[]
java.lang.String[]
java.math.BigDecimal
org.w3c.dom.Document
java.io.File
java.net.InetAddress
java.net.URL
javax.management.ObjectName (if running in JBoss)

Besides types, you can also inject AOP runtime constructs into the aspect. These types of attributes are referenced within XML under special tags. See the table below.

Table 4.3.  Injecting AOP runtime constructs

<advisor-attribute>org.jboss.aop.Advisor
<instance-advisor-attribute>org.jboss.aop.InstanceAdvisor
<joinpoint-attribute>org.jboss.aop.joinpoint.Joinpoint

4.4.3.1. Names

If there is no name attribute defined, the name of the aspect is the same as the class or factory attribute value.

4.4.3.2. Example configuration

<aspect class="org.jboss.SomeAspect">
        <attribute name="SomeIntValue">55</attribute>
        <advisor-attribute name="MyAdvisor"/>
        <instance-advisor-attribute name="MyInstanceAdvisor"/>
        <joinpoint-attribute name="MyJoinpoint"/>
</aspect>

The above example would would need a class implemented as follows:

public class SomeAspect {
   public SomeAspect() {}

   public void setSomeIntValue(int val) {...}
   public void setMyAdvisor(org.jboss.aop.Advisor advisor) {...}
   public void setMyInstanceAdvisor(org.jboss.aop.InstanceAdvisor advisor) {...}
   public void setMyJoinpoint(org.jboss.aop.joinpoint.Joinpoint joinpoin) {...}
}

4.4.4. Aspect Factories

<aspect name="MyAspect" factory="org.jboss.AspectConfigFactory" scope="PER_CLASS">
     <some-arbitrary-xml>value</some-arbitrary-xml>
</aspect>

If you do not like the default Java Bean configuration for aspects, or want to delegate aspect creation to some other container, you can plug in your own factory class by specifying the factory attribute rather than the class attribute. Any arbitrary XML can be specified in the aspect XML declaration and it will be passed to the factory class. Factories must implement the org.jboss.aop.advice.AspectFactory interface.

4.5. interceptor

<interceptor class="org.jboss.MyInterceptor" scope="PER_VM"/>
<interceptor class="org.jboss.SomeInterceptor">
     <attribute name="SomeIntValue">55</attribute>
     <advisor-attribute name="MyAdvisor"/>
     <instance-advisor-attribute name="MyInstanceAdvisor"/>
     <joinpoint-attribute name="MyJoinpoint"/>
</interceptor>
<interceptor name="MyAspect" factory="org.jboss.InterceptorConfigFactory" scope="PER_CLASS">
  <some-arbitrary-xml>value</some-arbitrary-xml>
</interceptor>

Interceptors are defined in XML the same exact way as aspects are. No difference except the tag. If there is no name attribute defined, the name of the interceptor is the same as the class or factory attribute value.

4.6. bind

<bind pointcut="execution(void Foo->bar())">
     <interceptor-ref name="org.jboss.MyInterceptor/>
     <advice name="trace" aspect="org.jboss.MyAspect"/>
</bind>

In the above example, the MyInterceptor interceptor and the trace method of the MyAspect class will be executed when the Foo.bar method is invoked.

bind

bind tag is used to bind an advice of an aspect, or an interceptor to a specific joinpoint. The pointcut attribute is required and at least an advice or interceptor-ref definition.

interceptor-ref

The interceptor-ref tag must reference an already existing interceptor XML definition. The name attribute should be the name of the interceptor you are referencing.

advice

The advice tag takes a name attribute that should map to a method within the aspect class. The aspect attribute should be the name of the aspect definition.

4.7. stack

Stacks allow you to define a predefined set of advices/interceptors that you want to reference from within a bind element.

<stack name="stuff">
      <interceptor class="SimpleInterceptor1" scope="PER_VM"/>
      <advice name="trace" aspect="org.jboss.TracingAspect"/>
      <interceptor class="SimpleInterceptor3">
           <attribute name="size">55</attribute>
      </interceptor>
</stack>

After defining the stack you can then reference it from within a bind element.

<bind pointcut="execution(* POJO->*(..))">
       <stack-ref name="stuff"/>
</bind>

4.8. pointcut

The pointcut tag allows you to define a pointcut expression, name it and reference it within any binding you want. It is also useful to publish pointcuts into your applications to that others have a clear set of named integration points.

<pointcut name="publicMethods" expr="execution(public * *->*(..))"/>
<pointcut name="staticMethods" expr="execution(static * *->*(..))"/>

The above define two different pointcuts. One that matches all public methods, the other that matches the execution of all static methods. These two pointcuts can then be referenced within a bind element.

<bind pointcut="publicMethods AND staticMethods">
      <interceptor-ref name="tracing"/>
</bind>
         

4.9. introduction

4.9.1. Interface introductions

The introduction tag allows you to force an existing Java class to implement a particular defined interface.

<introduction class="org.acme.MyClass">
    <interfaces>java.io.Serializable</interfaces>
</introduction>

The above declaration says that the org.acme.MyClass class will be forced to implement java.io.Serializable. The class attribute can take wildcards but not boolean expressions. If you need more complex type expressions, you can use the expr attribute instead.

<introduction expr="has(* *->@test(..)) OR class(org.acme.*)">
    <interfaces>java.io.Serializable</interfaces>
</introduction>

The expr can be any type expression allowed in a typedef expression

4.9.2. Mixins

When introducing an interface you can also define a mixin class which will provide the implementation of that interface.

<introduction class="org.acme.MyClass">
      <mixin>
         <interfaces>
            java.io.Externalizable
         </interfaces>
         <class>org.acme.ExternalizableMixin</class>
         <construction>new org.acme.ExternalizableMixin(this)</construction>
      </mixin>
   </introduction>
interfaces

defines the list of interfaces you are introduction

class

The type of the mixin class.

construction

The construction statement allows you to specify any Java code to create the mixin class. This code will be embedded directly in the class you are introducing to so this works in the construction statement.

4.10. annotation-introduction

Annotation introductions allow you to embed an annotation within a the class file of the class. You can introduce an annotation to a class, method, field, or constructor.

<annotation-introduction expr="constructor(POJO->new())">
      @org.jboss.complex (ch='a', string="hello world", flt=5.5, dbl=6.6, shrt=5, lng=6, integer=7, bool=true, annotation=@single("hello"), array={"hello", "world"}, clazz=java.lang.String)
</annotation-introduction>

The expr attribute takes method(), constructor(), class(), or field(). Within those you must define a valid expression for that construct. The following rules must be followed for the annotation declaration:

  • Any annotation, Class or Enum referenced, MUST be fully qualified.

4.11. cflow-stack

Control flow is a runtime construct. It allows you to specify pointcut parameters revolving around the call stack of a Java program. You can do stuff like, if method A calls method B calls Method C calls Method D from Constructor A, trigger this advice. In defining a control flow, you must first paint a picture of what the Java call stack should look like. This is the responsibility of the cflow-stack.

<cflow-stack name="recursive2">
      <called expr="void POJO->recursive(int)"/>
      <called expr="void POJO->recursive(int)"/>
      <not-called expr="void POJO->recursive(int)"/>
</cflow-stack>

A cflow-stack has a name and a bunch of called and not-called elements that define individual constructor or method calls with a Java call stack. The expr attribute must be a method or constructor expression. called states that the expr must be in the call stack. not-called states that there should not be any more of the expression within the stack. In the above example, the cflow-stack will be triggered if there are two and only two calls to the recursive method within the stack. Once the cflow-stack has been defined, it can then be referenced within a bind element through the cflow attribute. Boolean expressions are allowed here as well.

<bind pointcut="execution(void POJO->recursive(int))" cflow="recursive2 AND !cflow2">
      <interceptor class="SimpleInterceptor"/>
</bind>

4.12. typedef

<typedef name="jmx" expr="class(@org.jboss.jmx.@MBean) OR
                          has(* *->org.jboss.jmx.@ManagedOperation) OR
                          has(* *->org.jboss.jmx.@ManagedAttribute)"/>

typedefs allow you to define complex type expressions and then use then pointcut expressions. In the above example, we're defining a class that is tagged as @Mbean, or has a method tagged as @ManagedOperaion or @ManagedAttribute. The above typedef could then be used in a pointcut, introduction, or bind element

<pointcut name="stuff" expr="execution(* $typedef{jmx}->*(..))"/>
<introduction expr="class($typedef{jmx})">

4.13. dynamic-cflow

dynamic-cflow allows 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 Dynamic CFlow 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. Create a dynamic cflow class, then you must declare it with XML so that it can be used in bind expressions.

<dynamic-cflow name="simple" class="org.jboss.SimpleDynamicCFlow"/>

You can then use it within a bind

<bind expr="execution(void Foo->bar())" cflow="simple">

4.14. prepare

The prepare tag allows you to define a pointcut expression. Any joinpoint that matches the expression will be aspectized and bytecode instrumented. This allows you to hotdeploy and bind aspects at runtime as well as to work with the per instance API that every aspectized class has. To prepare something, just define a pointcut expression that matches the joinpoint you want to instrument.

<prepare expr="execution(void Foo-bar())"/>

4.15. metadata

You can attach untyped metadata that is stored in org.jboss.aop.metadata.SimpleMetaData structures within the org.jboss.aop.Advisor class that manages each aspectized class. The XML mapping has a section for each type of metadata. Class, method, constructor, field, and defaults for the whole shabang. Here's an example:

<metadata tag="testdata" class="org.jboss.test.POJO">
      <default>
         <some-data>default value</some-data>
      </default>
      <class>
         <data>class level</data>
      </class>
      <constructor expr="POJOConstructorTest()">
         <some-data>empty</some-data>
      </constructor>
      <method expr="void another(int, int)">
         <other-data>half</other-data>
      </method>
      <field name="somefield">
         <other-data>full</other-data>
      </field>
</metadata>

Any element can be defined under the class, default, method, field, and constructor tags. The name of these elements are used as attribute names in SimpleMetaData structures. The tag attribute is the name used to reference the metadata within the Advisor, or Invocation lookup mechanisms.

4.16. metadata-loader

<metadata-loader tag="security" class="org.jboss.aspects.security.SecurityClassMetaDataLoader"/>

If you need more complex XML mappings for untyped metadata, you can write your own metadata binding. The tag attribute is used to trigger the loader. The loader class must implement the org.jboss.aop.metadata.ClassMetaDataLoader interface.

public interface ClassMetaDataLoader
{
   public ClassMetaDataBinding importMetaData(Element element, String name,
                                              String tag, String classExpr) throws Exception;

   public void bind(ClassAdvisor advisor, ClassMetaDataBinding data,
                    CtMethod[] methods, CtField[] fields, CtConstructor[] constructors) throws Exception;

   public void bind(ClassAdvisor advisor, ClassMetaDataBinding data,
                    Method[] methods, Field[] fields, Constructor[] constructors) throws Exception;
}

Any arbitrary XML can be in the metadata element. The ClassMetaDataBinding.importMetaData method is responsible for parsing the element and building ClassMetaDataBinding structurs which are used in the precompiler and runtime bind steps. Look at the SecurityClassMetaDataLoader code shown above for a real concrete example.

4.17. precedence

Precedence allows you to impose an overall relative sorting order of your interceptors and advices.

         <precedence>
            <interceptor-ref name="org.acme.Interceptor"/>
            <advice aspect="org.acme.Aspect" name="advice1"/>
            <advice aspect="org.acme.Aspect" name="advice2"/>
         </precedence>         
      

This says that when a joinpoint has both org.acme.Interceptor and org.acme.Aspect.advice() bound to it, org.acme.Interceptor must always be invoked before org.acme.Aspect.advice1() which must in turn be invoked before org.acme.Aspect.advice2(). The ordering of interceptors/advices that do not appear in a precedence is defined by their ordering for the individual bindings or intercerceptor stacks.

4.18. declare

You can declare checks to be enforced at instrumentation time. They take a pointcut and a message. If the pointcut is matched, the message is printed out.

4.18.1. declare-warning

   <declare-warning expr="class($instanceof{VehicleDAO}) \
            AND !has(public void *->save())">
         All VehicleDAO subclasses must override the save() method.
   </declare-warning> 

The above declaration says that if any subclass of VehicleDAO does not implement a noargs save() method, a warning with the supplied message should be logged. Your application will continue to be instrumented/run (since we are using declare-warning in this case).

4.18.2. declare-error

      <declare-error expr="call(* org.acme.businesslayer.*->*(..)) \
            AND within(org.acme.datalayer.*)">
      Data layer classes should not call up to the business layer
   </declare-error> 

The above declaration says that if any classes in the datalayer call classes in the business layer of your application, an error should be thrown. Instumentation/execution of your application will stop.