Join point selection pattern language

AspectWerkz support a fine-grained pattern language for picking out join points.

You can utilize two types of wildcards when constructing your patterns:

  • * - which is used as a regular wildcard. Matches for example only one package level or one method parameter. Matches at least one character when used solely else match zero or more character.


  • .. - matches any sequence of characters that start and end with a ".", so it can be used to pick out all types in any subpackage. For example org.codehaus..* will match all classes in all subpackages starting from org.codehaus .
Note: you can only use the .. wildcard as the "last" thing specified. I.e. this is not possible: foo.bar..test.MyClass , but this is: foo.bar.. . The same thing holds for method parameters.

Combining the patterns

The patterns normally consists of a combination of a class and a method pattern or a class and a field pattern.

Example of a full method pattern:

< return_type_pattern > < package_and_class_pattern > . < method_name_pattern > ( < parameter_type_patterns > )

Example of a full field pattern:

< field_type_pattern > < package_and_class_pattern > . < field_name_pattern >

Class selections

For the class selections specify the full package name of the class along with some wildcards.

Examples:

  • foo.bar.* - will match

    foo.bar.FooBar2 as well as

    foo.bar.FooBear .


  • foo.*.FooBar - will match

    foo.bar.FooBar2 as well as

    foo.bear.FooBar but not

    foo.bear.FooBear .


  • foo.*.FooB* - will match

    foo.bar.FooBar2 as well as

    foo.bear.FooBear as well as

    foo.bear.FooB .


  • foo.. - will match

    all classes in all packages starting with foo.

Method selections

The methods are selected by specifying a pattern that consists of:

  • return type
  • full name of method (including class and package)
  • parameter types

All method patterns must follow this structure:

< return_type > < full_method_name > ( < parameter_types > )

Examples

  • int foo.*.Bar.method() - will match

    int method() but not

    int method(int i) .


  • int *.method(*) - will match

    int Foo.method(int i) but not

    int Foo.method() .


  • int foo.*.*.method(*,int) - will match

    int method(String s, int i) as well as

    int method(int i1, int i2) .


  • int foo.*.Bar.method(..) - will match

    int method() as well as

    int method(String s, int i) as well as

    int method(int i, double d, String s, Object o) .


  • int foo.*.Bar.method(int,..) - will match

    int method(int) as well as

    int method(int i, String s) as well as

    int method(int i, double d, String s, Object o) .


  • int foo.*.Bar.method(java.lang.*) - will match

    int method(String s) as well as

    int method(StringBuffer sb) .


  • int foo.*.Bar.me*o*() - will match

    int method() as well as

    int metamorphosis() and int meo() but not

    int me() .


  • * foo.*.Bar.method() - will match

    int method() as well as

    java.lang.String method() .


  • java.lang.* foo.*.Bar.method() - will match

    java.lang.String Bar.method() as well as

    java.lang.StringBuffer Bar.method() .

Field selections

The fields are selected by specifying a pattern that consists of:

  • field type
  • full name of field (including class and package)

All field patterns must follow this structure:

< field_type > < full_field_name >

Examples

  • int foo.*.Bar.m_foo - will match

    int m_foo but not

    int s_foo or

    long m_foo .


  • * foo.*.Bar.m_foo - will match

    int m_foo as well as

    java.lang.String m_foo .


  • java.lang.* foo.*.Bar.m_foo - will match

    java.lang.String m_foo as well as

    java.lang.StringBuffer m_foo .


  • int foo.*.Bar.m_* - will match

    int m_foo as well as

    int m_bar .


  • int foo.*.Bar.m_*oo* - will match

    int m_foo as well as

    int m_looser as well as

    int m_oo .

Subtype patterns

It is possible to pick out all subtypes of a type with the "+" wildcard. The "+" wildcard follows immediately a type name pattern. So, while

* foo.Bar.*(..)

picks out all method call join points where an instance of exactly type Foo is constructed,

* foo.Bar+.*(..)

picks out all method call join points where an instance of any subtype of Foo (including Foo itself) is constructed.

Array type patterns

A type name pattern or subtype pattern can be followed by one or more sets of square brackets to make array type patterns. So Object[ ] is an array type pattern, and so is foo.bar.*[ ] [ ] .

Abbreviations

When picking out the return and parameter types it is possible to use predefined abbreviations for the classes in the java.lang.* and java.util.* packages. If you specify only the class name it will be mapped to the full class name for the class (you cannot use patterns in abbreviations).

Examples

You can use:

  • String instead of java.lang.String
  • List instead of java.util.List
  • but not String* instead of java.lang.String or java.lang.StringBuffer
  • and so on...

Apart from these abbreviations you always have to specify the fully qualified name of the class (along with the wildcards).

Passing parameters to advices

You also have the option of passing parameters to your advices. This can be very convenient if you want to reuse the same advice but with a different configuration. To pass a parameter to the advice you simply add a param tag to the advice definition, like this:

<advice-def ... >
    <param name="timeout" value="10"/>
</advice-def>

If you prefer the Runtime Attributes way of defining your advices:

/**
 * ...
 * @aspectwerkz.advice-param advice-ref=advices/MyAdvice
 *                           name=timeout
 *                           value=10
 */
 public class MyAdvice extends AroundAdvice {..}

You can pass as many parameters to your advices as you want.

At runtime you can then retrieve the parameters in your Advice like this:

String timeOut = getParameter("timeout");

Advice stacks

An advice stack lets you define a stack/chain with advices that you can refer to in your pointcuts. The order of the advices in the stack is the same order as they will be executed in.

Advice stacks can come in very handy when you have a bunch of advices that logically belongs together and are used in the same order at many places in the definition. If the order between the advices is critical then it is recommended that you arrange the advices using an advice stack , (or specifying them in the same pointcut in the order you want).

If the order between some advices is critical then it is recommended that you arrange the advices using an advice stack .

Example

You define an advice stack using the advices element:

<advices-def name="advicestack">
    <advice-ref name="acl"/>
    <advice-ref name="logging"/>
    <advice-ref name="caching"/>
</advices-def>

This advice stack can then be referenced in your Pointcut :

<advice pointcut="pc1">
    <advices-ref name="advicestack"/>
</advice>

Package namespaces

You have two ways of defining a package namespace:

  • using the base-package attribute in the aspectwerkz element. This defines a global package namespace.


  • using the package element. This element has one attribute; name , which defines the package namespace.

Example

<aspectwerkz base-package="foo">
    <!-- All classes are prefixed by 'foo' -->

    <package name="bar">
        <!-- All classes within these 'package' tags are prefixed by 'foo.bar' -->
    </package>

    <package name="baz.buzz">
        <!-- All classes within these 'package' tags are prefixed by 'foo.baz.buzz' -->
    </package>

</aspectwerkz>

Transformation scopes

Using transformation scopes you can choose to perform the transformation within certain packages only. E.g. filter out all classes from all other packages in the transformation process. This can speed up the transformation process a lot as well as assure you that only classes within the packages you have defined is getting transformed no matter how freely you define your pointcut patterns.

A transformation scope is defined using the transformation-scope element which has one attribute; package where you define the package name. You can define as many transformation scopes as you want.

Exam ple

<aspectwerkz>

    <!-- Transformations will only take place within the 'org.codehaus.package' package
         and its subpackages -->
    <transformation-scope package="org.codehaus"/>
    ...

</aspectwerkz>

DTD for XML definition

The DTD is provided since 0.8. Each distribution comes with a bundled DTD so that it is not looked for on the web at runtime.

When you write your XML definition file, add the following at the top of your XML.

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD 0.8.1//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_8_1.dtd">
                
The 0.8 and 0.8.1 DTD are the same so you can also use
<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD 0.8//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_8.dtd">
                

The DTD of the latest release is also referenced

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">
            

Example of an XML definition

Here is an example where all the definitions are put together into a single AspectWerkz XML definition file.

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD 0.8.1//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_8_1.dtd">

<aspectwerkz>
    <!-- ============================================= -->
    <!--  Define the advices                           -->
    <!-- ============================================= -->
    <advice-def name="log"
                advice="advices.LoggingAdvice"
                deployment-model="perInstance"/>

    <advice-def name="cache"
                advice="advices.CachingAdvice"
                deployment-model="perClass"/>

    <advice-def name="persistent"
                advice="advices.PersistenceAdvice"
                deployment-model="perJVM"/>

    <advices-def name="log_and_cache">
        <advice-ref name="log"/>
        <advice-ref name="cache"/>
    </advices-def>

    <!-- ============================================= -->
    <!--  Define the introductions                     -->
    <!-- ============================================= -->
    <introduction-def name="serializable"
                      interface="java.io.Serializable"/>

    <introduction-def name="mixin"
                      interface="mixins.Mixin"
                      implementation="mixins.MixinImpl"
                      deployment-model="perInstance"/>

    <!-- ============================================= -->
    <!--  Define the aspects                           -->
    <!-- ============================================= -->
    <abstract-aspect name="MyAbstractAspect">

        <advice cflow="facadeCalls" pointcut="setters AND !getters">
            <advices-ref name="log_and_cache"/>
        </advice>

        <advice pointcut="persistentFields">
            <advice-ref name="persistent"/>
        </advice>
    </aspect>

    <aspect name="MyAspect" extends="MyAbstractAspect">
        <introduction class="domain.*">
            <introduction-ref name="serializable"/>
            <introduction-ref name="mixin"/>
        </introduction>

        <pointcut-def name="facadeCalls" type="cflow" pattern="* *..facade.*.*(..)"/>
        <pointcut-def name="setters" type="method" pattern="String domain.*.set*(..)"/>
        <pointcut-def name="getters" type="method" pattern="String domain.*.get*(..)"/>
        <pointcut-def name="persistentFields" type="setField" pattern="* domain.*.*">
    </aspect>
</aspectwerkz>

Runtime attributes

AspectWerkz supports runtime attributes. Runtime attributes enables you to decorate your code with meta-data. This meta-data can then be parsed and used during the transformation process. The attributes are defined using JavaDoc tags.

At the moment you can use attributes to define advices and introductions as well as to reference them in your classes. You can also pass parameters to your advices using attributes.

You compile you attributes using the AttributeC compiler. (See the Attribute compilation section for a detailed description on how to use the compiler.)

If you are defining your whole system only using attributes (yes, it is possible) then you don't need the XML definition at all at runtime. I.e. you can skip the -Daspectwerkz.definition.file=.. parameter.

Definition tags

You can then use attributes to define both your advices and your introductions.

The syntax for the definition attributes are as follows:

  • @aspectwerkz.advice-def - for defining an advice, should be set on class level on the advice class.


  • @aspectwerkz.advice-param - for defining a parameter that should be passed to the advice, should be set on class level on the advice class. (See the Passing parameters to advices section for a detailed description.)


  • @aspectwerkz.introduction-def - for defining an introduction, should be set on class level on the interface of the introduction class.


After each tag you add the different definition attributes for your advice and/or introduction definition.

Examples

/**
 * @aspectwerkz.introduction-def name=mixins/MyMixin
 *                               implementation=mixins.MyMixinImpl
 *                               deployment-model=perInstance
 *                               attribute=my_mixin
 */
public interface Mixin {..)

/**
 * @aspectwerkz.advice-def name=advices/MyAroundAdvice
 *                         deployment-model=perJVM
 *                         attribute=log
 * @aspectwerkz.advice-param advice-ref=advices/MyAroundAdvice
 *                           name=param1
 *                           value=value1
 * @aspectwerkz.advice-param advice-ref=advices/MyAroundAdvice
 *                           name=param2
 *                           value=value2
 */
public class MyAroundAdvice extends AroundAdvice {..)

Reference tags

You can then use these attributes by decorating your source code (methods and fields) with them.

The syntax for the reference attributes are as follows:

  • @aspectwerkz.introduction - for specifying the introductions that should be applied to the class. Is defined on class level.


  • @aspectwerkz.advice.method - for specifying the advices that should be applied to the method. Is defined on method level.


  • @aspectwerkz.advice.setfield - for specifying the advices that should be applied to the field at a setField pointcut (meaning when a field is modified). Is defined on field level.


  • @aspectwerkz.advice.getfield - for specifying the advices that should be applied to the field at a getField pointcut (meaning when a field is accessed). Is defined on field level.


  • @aspectwerkz.advice.throws - for specifying the advices that should be applied to the method at a throws pointcut (meaning when an exception is thrown out of the method). Is defined on method level.


  • @aspectwerkz.advice.callerside - for specifying the advices that should be applied to the caller side pointcut. I.e. the point where a certain method is invoked (not executed). When using this tag you also have specify the pattern that it should apply to, meaning the classes that should be advised. Is defined on method level.


  • @aspectwerkz.cflow - for specifying a cflow pointcut.


  • @aspectwerkz.joinpoint.controller - for specifying a join point controller for this pointcut.


After each tag you add the attributes that you want to decorate the method/field with.

Like this: @aspectwerkz.advice.method log cache

If it is a @aspectwerkz.cflow attribute then you specify its name instead.

Like this: @aspectwerkz.cflow pointcutname

Example

/**
 * This class will now have the Mixin with the attribute "my_mixin" applied to it.
 *
 * @aspectwerkz.introduction my_mixin
 */
public class Target {..}

/**
 * This method will now be advised by the advices with the attributes "log" and "cache"
 * (in this order).
 *
 * @aspectwerkz.advice.method log cache
 */
public Object someMethod(String arg) {..}

/**
 * All the method invocations to this method within the "examples.caching.*" package
 * are advised by the advice with the attribute "cache_counter".
 *
 * @aspectwerkz.advice.callerside pattern=examples.caching.* cache_counter
 */
 public Object someMethod() {..}

/**
 * This method will start a cflow pointcut.
 *
 * @aspectwerkz.cflow pointcutname
 */
public Object someMethod(..) {..}

/**
 * This field is now monitored by the advice with the attribute "persistent".
 *
 * @aspectwerkz.advice.setfield persistent
 */
private int m_field;

/**
 * Here we define a join point controller for this join point.
 *
 * @aspectwerkz.joinpoint.controller examples.logging.MyJoinPointController
 */
public Object someMethod(..) {..}

XML definition

If you want to define your advices and introductions in the XML definition but want to use them using attributes then you have to add the attribute attribute to the Advice and Introduction definitions. Here is an example:

<advice-def name="logging"
            class="advices.LoggingAdvice"
            deployment-model="perJVM"
            attribute="log"/>

Attribute compilation

If you are using runtime attributes then you have to compile a the attributes to an XML definition. You can choose to compile a standalone XML definition file or merge with an existing definition.

To compile the attributes you have to use the AttributeC compiler.

Compile from command line

You can run the AttributeC from the command line. (It might be useful to run the ASPECTWERKZ_HOME/bin/setEnv.{bat|sh} script first.)

java [options... ] org.codehaus.aspectwerkz.metadata.AttributeC < path to src dir > < file name > -merge < file name to merge with > -uuid < uuid for definition >

  • < path to src dir > - the path to the root directory for the sources


  • < file name > - the name of the new XML definition file to compile the attributes to


  • -merge < file name to merge with > (or -m ) - the name of the file to merge the compiled attributes with.


  • -uuid < uuid for definition > (or -u ) - the UUID for the definition. (Is optional and if not specified one will be generated)

Deployment models

AspectWerkz supports four different deployment models, which defines the scope of the Advice or Introduction .

The four different deployment models are:

  • perJVM - one sole instance per JVM. Basically the same thing as a singleton class.


  • perClass - one instance per class.


  • perInstance - one instance per class instance.


  • perThread - one instance per thread.

Join point controller

The JoinPointController allows you to control the execution flow of your advices based on custom defined constraints and rules, rules that can be changed at runtime.

It allows you to (for example) control:

  • inter-aspect compatibility


  • inter-aspect dependency


  • aspect redundancy

A little example of how a JoinPointController can be used have been added to the logging example in the examples section.

Implementation

To implement your own JoinPointController you have to implement the

org.codehaus.aspectwerkz.joinpoint.control.JoinPointController interface

or extend the

org.codehaus.aspectwerkz.joinpoint.control.AbstractJoinPointController class.

It might be useful to take a look at the implementation for the default JoinPointController before implementing your own.

If you let your controller implementation extend the org.codehaus.aspectwerkz.joinpoint.control.AbstractJoinPointController class then you have a bunch of convenience methods for managing advices at runtime.

Definition

If you don't specify a JoinPointController then a default JoinPointController (that executes the advices in the order they are specified in the XML file) will be used.

Attributes

To specify which JoinPointController to use you have to use t he @aspectwerkz.joinpoint.controller tag. After this tag you specify the class name of the controller you want to be used at the specific join point.

/**
 * @aspectwerkz.joinpoint.controller foo.MyJoinPointController
 */
public void someMethod() {
    ...
}

XML definition

To specify which JoinPointController to use you have to use the controller-def element. This tag has two attributes that needs to be specified:

  • pointcut - the pointcut expression for the join points that you want to have using the controller (it works fine with expression as well).


  • class - the class name of the join point controller.

<aspect name="MyAspect">
    <pointcut-def name="pc1" type="method" pattern="* foo.Target.*(..)"/>

    <controller-def pointcut="pc1" class="foo.MyJoinPointController"/>
    ...
</aspect>

Non-reentrancy

By default AspectWerkz allows reentrancy for advices at the join points. This could lead to infinite recursion, which will end your application abruptly with a StackOverflowException .

To prevent this you have the option of defining s ome of your pointcuts as non-reentrant , which means that the programs execution flow is not allowed to enter the same join point more that once.

Definition

Attributes

You can define the non-reentrancy in the @aspectwerkz.advice.method attribute tag. Simply add an attribute called non-reentrant and set it to true .

/**
 * @aspectwerkz.advice.method log non-reentrant=true
 */
public void someMethod() {
    ...
}

XML definition

To define a pointcut as non-reentrant in the XML definition you have to add the attribute non-reentrant to the pointcut-def element and set it to true .

<aspect name="MyAspect">
    <pointcut-def ... non-reentrant="true"/>
    ...
</aspect>