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. When used to match a package name, matches at least one character. 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).

Expression

It is possible to combine patterns selection together using algebraic expressions. The following operators are supported:

  • AND or &&
  • OR or ||
  • NOT or !
  • IN (use for cflow expressions, e.g. executionPointcut IN cflowPointcut
  • parenthesis for grouping
For model 1 deployed aspects, since the expression is defined in an XML file, the operator AND is more convient (else & should be escaped).

AOP system

Aspects deployed belong to a system. For now AspectWerkz support only a single system in the whole JVM. System are named thru the XML definition file and each system should have a unique name (though for now AspectWerkz is singled system). The system name (id) is used when accessing system at runtime, f.e. to swap mixin implementation.

System is wether of type Model 1 - XML centric whether of type Model 2 - self defined aspects. The type is guessed from the XML file. For a detailled documentation on each system type read the core concepts.

Sample for a Model 1 - XML centric system

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">
<aspectwerkz>
    <system id="system name">
        ...
        <aspect name="MyAspect">
            ...
        </aspect>
    </system>
</aspectwerkz>
                

Sample for a Model 2 - Self defined aspects system

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">
<aspectwerkz>
    <system id="system name">
        ...
        <use-aspect class="MySelfDefinedAspect"/>
        ...
    </system>
</aspectwerkz>
                

Sample to access a system at runtime

    SystemLoader.getSystem("system name")
                

Deployment models

AspectWerkz supports four different deployment models, which defines

  • the scope of the Advice and Introduction in Model 1 systems
  • the scope of the Aspect and Introduction in Model 2 systems

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.

The internal component model of AspectWerkz uses the prototype design pattern, which means for example that with a perJVM deployement model, 2 instances are created (one prototype and one perJVM) though only one is really used (the perJVM one).

In Model 2 since introduction implementation are inner classes, they have to follow a subset of the aspect deployment model. If not specified, the introduction is deployed as the aspect that defines it. Else the following applies:

Introduction depl. modelrequired Aspect depl. model
perJVMperJVM (default for Aspect)
perClassperJVM or perClass
perInstanceperinstance or perClass or perJVM
perThreadperThread

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 exclude and include element which have one attribute package where you define the package name. You can define as many transformation scopes as you want.

The package attribute supports only .* as an ending pattern (package and all sub package).

During the transformation process (online or offline), a class might be transformed (depending on the pointcuts) if

  1. the class does not belongs to any exclude packages if any
  2. and
  3. the class belongs to at least one include packages if any
Note that when include is used the selection is much more restrictive. This selection mechanism should be used as most as possible especially in online mode since it enables an ealry filtering.

Example

<aspectwerkz>
    <system id="sample">
        <!-- Transformations will only take place within the 'org.codehaus.package' package
             and its subpackages -->
        <include package="org.codehaus"/>
        <include package="org.codehaus.*"/><!-- synonymous of org.codehaus -->
        <exclude package="com.oracle.*"/>
        ...
    </system>
</aspectwerkz>

Package namespaces

Package namespaces provide a way to simplify the edition of the XML definition file so that it is not necessary to prefix all class with the complete package information.

You have two ways of defining a package namespace:

  • using the base-package attribute in the system element. This defines a global package namespace for all elements defined within the system element.


  • using the package element. This element has one attribute; name, which defines the package namespace.
The package name will be used as a prefix for all nested elements.

Example

<aspectwerkz>
    <system id="foodemo" 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>
    </system>
</aspectwerkz>

DTD for XML definition

Each distribution comes with a bundled DTD so that it is not looked for on the web at runtime. The XML is not validated against its DTD at runtime so be cautious to provide a valid XML defintion.

When you write your XML definition file, add the following at the top of your XML to reference the latest release:

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

It is possible to specify a specific released version number as well (starting with 0.8)

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

Custom metadata compilation

Custom metadata compilation allows to turn @Attribute.<name> [<value>] frmo source files to .class files by incorporating them into bytecode. This can be used f.e. to implement declarative transaction demarcation for POJO.

The AspectC utility can be used to compile custom metadata to the class bytecode. Custom metadata is marked with @Attribute.. See AspectC usage notes.

Model 1 - 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");

Model 1 - 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>

Model 1 - 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//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">

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

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

        <advice-def name="persistent"
                    class="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">

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

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

        <aspect name="MyAspect" extends="MyAbstractAspect">
            <bind-introduction class="domain.*">
                <introduction-ref name="serializable"/>
                <introduction-ref name="mixin"/>
            </bind-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>

    </system>
</aspectwerkz>

Model 1 - Doclets

AspectWerkz supports Doclets style instead (or in complement) of the standard Model 1 XML file. Doclets enables you to decorate your code with meta-data. This meta-data can then be parsed and turned in an XML file in a preprocessing phase. The Doclets 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 transform you attributes in an XML file 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 to write the XML definition at all. This one will be entirely generated.

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"/>

Model 1 - Doclets compilation

If you are using Doclets then you have to compile them 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. 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)

Model 1 - 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 the @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>

Model 2 - Aspects compilation

Self defined Aspects need to be compiled in order to incorporate the metadata in the bytecode. This allows to almost totally skip the XML definition file complexity. The post compilation phase will not be needed under java 1.5 with JSR-175 support.

Aspect metadata compilation

To post-compile the aspect's .class files you have to use the AspectC compiler, which needs both the regular .class files and the aspect sources files containing the attributes metadata. You can run the AspectC from the command line. (It might be useful to run the ASPECTWERKZ_HOME/bin/setEnv.{bat|sh} script first.)

java [options...] org.codehaus.aspectwerkz.attribdef.definition.AspectC [-verbose] <path to src dir> <path to classes dir> [<path to destination dir>]

  • -verbose - (optional) to activate verbose logging of compilation
  • <path to src dir> - the path directory for the source files (not mandatory; it is not needed to point to the root package directory since package name is extracted from the source file itself)


  • <path to classes dir> - the path to the root directory of the regular .class files for the aspects


  • <path to destination dir> - (optional) - the path to the root directory to write the new .class files for the self-defined aspects. If not specified, the initial .class files are overridden.

Custom metadata compilation

AspectC can also be used to handle custom metadata as described here.

Model 2 - Passing parameters to aspects

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

<use-aspect ... >
    <param name="timeout" value="10"/>
</use-aspect>

From within an Aspect subclass use the method ___AW_getParameter("timeout") to retrieve the parameter value as a String.

Model 2 - 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.9//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_9.dtd">
<aspectwerkz>
    <system id="tests">
        <exclude package="test.attribdeff"/>
        <package name="test.attribdef.aspect">
            <use-aspect class="MemberMethodTestAspect"/>
            <use-aspect class="StaticMethodTestAspect"/>
            <use-aspect class="FieldTestAspect"/>
            <use-aspect class="CallerSideTestAspect"/>
            <use-aspect class="CFlowTestAspect"/>
            <use-aspect class="IntroductionTestAspect"/>
            <use-aspect class="DynamicDeploymentTestAspect"/>
        </package>
    </system>
</aspectwerkz>