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
.
..
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.
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>
For the class selections specify the full package name of the class along with some wildcards.
Examples:
foo.bar.*
- will matchfoo.bar.FooBar2
as well asfoo.bar.FooBear
.
foo.*.FooBar
- will matchfoo.bar.FooBar2
as well asfoo.bear.FooBar
but notfoo.bear.FooBear
.
foo.*.FooB*
- will matchfoo.bar.FooBar2
as well asfoo.bear.FooBear
as well asfoo.bear.FooB
.
foo..
- will matchThe methods are selected by specifying a pattern that consists of:
All method patterns must follow this structure:
<return_type> <full_method_name>(<parameter_types>)
Examples
int foo.*.Bar.method()
- will matchint method()
but notint method(int i)
.
int *.method(*)
- will matchint Foo.method(int i)
but notint Foo.method()
.
int foo.*.*.method(*,int)
- will matchint method(String s, int i)
as well asint method(int i1, int i2)
.
int foo.*.Bar.method(..)
- will matchint method()
as well asint method(String s, int i)
as well asint method(int i, double d, String s, Object o)
.
int foo.*.Bar.method(int,..)
- will matchint method(int)
as well asint method(int i, String s)
as well asint method(int i, double d, String s, Object o)
.
int foo.*.Bar.method(java.lang.*)
- will matchint method(String s)
as well asint method(StringBuffer sb)
.
int foo.*.Bar.me*o*()
- will matchint method()
as well asint metamorphosis()
and int meo()
but notint me()
.
* foo.*.Bar.method()
- will matchint method()
as well asjava.lang.String method()
.
java.lang.* foo.*.Bar.method()
- will matchjava.lang.String Bar.method()
as well asjava.lang.StringBuffer Bar.method()
.
The fields are selected by specifying a pattern that consists of:
All field patterns must follow this structure:
<field_type> <full_field_name>
Examples
int foo.*.Bar.m_foo
- will matchint m_foo
but notint s_foo
orlong m_foo
.
* foo.*.Bar.m_foo
- will matchint m_foo
as well asjava.lang.String m_foo
.
java.lang.* foo.*.Bar.m_foo
- will matchjava.lang.String m_foo
as well asjava.lang.StringBuffer m_foo
.
int foo.*.Bar.m_*
- will matchint m_foo
as well asint m_bar
.
int foo.*.Bar.m_*oo*
- will matchint m_foo
as well asint m_looser
as well asint m_oo
.
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.
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.*[][].
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
String*
instead of java.lang.String
or java.lang.StringBuffer
Apart from these abbreviations you always have to specify the fully qualified name of the class (along with the wildcards).
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
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")
AspectWerkz
supports four different deployment
models, which defines
Advice
and Introduction
in Model 1 systemsAspect
and Introduction
in Model 2 systemsThe 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. model | required Aspect depl. model |
---|---|
perJVM | perJVM (default for Aspect) |
perClass | perJVM or perClass |
perInstance | perinstance or perClass or perJVM |
perThread | perThread |
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
exclude
packages if anyinclude
packages if anyinclude
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 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:
base-package
attribute in the system
element. This defines a global package namespace for all elements defined within the system
element.
package
element. This element has one attribute;
name
, which defines the package namespace.
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>
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 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.
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");
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>
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>
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.
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 {..}
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(..) {..}
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"/>
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)
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:
A little example of how a JoinPointController
can be used have been
added to the
logging example
in the examples section.
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.
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>
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.
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.
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.
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>