Welcome!

Java Authors: Maureen O'Gara, Bruce Armstrong, Liz McMillan, Walter H. Pinson, III, Yakov Werde

Related Topics: Java, IT SOLUTIONS GUIDE

Java: Article

Kicking the Tires on Java 5.0

Building an aspect-oriented framework based on annotations

For those of you who have figured it out already or perhaps those of you who are wondering, what we are developing is a scheme by which we declare our aspect point-cuts via annotations. From a programming perspective we're dealing with an ordinary class (BizComponent) and we're using an annotation to tag it for bytecode engineering during the class-loading phase (more on this later). The nice thing about this approach is that the annotation (metadata) lives close to the source code eliminating the need to alter multiple source files. Compare this to "conventional" aspect-oriented programming where you have to declare this point-cut in a separate file and precompile or wire things up in an XML file like you would using Spring.

Getting Annotation Information at Runtime
From a programming perspective, getting information out of an annotation is pretty straightforward. Continuing with the example that we've developed to this point, imagine that we have a candidate class that we want to examine for annotations. We know that only our methods are annotated, so given a class, we first extract all the "Method" objects and iterate over them searching for our annotations, like so:


for( Method m : methods )
{
...
Annotation [] annotations =
m.getDeclaredAnnotations();
for( Annotation a : annotations )
{
System.out.println("annotation type: "
+a.annotationType());
if(AMAnnotation.class.getName().
equals(a.annotationType().getName())
{
// Byte-code engineering here...
}
}
}

Note the use of the new for loop iterator. Gone are the days where you have to iterate over unsafe, un-typed collections. Happy Days indeed!

Once we have the Annotation object, getting properties is just as simple as this snippet of code demonstrates:


if( getAnnotationProperty
("insertionPoint", a.toString()).equals("pre"))
{
...
}

This code block simply pulls a property value out of the annotation and checks to see if it literally equals the string "pre."

Great, What Happens Next? - Class Loading in 5.0
So now that we have our classes all annotated up, what do we do? Recall the intent of our Java 5.0 featured framework. We want to be able to annotate our classes (mark them) so that at runtime they are loaded in, examined, and altered (if they have the correct annotations) in such a way that a piece of code will run either before ("pre") or after ("post") our business method gets executed.

The class-loading mechanism in Java 5.0 has been extended and has provided us with a really slick feature: agents! Very simply, an agent class is something that you pass along to the VM via a command-line argument that lets you do things PRIOR to your main being called. Among other things, you can install a class-file transformer that's invoked after the class has physically loaded into the VM, but before the class is handed back to the application. What this gives you is the ability to manipulate the class physically as it's being loaded by the JVM. Before Java 5.0, this involved somewhat tricky, awkward custom class-loader wizardry. No more!

Here's the full implementation for our Agent class:


import java.lang.instrument.*;
public class Agent
{
public static void premain (String fqnBCEngineeringClass,
Instrumentation instrumentation)
{
try
{
// Install the new bytecode engineering loader
ClassFileTransformer xformer = (ClassFileTransformer)
Thread.currentThread().getContextClassLoader().getClass().forName
(fqnBCEngineeringClass).newInstance();
instrumentation.addTransformer(xformer);
}
catch(Exception ex)
{ System.err.println("Could not instantiate Bytecode Engineering Class:
"+ex.getMessage()); }
}

} // class Agent

There's nothing special about an Agent class, other than it must contain the one static "premain(String, Instrumentation)" method. Besides that, it doesn't have to implement any particular interface or extend any particular class. So in our case our agent code will get called in advance of our static main being executed. This requires some special arguments to the JVM that we'll cover in a moment. When our premain gets executed, we'll have two parameters passed in. One is the fully qualified name of our bytecode engineering class and the other is the hook that we'll need to install it.

Notice that the premain method dynamically (via reflection) tries to instantiate the transformer class and then turns around and installs it as a class-file transformer. The class instantiated here is the class that implements the interface "java.lang.instrumentation.ClassFileTransformer." In our case, this is the class that will do all the heavy lifting of the class file transformation. It's also the routine that slogs through classes looking for the marker annotations that slate a class for bytecode engineering.

Getting the Agent class to execute requires a bit of legwork. First at compile time, we must create a Manifest file that specifies a premain class. The easiest way I've found to accomplish this is in the Ant script that assembles the jar. Here's a snippet of the Ant task that simply stipulates the correct name-value that gets injected into the Manifest file and will cause the JVM to call my agent class prior to calling main().


<jar jarfile="${dist}/j105.jar" update="true" >
<fileset dir="${classes}"/>
<fileset dir="${configure}"/>
<manifest>
<attribute name="Premain-Class" value="Agent"/>
</manifest>
</jar>

Comments (3) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
slopzster 07/13/05 11:30:22 PM EDT

fantastic article, very thought provoking -- kudos. Can you comment on potential performance related issues in instrumented components that have high availability requirements?

Boris 06/29/05 11:45:06 AM EDT

I'm sorry, but I cann't find link to sources
of this cool article on the web pages.

Peter Braswell 06/24/05 01:03:39 PM EDT

This article will survey some of Java 5.0's new features and put them into practice through example. We'll build up a lightweight aspect-oriented system based on annotations to showcase what's new in 5.0. Some of these features you may be familiar with, some you may not. I've attempted to mix the obvious with some of the obscure. We'll examine some of the new hooks that the JVM has exposed for class loading, which makes the once dreadful work of bytecode manipulation during class loading much easier. In the "obvious" column, we'll look at generics and how they enable us to write more robust and sane programs, especially when dealing with collections. Perhaps the most notable aspect of Java 5.0 that we'll examine is the annotation framework. Annotations allow developers to inject metadata into their applications. We'll use this feature to demark classes we want manipulated at load time. To put this all in context, we'll create a lightweight framework that will manipulate classes as they are being loaded to enable logging, security, BAM (business activity monitoring), or any number of other scenarios that have yet to be dreamed up