AspectWerkz 2.0: An Extensible Aspect Container

Java Development News:

AspectWerkz 2.0: An Extensible Aspect Container

By Jonas Boner

01 Nov 2004 | TheServerSide.com

Abstract

In this article, Jonas Bonér introduces the new AspectWerkz 2.x architecture. AspectWerkz 2.x has been designed to be an extensible AOP container where any kind of aspects can coexist ranging from Spring aspects to AspectJ aspects. The article explains why this architecture is beneficial to the Java AOP communtity and explains the benefits and drawbacks of running AOP Alliance, Spring and AspectJ aspects within the AspectWerkz Extensible Aspect Container. It concludes with performance figures.

AspectWerkz 2.0.RC1 is already available for download here.

Introduction

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), but also some risks since it can be hard for one single container implementation to handle both optimal performance and the full expressiveness of all the supported AOP implementations.

Container architecture

If we try to illustrate what happens when a method has been advised by a before advice on execution (callee) side, the sequence diagram for the resulting invocation is as follows.

This first diagram shows a regular unadvised method invocation:

regular method invocation

In this diagram we have the same method invocation when it is advised by a before advice. The aspect in this example is deployed as perTarget() (one aspect instance should be created per advised callee instance):

method invocation advised by a before advice

This sequence diagram will be the same no matter how the advice has been implemented. The extension implementation for this aspect model needs to define parts like: "how to get an aspect instance?", while some other parts, like: "how to get the advised member arguments?" are independent of the implementation details of the aspect and should be part of container.

The container is responsible of handling the semantic differences between the different aspect models to allow the aspects to coexist in one single runtime environment. AspectWerkz 2.x architecture follows this principle and supports aspect models extension, so that both AspectJ, Spring, AOP Alliance and AspectWerkz aspects can be handled and deployed in a consistent single runtime. Virtually any kind of aspect model can be added to it.

Here is an illustration that shows the high-level architecture of the AspectWerkz Extensible Container and how it interacts with the Aspect Model Extensions and the weaver. Each extension is seen as an Aspect Model and is implemented by implementing the AspectModel interface, but more on that later.

AspectWerkz Extensible Aspect Container Architecture

AOP Alliance extension

One of the extensions that we have already implemented is one that supports aspects that implements the AOP Alliance interfaces.
This means all aspects written for:

Apart from benefits of having one single weaver and container, one intermediate representation and one container (which handles deployment modules, security, isolation, management etc.) there are many interesting aspects on how we can enhance the user experience of these frameworks.

We will continue this discussion using Spring as example, but the same concepts apply to all these frameworks (apart from perhaps JAC, which has a completely different approach).

Spring extension

Spring (and all frameworks listed above apart from JAC) only supports a subset of AOP, a subset that is in many cases sufficient for the needs of your application. It does a good job at implementing some of the standard J2EE services and being a starting point for users that are starting to learn AOP. However, when users get more experienced they tend to feel restricted and there are many situations where you need to utilize the rich semantics defined by AspectJ (and implemented by f.e. AspectWerkz and partly by JBoss AOP).

But, what is interesting and important is that the limitations these framework have is not necessary in the programming model (the way you implement, define and instantiate the aspects), but how they are weaved. All of them (apart from JAC) are using a proxy based approach to the weaving and this is what mainly limits the expressiveness (although the AOP Alliance and proxy based approaches have not designed an Invocation object that represents a field access/modification or catch handler join points).

In the Spring extension we have added support for the features available in Spring but not in AOP Alliance, f.e. before and after advice.

Richer semantics and expressiveness

Using the AspectWerk Extensible Container we now can take full advantage of the semantics defined and implemented by AspectWerkz. This includes advising:

  • method execution
  • method call
  • constructor execution
  • constructor call
  • field access
  • field modification
  • catch handlers
  • static initialization

Currently Spring (and all other proxy based implementations) only supports:

  • method execution
  • constructor execution

For Spring users this means being able to add the pre-packaged transaction interceptor on the caller (client) side, implement client side authentication (reduce network traffic) or implement generic transparent persistance by advising field modification and much more.

To make use of these richer semantics in Spring we need a way to define and handle them. We can do that in two ways, either we extend the Spring bean definition to understand and support the new semantics (f.e. add the caller instance to Spring's Invocation class) or we define our Spring aspects using the AspectWerkz definition, either using Annotations or XML. The latter approach actually works really smooth (and we can make it even better if we make use of the SpringAspectContainer to let Spring handle the instantiation, dependency injection and life-cycle management for the aspects).

Better performance

Since the weaver in AspectWerkz 2.x is compiling everything statically, meaning that the output of the weaver is code that is statically compiled and verified, Spring (and any other framework) will benefit from that.

Statically compiled code can be highly tuned and optimized and no reflection is needed. This means that we don't have to perform all the expensive plumbing needed in a reflective model.
E.g.:

  • no casting
  • no boxing and unboxing of primitives
  • no wrapping (and unwrapping) of parameters in an object array that needs to be allocated at each invocation (and maintained throughout the invocation flow)
  • no reflective invocations
  • etc.

Apart from this, statically compiled code tends to be more predictable than code that is reflectively evaluated.

For details on the performance improvements see the benchmark in the appendix at the end of this article.

AspectJ extension

We have also implemented support for running AspectJ aspects inside the AspectWerkz Extensible Container. The aspects on this are very different compared to the Spring extension. AspectJ still supports a richer set of semantics than AspectWerkz so there will be certain aspects that we will not be able to run, but we will on the other hand give the AspectJ users a new dimension of dynamicity (for a large subset of their aspects).

Load time weaving

When using the AspectJ extension the AspectJ aspects can be weaved into the target application at load time, using AspectWerkz's production stable implementation that is verified to work on all major application servers and JVMs on the market, for Java 1.3 up to Java 5.

Hot deployment and undeployment

The AspectJ users will have the possibility of doing hot deployment and undeployment of the aspects, a feature that is based on true runtime weaving and unweaving in the AspectWerkz 2.x architecture.

Here are some examples on how to use the API:

    // deploy a specific aspect in a specific class loader (will propagate down to all its children)
    Deployer.deploy(Aspect.class, loader);
    ...

    // undeploy the specific aspect from a specific class loader
    Deployer.undeploy(Aspect.class, loader)


    // deploy an aspect and store a handle to the deployment event
    DeploymentHandle handle = Deployer.deploy(Aspect.class);
    ...

    // revert the changes made by the deployment defined by the handle
    Deployer.undeploy(handle)

Deployment modules and class loader awareness

The AspectJ aspects can be packaged in deployment modules that can be deployed at different levels in the class loader hierarchy and will inherit the visibility and isolation of the class loader they are deployed in (just like AspectWerkz's deployment modules packaged with the META-INF/aop.xml deployment descriptor).

Deployment time configuration

The users of AspectJ can choose to override the pointcut definition in the aspects using external XML deployment descriptors (the META-INF/aop.xml file). Something that will allow late binding, e.g. the possiblity of taking certain decisions at deployment time and not at compile time. This is something that can be very beneficial in some cases in enterprise application development but is a highly debated topic and should be use with care.

Custom extension

If you want to plug in your own AOP or interceptor framework into the AspectWerkz Extensible Container then all you have to do is to implement the org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel interface and the container will manage the weaving, deployment, life-cycle management etc.

You need a way of registering your extension and this can be done by adding the fully qualified name of the implementation class to the -Daspectwerkz.extension.aspectmodels=... VM option. Autodetection of aspect models might be added later.

This is the API defined by the AspectModel interface which is the only interface that the extensions needs to implement:

public interface AspectModel {
    String getAspectModelType();
    boolean requiresReflectiveInfo();
    AroundClosureClassInfo getAroundClosureClassInfo();
    void defineAspect(..);
    void createMandatoryMethods(..);
    void createInvocationOfAroundClosureSuperClass(..);
    void createAspectReferenceField(..);
    void createAspectInstantiation(..);
    void createAroundAdviceArgumentHandling(..);
    void createBeforeAdviceArgumentHandling(..);
    void createAfterAdviceArgumentHandling(..);
}

The road ahead

Having one single intermediate representation for the aspect (definition/model/plan), regardless of how it is implemented, defined and weaved, and a well-defined API to work with this definition opens up for standardization on tools, both for development and runtime manageability.

Support for other frameworks, f.e. JBoss AOP which has a programming model that is somehow similar to AspectWerkz's, would be easy to implement. This is left as an exercise to the JBoss AOP users.

AspectWerkz Extensible Aspect Container allows to have this single intermediate representation, single container and single weaver, while making use of any aspect programming model. What would be even more interesting would be to have one single container and internal representation but make the weaver pluggable. Meaning being able to plug in different strategies of doing the actual weaving. This would be a natural step to integrate for example VM weaving, which we believe is the place where the weaving ultimately belongs. This illustration shows how the weaving mechanism can be made pluggable using different weaving strategies:

AspectWerkz Extensible Aspect Container Architecture - Weaving Strategies

Notes on compatibility:

  • Spring extension - supports all advice apart from ThrowsAdvice. Full compatibility will be implemented if the user community finds it of interest.
  • AspectJ extension - still only a proof-of-concept. It does f.e. not support, args(), target(), this(), thisJoinPoint, thisStaticJoinPoint and inter-type declarations. This is something that will be implemented if the user community finds it interesting and worth the effort.

Appendix

Benchmark

We have done a microbenchmark between the AspectWerkz container extensions to AspectJ and Spring, and the regular AspectJ, Spring, JBoss AOP and AspectWerkz frameworks.

The numbers in the table shows number of nanoseconds per iteration, where one iteration is the invocation of the advice and the advised method. All advice are doing the same amount of work (incrementing a public static int in another class).

Benchmarks where run on a Pentium M 1.60 GHz, 1.0 Go RAM running Java 1.4.2 (HotSpot) and Windows 2000 SP4 without any specific JVM settings.

AspectWerkz 2.0 RC1 Spring 1.1.1 Spring extension in AspectWerkz AspectJ 1.2 AspectJ extension in AspectWerkz JBoss 1.0 Non-advised invocation
before advice (1) 10 535 40 15 10 135 5
before advice - argument access 10 605 205 (2) 10 N/A (3) 195 5
around advice 70 465 70 15 95 (3) 125 6
around advice twice - argument access 80 661 460 (2) 45 N/A (3) 295 5

Notes :

  • (1) Using an after advice gives the same results.

  • (2) Spring extension - for the user to get all the benefits from the statically compiled approach he needs to make use of direct access to contextual information in the advice. This means f.e. adding the arguments he is interested of to the advice signature, something that will break the Spring API which relies on specific advice method signatures, in particular a the requirement to pass in an object array with the wrapped arguments. It could perhaps be solved using delegation, and lazy and optional creation of the object array.

  • (3) AspectJ extension - the before advice and all the after advice types has the same performance as when using the regular AspectJ weaver (ajc).
    The reason why the around advice is so much slower than both the regular AspectJ version and the AspectWerkz version is because:
    - First, the regular AspectJ compiler inlines the advice body in the target class, e.g. no closure is created (which is something that the AspectWerkz compiler can't do if it wants to keep the option of possible redefinition of the aspect model. This is the tradeoff for providing dynamicity, runtime weaving and multiple weaving support).
    - Second, the AspectJ compiled aspect in itself is poorly optimized.
    Note that argument access is not yet implemented in the AspectJ extension of the AspectWerkz Extensible Container.

Biography

Jonas Bonér is a Senior Software Engineer at the Java Runtime Products Group, BEA Systems. He is the founder of the AspectWerkz AOP framework and an active contributor to the Java Open Source community.