Gotchas when migrating from 1.x to 2.x

  • Build and develop for Java 5 -

    To build on Java 5 you will have to put the ./lib/aspectwerkz-jdk5-2.0.jar on the classpath instead of ./lib/aspectwerkz-jdk14-2.0.jar. Same if you want to run it inside an IDE, or use an Ant script. This jar contains the Java 5 implementations of the AspectWerkz annotations.


  • No more perThread deployment model -

    We have removed the perThread deployment model for both aspects and mixins. It should have never been added in the first place. We are working on supporting the much more fine-grained and expressive percflow and percflowbelow.


  • Currently no perJVM deployment model for mixins -

    This will most likely be added for the 2.0 final release.


  • No more annotation proxies -

    This implementation has been replaced by a better one, in which you only have to write an interface that is very similar to a Java 5 annotation component. For details see this section.


  • New Mixin implementation -

    The Mixin implementation is completely new, both the way it is implemented and defined. For details see this section.


  • For mixin definition, @Introduce and <introduce ...> -

    has been replaced by @Mixin and <mixin ...> (child of <system> XML element)


  • For interface only introduction, @Implements and <introduce ...> -

    Has been replaced by @Introduce and <introduce ...> (child of <aspect> XML element)


  • Dynamic AOP requires AspectWerkz native library in PATH - unless using Java 5.

    To make use of the dynamic features, such as runtime deployment and undeployment, you need to have the AspectWerkz native library (*.dll, *.so or *.jnilib) in your PATH. These can be found here.

    A consequence is that the dynamic tests will fail if you have not done this. The native library is not needed with Java 5.


AspectWerkz 2: A Fast, Open and Dynamic Aspect Container

The AspectWerkz 2 architecture is a complete rewrite of the core weaver and container.

When we where designing the new architecture we had four different objectives:

  • Performance
  • The AspectWerkz weaver is now completely based on static compilation (no reflection at all), which means that the weaved code is very fast. We have released a simple microbenchmark that compares AspectWerkz with all the other major AOP implementations.

    We will publish a small paper in which where we compare the performance of all major AOP frameworks on some days.

  • Extensibility
  • The new container is very open and extensible. It allows plugging in different Aspect Model Extensions that allows running other AOP framework's aspect inside the AspectWerkz Extensible Aspect Container.

    Read more here

  • Dynamicity
  • In AspectWerkz 2 we allow the user to both hot-deploy and undeploy his aspect at runtime. This implementation is based on HotSwap and guarantees that the performance of the target application will be the same when an aspect has been undeployed as it was before it was deployed (either hot-deployed or at startup time).

    Read more here

  • Feature complete
  • We have put a lot of effort into implementing the full semantics of AOP as defined by AspectJ.

AspectWerkz Extensible Aspect Container

The last couple of years the Java AOP landscape has flourished. It has gone from one single implementation (AspectJ), to a whole bunch of frameworks, each one with its own way of defining and instantiating its aspects and with its own weaver, runtime environment and tools. Even though having many ways of doing things is not necessary a bad thing, especially not in such early days of a new technology, it is not making it easier for new users of AOP and does not speed up its adoption.

When you look at most frameworks out there you can see that they have a lot in common, most of them share same semantics, life-cycle options etc. Most of them have re-implemented concepts introduced by AspectJ and in some cases introduced their own.

This is something that we have had in mind when we were designing the new AspectWerkz container and weaver and led us to an open architecture that was easy to extend, an architecture that formed the foundation for the AspectWerkz Extensible Aspect Container.

Basically it allows you to plug in extensions that can handle the framework specific details, while the common things are shared. What this brings you is a weaver and a container that can weave, deploy and run any aspect no matter how it is implemented and defined.

This introduces many advantages and opens for standardization on tools (development and runtime manageability).

Read more about this in the TSS article

Hot deployment and undeployment of aspects

AspectWerkz supports both hot deployment and hot undeployment of aspects. It utilizes HotSwap (Java 1.4) or JVMTI (Java 5) to redefine your application at runtime. New aspects can be added to the running system and old ones can be redefined or removed at runtime.

All these services are accessed from the org.codehaus.aspectwerkz.transform.inlining.deployer.Deployer class, which has a rich set of services.

Deployment

Deployment of aspects is done using one of the Deployer.deploy(..) methods.

Here is the API for deployment of aspects (see details below):

DeploymentHandle deploy(Class aspect)

DeploymentHandle deploy(Class aspect, ClassLoader deployLoader)

DeploymentHandle deploy(Class aspect, DeploymentScope scope, ClassLoader deployLoader)

DeploymentHandle deploy(Class aspect, String xmlDef)

DeploymentHandle deploy(Class aspect, String xmlDef, ClassLoader deployLoader)

DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope)

DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope, ClassLoader deployLoader)
                    

Details on the deployment API:

  • The first three methods are for deployment of annotation defined aspects only and the last three for XML defined aspects (that can have annotations as well, if so then the XML definition will override the annotation definition, but apart from that they will merge).


  • As you can see all methods returns a DeploymentHandle, read more about that in the section about deployment handles below.


  • The class loader passed to the method is the class loader that you want the aspect to be deployed in. If no class loader is specified then it will use the loader that loaded the aspect class. (Note: the algorithm will not handle importing classes from one class loader to another so they need to be in the same hierarchy.)


  • It is recommended that you pass a DeploymentScope to the deploy method if you want predictable and safe deployment. For details, see the section on deployment scopes below.

Undeployment

Undeployment of aspects is done using one of the Deployer.undeploy(..) methods.

Here is the API for undeployment of aspects (see details below):

void undeploy(Class aspect)

void undeploy(Class aspect, ClassLoader loader)

void undeploy(DeploymentHandle deploymentHandle)
                    

Details on the deployment API:

  • You can undeploy an aspect by just specifying the aspect's class. This means that the aspect will be undeployed from the class loader that has loaded the aspect class.


  • If you specify a class loader then the aspect will be undeployed from the class loader that is specified.


  • If you specify a DeploymentHandle then all join points that where affected by the deployment event defined by the handle will be reverted to the state they where in before the deployment occured. This means that you need to keep track of order and dependencies etc. e.g. f.e. rollback all changes in the correct order etc.


  • If an aspect that is being undeployed can not be found (e.g. is not deployed or is already undeployed) then nothing happens. No error is triggered.

Deployment scopes

The use of deployment scopes give you more predictable and safer deployment.

They are needed due to the fact that no JVMs today support schema redefinition when redefining your classes.

This means that you have to define a special kind of pointcut that we call deployment scope, which will prepare you application and advise the points that you are interested in doing hot deployment on later.

You can then retrieve a handle to this deployment scope by getting the actual instance of the abstraction and then use this to narrow down the scope of the deployment so you are sure that you will not try to deploy the aspect at points in your code that will not be affected. Hence you are garantueed that your aspect will be deployed at valid points in your code.

Definition

You define the deployment scope just as regular pointcuts, in its own aspect or in the same aspect as the rest of your code:

@Expression("execution(String *.toString())")
DeploymentScope toString;
                    
<deployment-scope name="toString" expression="execution(String *.toString())"/>
                    

Runtime retrieval

You can then retrieve the instance of the DeploymentScope like this:

DeploymentScope scope = SystemDefinition.getDefinitionFor(loader, systemId).getDeploymentScope("toString");
                    

Deployment handles

All deploy(..) methods returns a DeploymentHandle which is a handle to the specific deployment event. You can use this handle to revert the changes made by the deployment, In other words, it allows you to undeploy the aspect you deployed and be sure that it will be undeployed exactly the way it was deployed, same class loader, same deployment scope etc.

You retrieve it from one of the deploy(..) methods and can later use it when undeploying the aspect:

// deploy aspect
DeploymentHandle handle = Deployer.deploy(..);

// store the handle somewhere

...


// retrieve the handle from storage
DeploymentHandle handle = ...

// undeploy using handle
Deployer.undeploy(handle);
                    

Richer after advice semantics

In previous releases of AspectWerkz we only supported one single type of after advice. The sematics of this type was that it was always executed, regardless of wheter an exception had been throw or if the method returned successfully.

In the new 2.x architecture we have enhanced the semantics for this type of advice and have borrow the semantics from AspectJ. So now we support three different types of after advice:

  • @AfterFinally (same as @After)
  • @AfterReturning [TYPE] - @AfterReturing(pointcut) or @AfterReturning(type=onReturnedType, pointcut=pointcut)
  • @AfterThrowing [TYPE] - @AfterThrowing(pointcut) or @AfterThrowing(type=onThrownedType, pointcut=pointcut))

after finally (after)

An after finally advice declaration has the same semantics as an after advice declaration, you can use any syntax you like.

after finally advice are always executed, they work the same as a finally block. Meaning that they will be invoked regardless of wheter an exception has been thrown or if the method has returned successfully.

Java 5 annotation definition

@AfterFinally("execution(@TransactionAttribute * *.*(..))")
public void logTxMethods(StaticJoinPoint joinPoint) {..}
                    

JavaDoc annotation definition

/**
 * @AfterFinally("execution(@TransactionAttribute * *.*(..))")
 */
public void logTxMethods(StaticJoinPoint joinPoint) {..}
                    

XML definition

<advice type="after finally"
        bind-to="execution(@TransactionAttribute * *.*(..))"
        name="logTxMethods"/>
                    

after returning [TYPE]

after returning [TYPE] advice is executed if the method returns normally (without throwing an exception) and the actual type that is returned is of the type that is specified in the advice declaration.

If no return type is specified then it applies to all invocations that return normally, e.g. not when throwing an exception.

Java 5 annotation definition

@AfterReturning(
    type="@Service *..*",
    pointcut="execution(@TransactionAttribute * *.*(..)"
)
public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
                    

JavaDoc annotation definition

/**
 * @AfterReturning(
 *      type="@Service *..*",
 *      pointcut="execution(@TransactionAttribute * *.*(..)"
 * )
 */
public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
                    

XML definition

<advice type="after returning(@Service *..*)"
        bind-to="execution(@TransactionAttribute * *.*(..))"
        name="txCommitReturningService"/>
                    

after throwing [TYPE]

after throwing [TYPE] advice is executed after the execution completes abruptly because of an exception and the actual type of the exception that is thrown is of the type that is specified in the advice declaration.

If no exception type is specified then it applies to all invocations that returns with an exception.

Java 5 annotation definition

@AfterThrowing(
    type="RuntimeException",
    pointcut="execution(@TransactionAttribute * *.*(..)"
)
public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
                    

JavaDoc annotation definition

/**
 * @AfterThrowing(
 *    type="RuntimeException",
 *    pointcut="execution(@TransactionAttribute * *.*(..)"
 * )
 */
public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
                    

XML definition

<advice type="after throwing(RuntimeException)"
        bind-to="execution(@TransactionAttribute * *.*(..))"
        name="txRollbackOnRuntimeException"/>
                    

StaticJoinPoint, JoinPoint, EnclosingStaticJoinPoint

Advice are methods in aspect class. The method signature must conform to some rules, depending on the pointcut where the advice is bound to, when args(..), this(..) and/or target(..) are used to gain access to join point argument / caller / callee instance.

Aside from this pointcut bounded arguments, the advice can have extra arguments of type "JoinPoint" or "StaticJoinPoint", which do not appear in the pointcut. First version of AspectWerkz were supporting only "JoinPoint" as advice argument, and use of "getRtti()" RTTI API was mandatory.

In AspectWerkz 2.x, use of "StaticJoinPoint" and/or "JoinPoint" as advice argument is optional and use of it (or no use of it at all) depends on aspect developer decision. Note that for "around" advice, in order to invoke "proceed()" properly, you will need access to either a "StaticJoinPoint" or "JoinPoint" instance).

When only "StaticJoinPoint" is used, specific optimizations are used to maximize runtime performance since RTTI access is then guaranteed to not happen.

The following demonstrates legal syntax:

@Before("execution(* foo.Bar.method(..))")
void before() {}

@Before("execution(* foo.Bar.method(..))")
void before(StaticJoinPoint sjp) {}

@Before("execution(* foo.Bar.method(..))")
void before(JoinPoint jp) {}

@Around("execution(* foo.Bar.method(..))")
Object around() {
    // possible but rather useless since then all following advices
    // in the chain are skipped since proceed() is not called
    return null;
}

@Around("execution(* foo.Bar.method(..))")
Object around(StaticJoinPoint sjp) throws Throwable {
    return sjp.proceed();
}

@Around("execution(* foo.Bar.method(..))")
Object around(JoinPoint jp) throws Throwable {
    return jp.proceed();
}

@Around("call(* foo.Bar.method(..)) && args(i, j) && target(bar) && this(caller)")
Object aroundCallWithBindings(int i, int j, Bar bar, Object caller, StaticJoinPoint sjp) throws Throwable {
    // no use of RTTI and tedious casting
    // much better performance
    bar.method();
    int localInt = j + i;
    ...
    return sjp.proceed();
}

// use of pointcut composition with argument bindings
@Expression("execution(* foo.Bar.method(..)) && args(i)")
void pointcut_1(int i) {}

@Expression("execution(* foo.Bar.method(..)) && target(bar)")
void pointcut_2(Bar bar) {}

@Around("pointcut_1(myI) && pointcut_2(myBar)")
Object around(JoinPoint jp, int myI, Bar myBar) throws Throwable {
    myBar.method();
    return jp.proceed();
}

                

The use of EnclosingStaticJoinPoint does not interfere on advice signature. EnclosingStaticJoinPoint information is accessible from a StaticJoinPoint or JoinPoint instances.

this() and target()

AspectWerkz 2.x introduces this(..) and target(..) pointcut designators. They can be used to narrow a pointcut expression based on caller and calle types (note: they will implicitly filter out callee side static methods, callee constructors on constructor call pointcut (since the callee is not available yet), and caller side static method for call pointcuts and field pointcuts).

They can be used with type patterns: this(foo.Bar+), target(foo.*) etc.

They can be used to gain direct access to callee and caller instance in advice by using advice argument binding, when combined with pointcut with signature or advice with signature (see previous part).

// this pointcut is not declared as a field since it exposes parameter binding
// note use of "bar" variable name
@Expression("target(bar)")
void pointcut(Bar bar) {}

// use the pointcut with respect to its signature
// note use of "myBar" variable name
@Before("execution(* *..method(..)) && pointcut(myBar)")
void before(Bar myBar) {
    myBar.callback();
}
                

New annotation backend and Java 5 annotations support

In AspectWerkz 2.x, the former "TypedAnnotationProxy" and "UntypedAnnotationProxy" classes are gone. To define a strongly typed custom annotation with Java 1.3/1.4, you need to write an interface. This interface should only have method whose return type is the annotation element type (see restriction below) and with no parameters. This means that Java 1.3/1.4 annotation are identical to Java 5 annotations except that the annotation is an "interface" and not a "@interface" (see samples below).

If the annotation is exposing an anonymous element, the special "[type] value();" method should be used (as in Java 5 annotations)

Restrictions: In Java 5, Annotations defined with "@interface" have restrictions. Type of exposed elements cannot be multi-dimensional arrays, etc. The same restriction applies for Java 1.3/1.4 AspectWerkz annotations.

Note: Java 5 "default" value is not available in Java 1.3/1.4 AspectWerkz annotations, hence the default value of "non Java 5" annotation elements is "null" (or 0 / false for primitives types).

Note: Java 5 does not support having several annotation of the same type on the same member, while this is possible with AspectWerkz 1.3/1.4 Annotations (though not recommanded for upward compatibility)

Note: Java 1.3/1.4 annotation will implement both the given interface and the org...Annotation interface which is equivalent to the java.lang.annotation.Annotation interface (f.e. annotationType() will return the type of the annotation).

Note: for Java 5, the annotation that appears in the source must be part of the imports or fully qualified (@foo.bar.AnnotateMe("some texte")). For AspectWerkz equivalent with 1.3/1.4, use the custom annotation.properties file at compilation time to set annotation nickname (f.e. @Hello("some text") could be of type foo.bar.AnnotateMe this way..)

Samples: Java 5 Annotations and their Java 1.4 equivalent with AspectWerkz 2.x

// your Java 5 code
-------------------
// ... specific annotation @Target and @Retention to control retention and bindings
public @interface AnnotateMe {
    String value() default null;
}

// your source code for an annotated method (same for field, class etc)
@AnnotateMe
public void someMethod() {}

@AnnotateMe("my text")
public void someOtherMethod() {}

// your Java 1.3/1.4 code
//-----------------------

// use of AspectWerkz Annotations.getAnnotation(...) allow runtime visibily
// binding is not limited by the annotation
public interface AnnotateMe {
    String value();
}

// your source code for an annotated method (same for field, class etc)
/**
 * @AnnotateMe
 */
public void someMethod() {}

/**
 * @AnnotateMe("my text")
 */
public void someOtherMethod() {}

// Note for such an annotation that has a single element "value()", the following doclet style syntax is allowed:
// and the annotation value() will return "my text" as for @AnnotateMe("my text")
/**
 * @AnnotateMe my text  
 */
public void someOtherMethod() {}
                

Samples with more complex annotation

// your Java 5 code
-------------------

@interface Complex {
    Class someClass();
    int[] numbers()
}

@Complex(someClass=String.class, numbers={1, 2})
public void someMetod() {}

// your Java 1.3/1.4 code
-------------------------

// just remove the "@" sign
interface Complex {
    Class someClass();
    int[] numbers()
}

// just write it as a doclet and use fully qualified name when an element is a class type
/** @Complex(someClass=java.lang.String.class, numbers={1, 2}) */
public void someMetod() {}
                

New Mixin implementation

We have replaced the old mixin implementation with a completely new and better one. The new implementation mainly gives you two things you did not have before:

  • Serialization of the Mixins -

    The default behaviour is that the mixins will be serialized along with the target class. But you have the option of making the mixins transient (e.g. not serialized with when the target class is serialized), by setting the transient=true flag.


  • Providing your own factory to control the instantiation of the mixin. This means that you can plug in any IoC (dependency injection) framework to handle this for you. The custom factories needs only to implement the org.codehaus.aspectwerkz.aspect.MixinFactory interface.

The mixins can be any regular class, which means that it can be a standalone class, an inner class of a regular class or an inner class in your aspect class.

Three different deployment models are supported, perJVM, perClass and perInstance.

  • perJVM - means that there will be only one mixin instance - like a singleton.
  • perClass - means that there will be one mixin instance per target class.
  • perInstance - means that there will be one mixin instance per target instance.
Note: the perThread deployment model has been left to die in peace. The perJVM deployment model is currently not supported but might come back.

If you are using the default factory implementation you have to either provide an no argument constructor or a constructor that takes the:

  • target class as its only parameter - to be used with the perClass deployment model.
  • target instance as its only parameter - to be used with the perInstance deployment model.
The no argument constructor is mandatory for perJVM deployment model.

Annotation definition

To define the mixin using annotations you have to use the org.codehaus.aspectwerkz.annotation.Mixin annotation. If using Java 5 this is an annotation interface (e.g. @interface) else (for JavaDoc annotations) it is a regular interface.

This annotation has three different parameters:

  • expression (can be used as the default anonymous value) - the pointcut expression that is used to pick out the set of classes to apply this mixin to.
  • deploymentModel (optional) - has to me one of perJVM, perClass or perInstance. If not specified then perInstance is used.
  • isTransient (optional) - defines the mixin as transient or not. If not specified then the default is false, e.g. serializable.

Definition using Java 5 annotations

@Mixin("within(*..domain.*)")
public class MyMixin { ... }

@Mixin(
    pointcut="within(*..domain.*)",
    deploymentModel="perClass",
    isTransient=true
)
public class MyOtherMixin { ... }
                    

Definition using JavaDoc annotations

/** @Mixin("within(*..domain.*)") */
public class MyMixin { ... }

/**
 * @Mixin(
 *   pointcut="within(*..domain.*)",
 *   deploymentModel="perClass",
 *   isTransient=true
 * )
 */
public class MyOtherMixin { ... }
                    

XML definition

The XML definition (in the META-INF/aop.xml file) can be used for three different purposes:

  • Only to define which mixins should be used. Similar to how you declare which aspects should be used.) This can be the option if the mixin is defined in annotations. Then you only need to use the class attribute, e.g. <mixin class="foo.bar.MyMixinImpl"/>. See below for details.
  • To completely define the mixin. See below for all the attributes that can be used.
  • To override and/or resolve ad definition made using annotations. See below for all the attributes that can be used.

To define the mixin using the XML definition, use the mixin element. It has five different attributes that can be used:

  • class (mandatory) - defines which class to use as the implementation class. Has to be the fully qualified name of the class.
  • deployment-model (optional) - defines the deployment model. Can be one of perJVM, perClass or perInstance.
  • transient (optional) - defines the mixin as transient or not.
  • factory (optional) - defines the custom mixin factory implementation to use with the specific mixin.
  • bind-to (optional) - defines to which class set the mixin should be applied to.

Example:

<mixin class="foo.bar.MyMixinImpl"/>

<mixin class="foo.bar.MyOtherMixinImpl"
       deployment-model="perClass"
       transient="true"
       factory="factories.MyMixinFactory"
       bind-to="within(*..domain.*)"/>
                    

Custom Mixin factory

If you need to be in control of how the aspects are instantiated and/or need to pass in additional dependencies or configuration using an IoC (dependency injection) framework or similar. Then you can create custom mixin factory.

The custom factories needs to implement the org.codehaus.aspectwerkz.aspect.MixinFactory:

public interface MixinFactory {

    /**
     * Creates a new perClass mixin instance, if it already exists then return it.
     *
     * @param the target class
     * @return the mixin instance
     */
    Object mixinOf(Class klass);

    /**
     * Creates a new perInstance mixin instance, if it already exists then return it.
     *
     * @param the target instance
     * @return the mixin instance
     */
    Object mixinOf(Object instance);
}
                    

The custom mixin factory can only be defined in the mixin element in the META-INF/aop.xml file.

Retrieval of Mixin instances

You can get a hold of the mixin instances for a specific instance or class through the org.codehaus.aspectwerkz.aspect.management.Mixins class.

Example:

// get per class deplolyed mixin
Object perclassMixin = Mixins.mixinOf(MyMixin.class, Target.class);

// get per instance deplolyed mixin
Object perinstanceMixin = Mixins.mixinOf(MyMixin.class, targetInstance);
                    

Retrieval of Aspect instances

You can get a hold of specific aspect instances using the org.codehaus.aspectwerkz.aspect.management.Aspects class.

Example:

// get per jvm deployed aspect
Object singletonAspect = Aspects.aspectOf(MyAspect.class);
                   ... = Aspects.aspectOf("uniqueNameOfAspect");

// get per class deployed aspect
Object perclassAspect = Aspects.aspectOf(MyAspect.class, Target.class);
                  ... = Aspects.aspectOf("uniqueNameOfAspect", Target.class);

// get per instance deployed aspect
Object perinstanceAspect = Aspects.aspectOf(MyAspect.class, targetInstance);
                     ... = Aspects.aspectOf("uniqueNameOfAspect", targetInstance);
                

Strongly typed proceed

When using around, it can be usefull to modify advised target argument before proceeding to the next advice or target invocation. It is now possible to do it using a strongly typed user defined sub-interface of JoinPoint or StaticJoinPoint.

When using this feature, all you have to write is a public interface with a single method whose signature is Object proceed(< any parameters you need >). Then when writing an around advice, you simply declare this interface to be the first parameter of your advice method, instead of using the regular JoinPoint or StaticJoinPoint interface as described here.

Example:

public class MyAspect {

   // we could inherit StaticJoinPoint instead
   public static interface MyCustomJoinPoint extends JoinPoint {
       Object proceed(String arg0, int[] arg1);
   }

   // see here the use of MyCustomJoinPoint as first argument, and the
   // advice signature be in the order of the MyCustomJoinPoint.proceed(..) method
   @Around("somePointcut(arg0, arg1)")
   // we could write @Around("execution(* Foo.m(..)) && args(arg0, arg1)") as well
   public Object around(MyCustomJoinPoint jp, String arg0, int[] arg1) throws Throwable {
      ...
      // lets change the args :
      return jp.proceed("new arg0", new int[]{1,2,3});
   }
}
                

staticinitialization() pointcut

Since 2.0.RC3, staticinitialization(<type pattern>) pointcut is supported. It can also be used in conjunction with withincode in the form of withincode(staticinitialization(<type pattern>))

Per instance interception

Aspects can be deployed as perInstance as described here. In such a case, every single instance will be advised, even though each one will have a distinct aspect instance.

Per instance interception allow to programmatically add a new interceptor to a given instance only. We name it interceptor since to allow this dynamicity, the semantics are limited and the interceptor must conform to a given interface, hence the regular semantics of advice (like parameter binging with args()) cannot apply.

For this to work, the META-INF/aop.xml definition has to declare which classes should be opened for per instance interception. Fine grained control is provided to only open to a specific set of join points f.e. only execution, only call, get, set or any composition of those get|set|execution or all.

Note that the <advisable... > directive controls the caller side. Only the caller side is made advisable, and will expose the API to add and remove advices. For finer control on the callee side, you should consider using perTarget deployment model for regular aspects.

Then at runtime, you simply cast the instance to org.codehaus.aspectwerkz.intercept.Advisable and use the API provided thru it. The interceptors must implements one of the interface among:

  • org.codehaus.aspectwerkz.intercept.BeforeAdvice
  • org.codehaus.aspectwerkz.intercept.AroundAdvice
  • org.codehaus.aspectwerkz.intercept.AfterReturningAdvice
  • org.codehaus.aspectwerkz.intercept.AfterThrowingAdvice
  • org.codehaus.aspectwerkz.intercept.AfterAdvice

The API allows to programmatically bind an interceptor to a new pointcut, which will be implicitly narrowed to the advisable instance. It is possible to use anonymous inner interceptors: Example for the definition:

<aspectwerkz>
    <system id="tests">
        <advisable pointcut-type="execution" expression="within(test.intercept.execution.InterceptTest)"/>
        <advisable pointcut-type="call" expression="within(test.intercept.call.Caller) && target(test.intercept.call.Callee)"/>
        <advisable pointcut-type="set" expression="within(test.intercept.set.InterceptTest)"/>
        <advisable pointcut-type="get" expression="within(test.intercept.get.InterceptTest)"/>
        <advisable pointcut-type="call|execution|set|get" expression="within(test.aspect.MemberMethodTestAspect)"/>
        <advisable pointcut-type="all" expression="within(test.aspect.AllMemberMethodTestAspect)"/>
        ...
        ... regular aspect / mixin definition
    </system>
</aspectwerkz>
                
It is also possible to narrow the expression using all regular pointcut designators (except cflow).

Example for the runtime API:

// Caller has been defined as Advisable in the aop.xml
public class Caller {

  public void demo() {

    Callee callee = new Callee();
    // Callee was declared as a possible target for "call" pointcuts in Caller
    // hence any call of Callee by a Caller instance is able to handle interceptor addition / removal

    // this call will occur with regular aspects bounded - if any
    callee.adviseWithAround();
    callee.adviseWithAround(1);

    // lets add an around interceptor to the "this" Caller instance
    // and for the the call to Callee as expressed in the given pointcut
    // Note that the change will depend on the advisable definition
    // Here it was <advisable pointcut-type="call|execution" ...> and thus
    // any aw_addAdvice(..) on f.e. get / set pointcut will be useless
    ((Advisable) this).aw_addAdvice(
        "call(* test.intercept.call.Callee.adviseWithAround(..))",
        // we use an anonymous inner class here
        new AroundAdvice() {
            // notice the generic invoke() method defined in the
            // org.codehaus.aspectwerkz.intercept.AroundAdvice interface
            public Object invoke(JoinPoint jp) throws Throwable {
                InterceptTest.log("around1_pre_call ");
                Object result = jp.proceed();
                InterceptTest.log("around1_post_call ");
                return result;
            }
        }
    );

    // this call will occur with regular aspects bounded - if any
    // and with the new around advice (with lower precedence)
    callee.adviseWithAround();
    callee.adviseWithAround(1);

    // this call on another instance of Caller will not be affected by the new around advice
    Caller caller = new Caller();
    caller.callAdviseWithAroundOnTheGivenCalleeInstance(callee);

    // we can now remove the advice
    // it will remove the first advice found of the given class type
    // thus cannot really be used when an anonymous class has been used,
    // but using the interface enables us to remove ALL around advice.
    // Again we narrow the pointcut
    ((Advisable) this).aw_removeAdvice("call(* test.intercept.call.Callee.adviseWithAround(int))", AroundAdvice.class);

    // will still have the interceptor since it is another instance of Caller
    caller.callAdviseWithAroundOnTheGivenCalleeInstance(callee);

    // will not have the interceptor any more : adviseWithAround(int) signature
    callee.adviseWithAround(2);
                

Note: constructors are not made advisable ie open for this programmatic interception for now (2.0)

AWProxy: proxy awareness of aspects

Sometimes it is not possible to use the weaver be it with offline mode (post compilation) or online mode (load time weaving). In such a case, you may still want to make use of a subset (at least) of AOP concepts. AspectWerkz provides its own proxy implementation to make use of the aspects (unchanged) with proxies. The implementation relies on subclassing hence it is not needed to use interface based proxies.

For a tutorial on AWProxy, you can read this blog article that explains some more details about the API.

The distribution includes out of the box support for org.codehaus.aspectwerkz.proxy.Proxy. Just include the aspectwerkz.jar, aspectwerkz-core.jar and aspectwerkz-jdkXXX.jar in your classpath, have your aspects declared in (one or more) META-INF/aop.xml available in the classpath (defined thru annotations or XML - the usual documentation applies), and start up the application as usual (no JVM option for weaving - though it is compatible as well).

Then to obtain a proxy of a class that is aware of the aspects available in the application classpath (following the regular deployment hierarchy rules according to classloader delegation described here) invoke the org.codehaus.aspectwerkz.proxy.Proxy API. You will get a proxy of the given class that will be aware of the aspects.

Note that only execution pointcuts will be applied (for both methods and constructors), though field get / set , call and handler pointcuts may have been defined within your aspects. This is the tradeoff of using proxy.

The advantages of using this approach is that you stick to one sole model to write your aspects and immediately obtain proxy aware of them without any changes. All pointcuts semantics like args(), this(), targe() apply, as well as composition with within(), and cflow().

The performance is as good as for regular weaved class, and actually better than CGLIB proxies. You can read more about that in our AWBench project (see AW Proxy).

Example (assuming you have some aspects in the classpath):

public class TraceMe {

    public TraceMe(String name) {
    }

    public void step1() {
        step2();
    }

    // this method is final hence won't be exposed in the proxy
    public final void step2() {
    }

    public static void main(String[] args) {
        // obtain a proxy that would be aware of the aspect(s) on "TraceMe"
        // TraceMe traceMe = new TraceMe("foo") is replaced by (regular reflective syntax)
        TraceMe traceMe = (TraceMe) Proxy.newInstance(
                TraceMe.class, new Class[]{String.class}, new String[]{"foo"}
        );
        traceMe.step1();
    }
                

Note that it is perfectly possible to use the per instance interception feature with proxies. Read more about it here.

AWProxy also provides a modified version of CGLIB so that CGLIB proxy are made aware of the AspectWerkz aspects. This version is not part of the distribution and you have to checkout it from the CVS, build it and include it first in your classpath so that your regular CGLIB becomes aware of it. This may not work if your CGLIB version has been repackaged - hence refer to the source code to rename the package as well.

CGLIB patch for AspectWerkz integration is available here To build a jar from this patch, checkout the CVS and run the following:

// in root folder of the distribution

// adapt for unix / mac
set ASPECTWERKZ_HOME=.

// build AW
ant clean dist

// build the CGLIB extension
cd src
cd cglib-ext
ant clean dist

// the jar is in src/cglib-ext/target/