One of the new features in the AspectWerkz 2 architecture is the ability to deploy and undeploy aspects at runtime, e.g. to do “hot” deployment and undeployment.
The implementation is based on HotSwap class redefinition and handles ‘change sets’ which ensures that the changes to each single join point is atomic. What this means in practice is that if you for example are deploying an aspect which has one before and one after advice, both advising the same join point (method/field etc.) then either both of them will be added or none.
The API exposed to the user is very simple and straight forward to use. Everything is handled by the Deployer class, which provides the following API for runtime deployment and undeployment of aspects:
As you can see you can for example choose to deploy an annotation defined aspect, e.g. a regular Java5 class with AspectWerkz defined annotations (or a regular Java 1.3/1.4 class with AspectWerkz defined JavaDoc annotations, compiled with AspectWerkz’s JavaDoc annotation compiler):
in the context classloader (just specify the aspect class)
in an arbitrary class loader (this option is most useful for application server vendors that can garantuee that all the dependecies can be resolved)
within a specific DeploymentScope (more about this in a minute)
You can read more about how you can use the DeploymentHandle (that is returned from each of these methods) here.
You also deploy a class without any annotation definition and pass in the definition in XML format. Here you pass in a string containing the
Use-case: Implementing a JMX monitoring aspect
Now let’s take a look at a concrete example on how to hot deploy an aspect that can do some monitoring for us, report that to an JMX MBean and then undeploy it when the monitoring is done.
Writing the aspect
So first, here is the (main parts of the) JMX monitoring aspect:
As you can see, this aspect is defined using Java5 annotations, but if you are using Java 1.3/1.4 you can define the annnotations using JavaDoc (just put the annotation exactly as it is in the JavaDoc for the method/class) and run AnnotationC on the class. Then the rest in this article should stay the same. You can read more about the AnnotationC compiler here.
Hot deploy and undeploy the aspect
As you can see we have added two static methods to the aspect, these will be used to enable and disable the response time aspect. E.g. deploy it and undeploy it.
First we have the enableMonitoring method, which enables monitoring in our application, e.g. deploys the monitoring aspect. This method can be invoked at any point in time, all threading issues etc. are handled by the underlying implementation. Here we make use of the XML definition to resolve the annotation definition in the aspects by defining the “abstract pointcut” that we have bound the advice to (the invocationsToMonitor pointcut).
Second we have the disableMonitoring method which disables monitoring for us, e.g. undeploys the aspect from all the join points where it had been defined (see the concept of DeploymentHandles for a more fine grained undeployment API).
Use the aspect to monitor JDBC SQL statements
This means that in the user code, all we need to do to enable and disabling this aspect is to invoke these two methods. Here we will monitor the executions of all JDBC statements:
What this pointcut expression (call(* java.sql.Statement+.execute*(..))) actually means is:
Pick out all method calls (call(..))
to alll methods that has a name that starts with execute (execute*)
that can have any parameter types ((..)) or return type (*)
that are in a concrete class that implements the java.sql.Statement interface (java.sql.Statement+)
By basing the pointcut expression on an interface we can be igonrant of how the actual JDBC driver is implemented (concrete subclasses etc.).
Note: we could in this specific use-case benefit from retrieving and storing the parameter value to the execute*(..) methods, since it is the actual SQL query that we are monitoring the execution time of. This could be done really easy but is out of scope for this article.
So now after doing some monitoring you should be able to easily see the data in any JMX console. For example hook in JConsole which you can read more about in this article.
Define a deployment scope
Finally, to get this to work in the general case we need to define a deployment scope which defines the set potential join points that we might want to monitor at runtime.
This construct gives you (as a application developer or vendor) control over which join points are exposed to the hot deployment API i.e. helps to prevent usage of the hot deployment facilities in specific areas of the application.
This can be done either in the META-INF/aop.xml file or using annotations in the aspect class. In this case we want to make the aspect generic so we will define it in the external XML definition:
Here we have defined a deployment scope that picks out all method calls in our sample application, which means that we can safely deploy our ResponseTimeAspect at these points. (We could have added something like AND within(package..*) to the expression if we wanted to narrow down the scope.)
In the code above we have an implicit contract that says that we can not define the aspect to be deployed outside a deployment scope. If we do not define a pointcut that is wider than this it is all fine. But we can now use this deployment scope to ensure that we only deploy the aspect at valid points. To do that we need to retrieve a handle to the scope like this (can be done at any point):
Now we can use the DeploymentScope handle to make a more explicit deployment (e.g. knowing exactly the points where our aspect will be applied):
Notes on the impact of the deployment scope preparation
The preparation made by the deployment scope construct means in practice that we are simply adding a level of indirection by adding a call to a public static final method that redirects to the target join point (method, field, constructor). This method will be inlined by all modern JVMs.
When we do undeploy of an aspect, if this aspect is the last one that affects the join point, then the class’ bytecode will be reverted to the same state it had before any aspect was deployed.
More info about the JMX ResponseTimeAspect
The code for the monitoring aspect is based on the implementation of the ResponseTimeAspect in the AWare Project. The only difference is that in this example I am defining it using Java5 annotations and I have added the two static methods for doing the deployment and undeployment. You can read more about this aspect here and the source code for it can be found here.
You can also check out the complete AWare project which has some tests for the JMX module. Info about how to check out the sources can be found here.
More info about the Deployer Module
I have not written any specific sample application for this article but if you want you can look at and run the tests for the deployer module in the AspectWerkz distribution. You can download the distribution here. The tests are in ./src/jdk15/test directory and you can run them by invoking ant test:jdk15 when standing in the AspectWerkz distribution’s root dir.
The aspectwerkz*.jar jars and the dependency jars are in the ./lib folder in the AspectWerkz distribution.