Wednesday, August 10, 2005

AOP weavers - are we doing it wrong?

(imported from http://blogs.codehaus.org/people/avasseur, read comments there)

These last days I have been prototyping around an interesting idea.


As you know we have been working on an API to add JVM support for AOP in JRockit. The prototype will be available any time soon.


The nice thing about it is that you don't manipulate the bytecode anymore and that you are using only well known java.lang.reflect.* API to tell the JVM if your pointcut is matching or not. This is somehow similar to what ones can do with Spring AOP - as this one is proxy based (see f.e. MethodMatcher API in Spring).


The immediate benefit of it is that first there is no need to have another in-memory representation of classes beeing weaved (before they get loaded) that is backed by some expensive (both memory and CPU) bytecode analysis.
This advantage is detailled in our JVM support for AOP part1 article.


As a consequence it is really easy to query the method annotation, generics properties, and such - something that is extremely complex to achieve with acceptable overhead in regular bytecode based weaver such as AspectJ or AspectWerkz (actually more complex than changing some bytecode instruction).


So what is that idea I had ?


Having AOP support in JRockit is nice, but it will take some time before that gets mainstream (with eventually a JSR etc). In this transition period, there must be a way to implement a better bytecode based weaver that will perform way better than current weavers (both AspectJ and AspectWerkz), that will be easier to implement, and whose only requirement is Java 5 (well off course, it won't be as good as JRockit JVM support for AOP - so it is still a transition technology).


I have thus been sketching on an hybrid system that makes extensive use of the hotswap API, and whose actual weaver relies only on pointcut matching backed by the java.lang.reflect.* API ie does not build any kind of equivalent structure backed by bytecode analysis.


As such the memory overhead is zero, and the CPU overhead is way less than current AspectJ and AspectWerkz.


The overall idea is quite simple and consists in 2 phases:


Phase 1


A first weaver is changing the bytecode in some stable way - such that all classes are transformed (lets say prepared) the same way - while not introducing any dependancies on any kind of AOP, and while not adding any kind of performance overhead (ie no changes in the execution flow such as introduced by wrappers method and such usually used when implementing instrumentation needed for around advice).


All classes thus get loaded as expected with a very limited time overhead and no memory overhead at all (thanks to the excellent ASM performance).


Phase 2

Then when an actual class gets loaded (as per regular application behavior) I get a small callback invoked when this class has just been loaded and just before anything else happens (ie the class static initializer invoked by the JVM). Current load time weavers do things "just before the class gets loaded" and as such cannot access the java.lang.reflect representation of what they weave.


This callback can then perform the actual weaving by relying entirely on the java.lang.reflect.* API to match the pointcuts. It then constructs the instrumented version of the class and hotswap it thanks to the Java 5 API. The JVM eats this one and goes on.


The first prepare phase is needed as the hotswap API does forbids change in the schema (ie cannot add or remove methods or fields).


A nice extra side effect is that at any point in time any class can be exposed to the AOP layer thru a simple API. This can be handy for some cases where there are some circular references in the dependancies (f.e. the instrumentation needs the java.lang.reflect.Method class to match against the pointcuts so if I want to add aspects to the Method class, I cannot do it until I have a representation of this class ie there 's no way to have it working by simply invoking the callback from the prepared Method class itself).


This might seems like gory details, especially when compared to what we do in the JRockit JVM support for AOP, but I am sure there are some concepts worth digging there - as memory usage and weaving overhead as been reported more than once as a major problem (see f.e. use case with AspectJ reported by Ron Bodkin where the system takes 4 minutes to start instead of less than a minute without any weaver here)


This makes it also very easy to add aspects even to java.* classes (though it's not generally a good idea unless you know what you are doing).


This sample is an actual code snip of the actual AOP transformation (part of phase 2). As you can see it relies on the java.lang.reflect API.



public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (!filter(access, name)) {//fast filter for f.e. clinit method as we look for method execution join points
// see if we get a match for this join point
// only java.lang.reflect.* API is used here
Member method = ReflectQuery.getMethod(m_klass, name, desc);
Class thisClass = m_klass;
Class targetClass = null;
Member withinCode = null;
//do matching
if (match(method, thisClass, targetClass, withinCode)) {
//change the bytecode but don't change the schema for hotswap purpose
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}


Finally I must say this idea is not 100% new as I wrote a paper on it for AOSD 2004 (read my paper here). At that time though I was not doing the pointcut matching based on the java.lang.reflect API - as the purpose was to enable dynamic AOP in AspectWerkz 1.0) ie I was not solving the problem of the memory and CPU overhead of the actual weaver.


What do you think? Are those ideas worth digging more?

Tuesday, August 2, 2005

JVM support for AOP in BEA JRockit

(imported from http://blogs.codehaus.org/people/avasseur, read comments there)

We have plublished a follow-up to our JavaOne 2005 session that describes with some more details the JVM support for AOP that is beeing designed within the BEA JRockit JVM.


This first article introduces the problems that usually happen with current weavers (in the Java land) and briefly described the proposed solution.

The next part to appear in the following weeks will give more details and code samples.


Read the article





JRockit JVM Support For AOP, Part 1 by Jonas Bonér and Alexandre Vasseur, Joakim Dahlstedt -- AOP is all the rage, but how do you implement it? In this article, Jonas, Alexandre, and Joakim show that the current approaches to implementing AOP suffer from many problems, making scalability an issue. Moreover, they indicate that the traditional approach to aspect weaving duplicates efforts that the JVM already performs.


We are currently working on making the prototype implementation available for further evaluation.


If you could not attend JavaOne 2005, the slides are available from the JavaOne web site, and they are also available here


The full webcast of the JavaOne session is also available on dev2dev.

Monday, August 1, 2005

AspectJ 5 load time weaving with Java 1.3 using ... AspectWerkz

(imported from http://blogs.codehaus.org/people/avasseur, read comments there)


**** Introduction


As development of AspectJ 5 and the merger with AspectWerkz making good progress, several users have started to wonder how the load time weaving under Java 1.3 / 1.4 VM be enabled.


Despite the name, AspectJ 5 is not at all tied to Java 5. I have already explained in this post how to write plain Java aspects using JavaDoc annotation and Backport175.


In this post I 'll explain how to enable load time weaving for Java 1.3/1.4 for AspectJ... by using AspectWerkz ! ie that AspectJ will sit on top of the very low level layer of AspectWerkz.



**** Some background


In AspectWerkz we enable load time weaving to happen thru a wide range of options that user can choose:


  • Java 5 agent

  • JRockit specific integration with JRockit agent

  • bootclasspath family

  • hotswap family


I have integrated the most important ones in the AspectJ 5 code base already - ie the first two - but some of them won't be integrated - hence this post.


When running Java 5 or JRockit (java 1.3 / 1.4 / 1.5) you don't need to read further and can stick to what's provided out of the box in AspectJ 5. Else... continue reading.


The AspectWerkz low level layer named aspectwerkz-core is actually a generic load time weaving layer. It comes with one interface that one has to implement - very similar to the JSR-163 instrumentation agent: the org.codehaus.aspectwerkz.hook.ClassPreProcessor.


This core layer comes with a set of tools that allows to turn this one on into the environment. The most easiest to use is what we called the "prepared bootclasspath" where you basically run a little script that will patch the java.lang.ClassLoader to hook in the agent, and will give you back a jar. This jar file can then be used first in the classpath when you start your JVM so that the agent gets called.


You can read more about all the options in the AspectWerkz doc here.



**** Running AspectJ by using AspectWerkz for load time weaving


I describe here the implementation of the agent, but you can use directly the one I ship with this post if you are not interested in the details. You can assume this one is LGPL, as AspectWerkz is.


The implementation of the ClassPreProcessor to use AspectJ on top of AspectWerkz core is straightforward and looks like that:

package org.aspectj.ext.ltw13;

public class ClassPreProcessorAdapter implements
// implements the AspectWerkz core interface
org.codehaus.aspectwerkz.hook.ClassPreProcessor {

/**
* Concrete preprocessor we delegate to
* This one sits in org.aspectj.weaver.loadtime.*
*/
private static ClassPreProcessor s_preProcessor;

static {
try {
s_preProcessor = new Aj();
s_preProcessor.initialize();
} catch (Exception e) {
throw new ExceptionInInitializerError("could not initialize preprocessor due to: " + e.toString());
}
}

public void initialize() {
;
}

public byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader) {
// skip bootCL
if (classLoader == null) {
return bytes;
}

// skip AJ weaver well know stuff to avoid circularity
if (className != null) {
String slashed = className.replace('.', '/');
if (slashed.startsWith("org/aspectj/weaver/")
|| slashed.startsWith("org/aspectj/bridge/")
|| slashed.startsWith("org/aspectj/util/")
|| slashed.startsWith("org/aspectj/apache/bcel")
|| slashed.startsWith("org/aspectj/lang/")
) {
//System.out.println("SKIP " + className);
return bytes;
}
}
// do the weaving using AspectJ weaver
return s_preProcessor.preProcess(className, bytes, classLoader);
}
}


By using the AspectWerkz Plug utility we generate our load time weaving enabled java.lang.ClassLoader in awaj-boot.jar (see AspectWerkz doc for other options, that might be more license friendly if you care). This can be done thru Ant:










The last step consists in starting the JVM with this one, passing in a specific option to say what is the ClassPreProcessor implementation we want to use (the default one beeing AspectWerkz).
Starting a sample with Ant will thus looks like this - the important details are int he jvmarg option




.....






**** Sample project


I am attaching the complete source code, along with a sample Aspect in a zip.


This sample further use Backport175 so that the project is 100% Java 1.3 - without any of the AspectJ specific extra keyword to defines aspects that would disturb your regular javac compiler.


Off course, this load time weaving can be used for any kind of aspects, so adapt it for using AJC if you have some .aj files to compile.


Note: this zip does not include AspectWerkz jars, Backport175 jars, and AspectJ jars needed. You will have to get those and fix the Ant build.xml for your environment. See the build.xml file in the zip. (the sample is also refering to AspectWerkz 2.1RC1 - so change it to AspectWerkz 2.0 / sorry about those minor details).


It may also happen that you will need a SAX XML parser has Java 1.3 does not ships one. You may read more about that in the AspectWerkz FAQ for example
here (read "Does AspectWerkz support Java 1.3?"). This is not contained in the sample project neither.


Get AspectJ load time weaving on Java 1.3 enabled by AspectWerkz !


You will need AspectWerkz 2.0 for it. Get it there.


To run the sample, you will also need AspectJ and Backport and the Backport175 AspectJ extension. See build.xml on how to get those.