Introduction

Here are some simple examples to get you going with the Model 1 - XML centric approach. All examples together with ready to execute tests are to be found in the source distribution under src/samples/examples/xmldef. They are all defined using the same definition file samples-xmldef.xml

If you are using Doclet style instead or in complement of the XML definition then you have to process the source files to generate or merge the XML definition file before running it. You can generate or merge the XML definition file from doclets in source files with the provided utility AttributeC. See the related section. This is done automatically in these examples.

Caching

This advice implements a simple caching service. It caches the results from the method invocations that are picked out by the pointcuts mapped to this advice.

Note: The caching example has been rewritten and is now a bit more complex than necessary. The background to the change is that the current implementation is a solution to Cedric Beusts AOP challange. The simplified version is shown here for improved readability and understanding.

To run the example type: maven aspectwerkz:xmldef:samples:caching

public class CachingAdvice extends AroundAdvice {

    private Map m_cache = new StaticBucketMap(1000);

    public CachingAdvice() {
        super();
    }

    public Object execute(final JoinPoint joinPoint) throws Throwable {
        final Long hash = new Long(calculateHash((MethodJoinPoint)joinPoint));

        final Object cachedResult = m_cache.get(hash);

        // if we have a cached result; return the cache
        if (cachedResult != null) return cachedResult;

        // else, proceed with the method invocation and store the result in the cache
        final Object result = joinPoint.proceed();
        m_cache.put(hash, result);

        return result;
    }

    private long calculateHash(final MethodJoinPoint joinPoint) {
        int result = 17;
        result = 37 * result + joinPoint.getMethodName().hashCode();
        Object[] parameters = joinPoint.getParameters();
        for (int i = 0, j = parameters.length; i < j; i++) {
            result = 37 * result + parameters[i].hashCode();
        }
        return result;
    }
}

Asynchronous calls

This advice makes it possible to achive asynchronous method invocations. All the methods that are picked out by the pointcuts mapped to this advice are being executed in it's own thread. Uses a thread pool.

To run the example type: maven aspectwerkz:xmldef:samples:asynchronous

public class AsynchronousAdvice extends AroundAdvice {

    private PooledExecutor m_threadPool = ... // initalize the thread pool

    public AsynchronousAdvice() {
        super();
    }

    public Object execute(final JoinPoint joinPoint) throws Throwable {
        m_threadPool.execute(
                new Runnable() {
                    public void run() {
                        try {
                            // invoke the intercepted method
                            joinPoint.proceedInNewThread(); // using proceedInNewThread
                        }
                        catch (Throwable e) {
                            throw new WrappedRuntimeException(e);
                        }
                    }
                }
        );
        return null;
    }
}

Synchronization

This advice implements method synchronization. It synchronizes access to the methods that are picked out by the pointcuts mapped to this advice.

To run the example type: maven aspectwerkz:xmldef:samples:synchronization

public class SynchronizationAdvice extends AroundAdvice {

    private Mutex m_mutex = new Mutex();
    // if a counting semaphore is needed use:
    // private Semaphore m_mutex = new Semaphore(nrThreadsAllowed);

    public SynchronizationAdvice() {
        super();
    }

    public Object execute(final JoinPoint joinPoint) throws Throwable {
        m_mutex.acquire();
        Object result = joinPoint.proceed();
        m_mutex.release();
        return result;
    }
}

Logging + Doclet attributes + parameterized advices + join point controller

This advice implements a simple logging service. It logs the entry and exit of the methods that are picked out by the pointcuts mapped to this advice. In this simple example I am only using a small subset of all the metadata available from the join point.

This example is defined using Doclets.

To run the example type: maven aspectwerkz:xmldef:samples:logging

Here is the advice that does the logging. Here yoe can see that I have defined it using Doclet attributes (written as JavaDoc tags). I have also defined a parameter name:value tuple. This parameter can be read in the advice at Doclet using the getParameter(..) method.

/**
 * @aspectwerkz.advice-def name=log
 *                         deployment-model=perJVM
 *                         attribute=log
 * @aspectwerkz.advice-param advice-ref=log
 *                           name=param
 *                           value=value
 */
 public class LoggingAdvice extends AroundAdvice {

    private int m_level = 0;

    public LoggingAdvice() {
        super();
    }

    public Object execute(final JoinPoint joinPoint) throws Throwable {
        String parameter = getParameter("param")); // get the parameter defined in the definition

        MethodJoinPoint jp = (MethodJoinPoint)joinPoint;
        indent();
        System.out.println("--> " + jp.getTargetClass().getName() + "::" + jp.getMethodName());
        m_level++;
        final Object result = joinPoint.proceed();
        m_level--;
        indent();
        System.out.println("<-- " + jp.getTargetClass().getName() + "::" + jp.getMethodName());
        return result;
    }

    private void indent() {
        for (int i = 0; i < m_level; i++) {
            System.out.print("  ");
        }
    }
}

This is the code for the target class that wants to be logged. As we can see here we have defined Doclet attributes at each method that we want to be logged. This is done by using the @aspectwerkz.advice.method tag and after this tag referring to the attribute name of the advices that we want to be applied to this join point (method). In this case we add the name log since this is the attribute name of the advice that we have defined.

For one of the methods we have also added a JoinPointController that removes redundant advices for us. As you can see we have added the attribute log twice to see the controller remove one of the redundant advices from the join point.

public class Target {

    /**
     * @aspectwerkz.joinpoint.controller examples.logging.DummyJoinPointController
     * @aspectwerkz.advice.method log
     * @aspectwerkz.advice.method log
     */
    public static void toLog1() {
        new Target().toLog2("parameter");
    }

    /**
     * @aspectwerkz.advice.method log
     */
    private void toLog2(java.lang.String arg) {
        new Target().toLog3();
    }

    /**
     * @aspectwerkz.advice.method log
     */
    private String toLog3() {
        return "result";
    }

    /**
     * @aspectwerkz.advice.method log
     */
    public static void main(String[] args) {
        Target.toLog1();
    }
}

Introductions and Mixins

This example shows both how an Mixin/Introduction is implemented.

To run the example type: maven aspectwerkz:xmldef:samples:introduction

Note: if you add more than one Introduction to a class then you have to make sure that the names of the methods do not collide.

public interface Mixin {
    String sayHello();
}

public class MixinImpl implements Mixin {
    public String sayHello() {
        return "Hello World!";
    }
}
As you can see neither the interface nor the implementation class needs to implement a certain interface or extend a certain class.

Second you have to define your Introduction in the XML definition file.

Now you will be able to invoke your Introduction like this:

public class Target {
    ...
    System.out.println("The mixin says: " + ((Mixin)this).sayHello());
    ...
}

Caller side pointcuts

As a part of the caching example I am also demonstrating how to implement caller pointcuts. Which means that the method is advised on the caller side and not on the callee side. I.e. method invocation and not method execution.

To advise on the caller side your Advice must extend either the PreAdvice or the PostAdvice. In this example I have written a simple advice that counts the number of time that a certain method is invoked (not executed). This Advice extends the PreAdvice class because I want to make the count before the method is invoked and not after.

public class InvocationCounterAdvice extends PreAdvice {

    public InvocationCounterAdvice() {
        super();
    }

    public void execute(final JoinPoint joinPoint) throws Throwable {
        CallerSideJoinPoint jp = (CallerSideJoinPoint)joinPoint;
        CacheStatistics.addMethodInvocation(
            jp.getMethodName(), jp.getParameterTypes());
    }
}

This advice is then defined as usual (see the XML definition section), but the syntax for adding it to the Pointcut is slightly different:

<aspect ...>
    <pointcut-def name="caller"
                  type="callerSide"
                  pattern="examples.caching.*->int examples.caching.Pi.getPiDecimal(int)"/>
</aspect>

The pointcut is defined by using the callerSide type and the pattern is the caller class pattern (the classes that calls the method) and the method pattern itself (full name including the class and package) separated by a -> character.

What happens now is that the InvocationCounterAdvice will be invoked before each method invocation of the examples.caching.Pi.getPiDecimal method and that counting will take place on the caller side.

Control flow (cflow) pointcuts + Doclet attributes

This example shows how control flow (cflow) pointcuts are implemented.

This example is defined using Doclet attributes.

To run the example type: maven aspectwerkz:xmldef:samples:cflow

In this example we have two methods step1 and step2 in which step1 calls step2.

First we define a cflow attribute on the step1 method. Which means that we have defined a cflow pointcut for this method, we have also given it a name so that we can reference it in other parts of the definition.

Second we define a normal method advice for the step2 method (the advice is defined with the name "my_advice" and we are applying it here using its name). Then we define a cflow for this method pointcut. This means that the advices at this pointcut will only be triggered if we are in the control flow of the cflow pointcut called cflowtest, e.g. the method step1. Otherwise the advices should be skipped.

public class Target {

    /**
     * @aspectwerkz.cflow cflowtest
     */
    public void step1() {
        step2(); // step2 is called in the control flow of step1
    }

    /**
     * @aspectwerkz.advice.method my_advice cflow=cflowtest
     */
    public void step2() {
    }

    ...
}

An equivalent XML definition of the above would look like this:

<aspect name="CFlowExample">
    <pointcut-def name="step1" type="cflow" pattern="* Target.step1()"/>
    <pointcut-def name="step2" type="method" pattern="* Target.step2()"/>

    <bind-advice cflow="step1" pointcut="step2">
        <advice-ref name="my_advice"/>
    </bind-advice>
</aspect>

XML definition file for the examples

Here is the XML definition file for the advice examples above:

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

<aspectwerkz>
    <system id="examples">

        <!-- ============================================= -->
        <!--  Introductions                                -->
        <!-- ============================================= -->
        <introduction-def name="mixin"
                          interface="examples.introduction.Mixin"
                          implementation="examples.introduction.MixinImpl"
                          deployment-model="perJVM"/>

        <!-- ============================================= -->
        <!--  Advices                                      -->
        <!-- ============================================= -->
        <advice-def name="cache"
                    class="examples.caching.CachingAdvice"
                    deployment-model="perJVM"/>

        <advice-def name="invocationCounter"
                    class="examples.caching.InvocationCounterAdvice"
                    deployment-model="perJVM"/>

        <advice-def name="asynchronous"
                    class="examples.asynchronous.AsynchronousAdvice"
                    deployment-model="perJVM"/>

        <advice-def name="synchronize"
                    class="examples.synchronization.SynchronizationAdvice"
                    deployment-model="perJVM"/>

        <!-- ============================================= -->
        <!--  Abstract aspects                             -->
        <!-- ============================================= -->
        <abstract-aspect name="AbstractSynchronization">
            <bind-advice pointcut="synchronizedCalls">
                <advice-ref name="synchronize"/>
            </bind-advice>
        </abstract-aspect>

        <!-- ============================================= -->
        <!--  Concrete aspects                             -->
        <!-- ============================================= -->
        <aspect name="Caching">
            <pointcut-def name="callee" type="method"
                    pattern="int examples.caching.Pi.getPiDecimal(int)"/>
            <pointcut-def name="caller" type="callerSide"
                    pattern="examples.caching.*->int examples.caching.Pi.getPiDecimal(int)"/>

            <bind-advice pointcut="callee">
                <advice-ref name="cache"/>
            </bind-advice>
            <bind-advice pointcut="caller">
                <advice-ref name="invocationCounter"/>
            </bind-advice>
        </aspect>

        <aspect name="Asynchronous">
            <pointcut-def name="asynchronousCalls" type="method"
                    pattern="* examples.asynchronous.Target.toRunAsynchronously()"/>

            <bind-advice pointcut="asynchronousCalls">
                <advice-ref name="asynchronous"/>
            </bind-advice>
        </aspect>

        <aspect name="Synchronization" extends="AbstractSynchronization">
            <pointcut-def name="synchronizedCalls" type="method"
                    pattern="* examples.synchronization.Target.toSynchronize()"/>
        </aspect>

        <aspect name="Introduction">
            <bind-introduction class="examples.introduction.Target">
                <introduction-ref name="mixin"/>
            </bind-introduction>
        </aspect>

    </system>
</aspectwerkz>

Field interception - Transparent persistence of POJOs

In the src/extensions directory you will find an example how to implement transparent persistence for POJOs.

Read more about it on Jonas' weblog.

Run the example with: maven aspectwerkz:xmldef:samples:transparentpersistence