GluonJ Test Drive : Simple AOP

I admitedly don’t have a lot of experience with the various AOP frameworks that have gained popularity over the past few years.

I happened to be looking at Javassist a couple weeks back and noticed that they were in the process of developing a lightweight AOP framework on top of it called GluonJ. It’s still in fairly active development with a 1.5b being released last November.

I naively set out to solve what I thought was a pretty simple problem, track statistics about object serializations. Essentially I wanted to build something similar to JDBCSpy that tracked and exposed basic serialization information over JMX. Initially I’m thinking basic things like # of serialized objects and # of bytes serialized. I spend my days working on a client/server application and this type of information would be useful, if done in a transparent and non-intrusive way.

Caveat – There might be (and probably are) better ways to accomplish this, I’m not dead set on using the AOP hammer to solve this problem but it does present a nice excuse to try it out.

Attempt #1

My first attempt was to refine the definition of java.lang.Object and insert advice around writeObject().

This unfortunately didn’t make it very far as it doesn’t look like GluonJ allows you to mess with Object. You can refine any other class (even the final ones) but there’s no love for java.lang.Object.

Attempt #2

Second attempt was a bit more selective. I choose an object to monitor serializations for and went about trying to apply advice to its writeObject().

Unfortunately, I didn’t have much success in being able to apply any sort of advice (@Before, @After, @Around) to a private method. In hindsight, it would have been less then ideal anyways as most Serializable objects don’t implement their own writeObject() anyways.

Attempt #3

After a couple dead ends I decided to approach to problem from a slightly different angle. Rather than focusing on modifying the behaviour of the object being serialized, why not change the behaviour of the object actually responsible for performing the serialization. In this trivial example, that object was the ObjectOutputStream.

It worked and didn’t require very much code at all.

The Glue Code
This doesn’t really do much other than instruct the runtime that whenever ObjectOutputStream.writeObject() is invoked, we want to do a System.out.println() afterwards. Pretty simple right?

@Glue
public class SerializationGlue
{
    /**
     * @param obj Object
     * @return Number of bytes in the serialized form of 'obj'
     */
    public static long calculateSize(Object obj)
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try
        {
            ObjectOutputStream out = new ObjectOutputStream(baos);
            out.writeObject(obj);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return -1;
        }

    return baos.size();
}

@After("{ System.out.println(`obj size=` + org.jordens.jdbcspy.testutil.gluonj.SerializationGlue.calculateSize(object)); }")
Pointcut pc1 = Pcd.define("object", "$1")
                  .define("target", "$0")
                  .call("java.io.ObjectOutputStream#writeObject(Object)");

}

The Test Code

public class Runner
{
    public static void main(String[] args)
    {
        List<Long> values = new ArrayList<Long>()
        {
            public String toString()
            {
                return "List<Long>[" + size() + "]";
            }
        };

    for (long i=0; i&lt;100000; i++)
    {
        values.add(i);
    }

    try
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(values);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

}

How do I run this?
java -javaagent:gluonj-1.5b.jar=org.jordens.jdbcspy.testutil.gluonj.SerializationGlue -classpath classes/ org.jordens.jdbcspy.testutil.gluonj.Runner

would print:
obj size=1400181

Nothing too crazy there. One niceity in all of this is the relative ease in which you can enable/disable the runtime code weaving. Just leave off the -javaagent:XXX and you’re effectively back to normal.

Conclusions

GluonJ looks pretty cool but obviously it’s still fairly new and lacks many of the capabilities available in a more mature offering like AspectJ. That being said, the documentation is pretty good and the learning curve was basically non-existent. If you’re looking for something to play around with, it’s probably a bit easier to get started with.

The possibilities are certainly encouraging and I’ll definitely be keeping an eye on the progress of GluonJ as well as taking a more hands-on look at AspectJ.