Startup and transformation issues

AspectWerkz support three different ways of transforming your code. All of these are managed using the command line tool aspectwerkz which resides in the ASPECTWERKZ_HOME/bin directory.

The aspectwerkz command line tool

When you start up your application using the aspectwerkz command line tool instead of using the java command, AspectWerkz will intercept the normal class loading mechanism and be able to transform classes loaded by any class loader except the bootstrap class loader.

The command line tool is one of the several ways to enable on the fly weaving. For a complete description read the online architecture section.

The command line tool also allows for offline post-processing of classes.

The aspectwerkz program assumes that both the ASPECTWERKZ_HOME and JAVA_HOME enviromental variables are set.

The options are:

  • Starting up an application:

    aspectwerkz [-options] <main class> [args...]


  • Starting up an application using an executable jar file:

    aspectwerkz [-options] -jar <jar file> [args...]


  • Post-process all class files in a specific directory (recursively), jar or zip file:

    aspectwerkz -offline <definition file> [-verbose] [-verify] [-haltOnError] [-cp <classpath>]* <target classes to transform>+

    The 'definition file' option is the path to the definition file.

    Turns on -verbose to follow each step of the processing.

    Turns on -verify to verify all weaved class according to the Java Class Format specification. Note that this can slow down the processing.

    When an error occurs, all classes of the current target are restored to their initilal state and the processing goes on with the next target unless -haltOnError was set. In this case the processing stops. Else a complete status about successfull and failed targets is printed at the end of all target processing.

    The 'classpath' option is the classpath needed for compilation but not to be transformed (eg -cp myAspects.jar;servletAPI.jar). It is possible to use several -cp <classpath> option instead of doing concatenation.

    The 'target classes to transform' option specifies the classes to transform. It must the path to the root directory of the classes you want to transform, or a jar file or a zip file. Several targets can be passed in one call (eg toTransform.jar /classes otherToTransform.jar).

    Note: when you then are running the application (without using the aspectwerkz command) you have to (apart from feeding it with the definition) put the aspectwerkz jar along with all the the dependency jars the ASPECTWERKZ_HOME/lib directory in your classpath.


Invoke the aspectwerkz command line tool without any arguments to get a more detailed usage description.

In all three cases you have to feed the system with your definition. See the Loading the definition section for a detailed description on how to do this.

Ant task for offline post-processing

For the -offline mode there is an Ant task that you can use for simplifying your build process. (It might be useful to run the ASPECTWERKZ_HOME/bin/setEnv.{bat|sh} script first.)

Please not that this Ant task uses exec and is using bin/aspectwerkz. It is provided in 0.8 for backward compatibility with 0.7.x and will be refactored in the next release. It is possible to use directly the main class org.codehaus.aspectwerkz.compiler.AspectWerkzC.

In order to use the Ant task you have to first define the task and then create a target that invokes the task.

Example:

<target name="offline" depends="init">
    <offlineTransformation
        aspectWerkzHome="${basedir}"
        definitionFile="${basedir}/src/samples/samples.xml"
        classesToTransform="${basedir}/target/samples-classes"
        classPath="${basedir}/lib"/>
</target>

<taskdef name="offlineTransformation"
    classname="org.codehaus.aspectwerkz.task.OfflineTransformationTask"
    classpath="${aspectwerkz.classpath}"/>

Loading the definition

The AspectWerkz system needs to know where it can find the aspect definition.

You have two options depending on your needs and settings:

  • Feed the system with the XML definition using the:

    -Daspectwerkz.definition.file=<path_to_definition_file> command line option.


  • Put the definition in a file called exactly aspectwerkz.xml and put it on the class path. Then the system will try to load the definition as a resource.


You can validate the definition at runtime by defining the:

-Daspectwerkz.definition.validate=true option.

See the Validate definition section for details.

Validate definition

AspectWerkz has a build in definition validator that can help you catch errors in the XML definition as well as in the attributes at compile time.

This feature is turned off by default and you can switch it on by defining the:

-Daspectwerkz.definition.validate=true option.

To be able to validate you have to have all the advices and introductions on the classpath. This is needed since the definition validator checks if the classes defined actually exists.

Even if you don't compile a weave model before starting up the system you can use the definition validator which will then validate the XML definition at runtime before proceeding (but it will not abort on errors).

Hot deployment and reconfiguration

AspectWerkz supports hot deployment and reconfiguration of both Advices and Introductions. It can actually create, add, remove and rearrange Advices as well as replace an introduced implementation at runtime. The target classes does not have to be reloaded or transformed again.

The AspectWerkz system is retrieved by invoking the AspectWerks.getSystem(uuid). The uuid is the same UUID as specfied when compiling the weave model. If an auto-generated has been used the generated UUID can be read in the file name of the weave model (weaveModel_<UUID>.ser). If no weave model has been compiled (only using the XML definition) then you can retrieve the system by invoking AspectWerkz.getDefaultSystem().

Examples

  • For replacing an Introduction implementation use:
    SystemLoader.getSystem(uuid).getMixin("mixins/Mixin").
            ___AW_swapImplementation("mixins.NewMixinImpl");
    
    (the new implementation has to implement the same interface as the previous one)


  • For creating a new Advice use:
    // The parameters are:
    // 1. the name of advice
    // 2. the class name of advice
    // 3. the deployment model
    // 4. an optional user defined classloader (can be null)
    
    ((XmlDefSystem)SystemLoader.getSystem(uuid)).createAdvice(
            "advices/NewAdvice", "advices.NewAdvice", "perInstance", myClassLoader);
    


  • For removing an Advice from a specific pointcut use:
    List methodPointcuts = SystemLoader.getSystem(uuid).getAspectMetaData("MyAspect").
                    getMethodPointcuts(className, methodMetaData);
    for (Iterator it = methodPointcuts.iterator(); it.hasNext();) {
        MethodPointcut pointcut = (MethodPointcut)it.next();
        if (pointcut.hasAdvice("advices/NewAdvice")) {
            pointcut.removeAdvice("advices/NewAdvice");
        }
    }
    


  • For adding a new Advice to a specific pointcut use:
    List methodPointcuts = SystemLoader.getSystem(uuid).getAspectMetaData("MyAspect").
                    getMethodPointcuts(className, methodMetaData);
    for (Iterator it = methodPointcuts.iterator(); it.hasNext();) {
        MethodPointcut pointcut = (MethodPointcut)it.next();
        if (pointcut.getExpression().equals(expression)) { // check that we are at the right pointcut
            pointcut.addAdvice("advices/NewAdvice");
        }
    }
    


  • For changing the order of the Advices at a specific pointcut use:
    List methodPointcuts = SystemLoader.getSystem(uuid).getAspectMetaData("MyAspect").
            getMethodPointcuts(className, methodMetaData);
    for (Iterator it = methodPointcuts.iterator(); it.hasNext();) {
        MethodPointcut pointcut = (MethodPointcut)it.next();
        List advices = pointcut.getAdviceIndexTuples();
    
        ... // change the order of the advices
    
        pointcut.setAdviceIndexTuples(advices);
    }
    


All these operations are fully thread-safe.

Remote Proxy Server - RPC mechanism

AspectWerkz provides an RPC mechanism that allows you to use the instrumented objects (advices and mixins) seamlessly on a remote client site.

Client side

You can use the remote proxy on the client side in two ways:

  • create a client proxy that creates a matching instance on the server. The client now has seamless access this new specific instance on the server.

    Example:

    ...
    // creates a new remote proxy for the MixinImpl class which
    // maps to an instance of this class on the server
    RemoteProxy remoteProxy = RemoteProxy.createClientProxy(
            new String[]{"examples.connectivity.Mixin"},
            "examples.connectivity.MixinImpl",
            "localhost",
            7777
    );
    
    // retrieves the proxy to the MixinImpl instance
    Mixin mixin = (Mixin)remoteProxy.getInstance();
    
    // invoke methods on the proxy (executed on the server)
    System.out.println("Mixin says: " + mixin.test1());
    
    // close the proxy (close() must always be called)
    remoteProxy.close();
    ...
    



  • create an instance of some class on the server-side, wrap it with a RemoteProxy and send to the client over the wire. The client then have access to this specific instance on the server.

    Server side:

        ...
        // wrap an instance in a remote proxy on the server-side and send it to the client
        RemoteProxy remoteProxy = RemoteProxy.createServerProxy(anotherMixinInstance, "localhost", 7777);
        return remoteProxy;
    }
    

    Client side:

    ...
    // retrieve the proxy to the specific instance created on the server
    RemoteProxy remoteProxy = mixin.getMixin();
    
    // retrieves the proxy to the instance on the server
    AnotherMixin anotherMixin = (AnotherMixin)remoteProxy.getInstance();
    ...
    

A sample have been provided and can be found in the src/samples/examples/connectiviy directory. Start by taking a look at the Client.java class. You can run the sample by executing maven aspectwerkz:samples:remote:server to start up the server and then in another console execute maven aspectwerkz:samples:remote:client to start the client. (When you close down the server using ^C then the server listener threads are still hanging so you need to close down them manually.)

Server side

The remote proxy server is a multi-threaded production-ready implementation that is ready to serve any number of incoming requests. To configure the server settings (number of listener threads, max/min/init size of the thread pool, backlog etc.) you have to change the settings in the in the aspectwerkz.properties resource bundle and put it on the classpath. If no bundle is found default values are used.

The server resource bundle has the following format/options:

remote.server.hostname=localhost
remote.server.port=7777
remote.server.client.threads.timeout=60000
remote.server.listener.threads.nr=10
remote.server.listener.threads.backlog=200
remote.server.listener.threads.run.as.daemon=false
remote.server.thread.pool.type=bounded # possible types are bounded or dynamic
remote.server.thread.pool.max.size=100
remote.server.thread.pool.init.size=10
remote.server.thread.pool.min.size=10
remote.server.thread.pool.keep.alive.time=300000
remote.server.thread.pool.wait.when.blocked=true
remote.server.invoker.classname=

To enable support for RPC in the AspectWerkz system you have to feed the JVM with the -Daspectwerkz.remote.server.run=true option.

If you have specific requirements. For example if you want to handle the user context that you (optionally) can set on the client RemoteProxy and have sent over or have other requirements on how you want to invoke you objects then you can create your own implementation of the org.codehaus.aspectwerkz.connectivity.Inovoker interface and specify the implementation in the aspectwerkz.properties resource bundle using the remote.server.invoker.classname=.. option. For an example implementation see the org.codehaus.aspectwerkz.AspectWerkz.getDefaultInvoker() method.

Thread safety

AspectWerkz is generally thread-safe.

There is only one rare case where you have set it explicitly and that is when you are spawning a new thread inside an Advice. Then you have to call joinPoint.proceedInNewThread() instead of the normal call to joinPoint.proceed().

Here is an example taken from the Asynchronous calls example in the Examples section.

public Object execute(final JoinPoint joinPoint) throws Throwable {
    m_threadPool.execute(
            new Runnable() {
                public void run() {
                    try {
                        // this call is made in a new thread
                        joinPoint.proceedInNewThread(); // using proceedInNewThread

                    } catch (Throwable e) {
                        throw new WrappedRuntimeException(e);
                    }
                }
            }
    );
    return null;
}

Pluggable container implementation

You have the possibility of providing your own advice or introduction container implementation. This can be useful if you for example would like to make the advices and/or introductions persistent.

To create a custom container you only need to implement the org.codehaus.aspectwerkz.advice.AdviceContainer or the org.codehaus.aspectwerkz.introduction.IntroductionContainer interface. The custom implementation then need to be fed to the system using the -Daspectwerkz.introduction.container.impl=... or the -Daspectwerkz.advice.container.impl=... option.

If you don't provide a custom implementation the default one will be used.

For an example on how to create a persistent container using Prevayler as the persistence engine see code in the ASPECTWERKZ_HOME/sandbox dir.