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.
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; } }
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; } }
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; } }
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(); } }
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!"; } }
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()); ... }
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.
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>
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>
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