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.
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:
aspectwerkz [-options] <main class> [args...]
aspectwerkz [-options] -jar <jar file> [args...]
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.
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}"/>
The AspectWerkz
system needs to know where it
can find the aspect definition.
You have two options depending on your needs and settings:
-Daspectwerkz.definition.file=<path_to_definition_file>
command line option.
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.
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).
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()
.
Introduction
implementation use:
SystemLoader.getSystem(uuid).getMixin("mixins/Mixin"). ___AW_swapImplementation("mixins.NewMixinImpl");
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);
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"); } }
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"); } }
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.
AspectWerkz
provides an RPC mechanism that allows you to use the
instrumented objects (advices and mixins) seamlessly on a remote client site.
You can use the remote proxy on the client side in two ways:
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(); ...
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.)
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.
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; }
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.