Back.gif
A Java Agent, once registered with the class loader, has a single method:
public class SkeletonClassFileTransformer implements ClassFileTransformer {
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        return null;
    }
}

A Java Agent is given the option of modifying the array of bytes representing the class before the class loader finishes the class loading process. Here is a specification of class loading. In a nutshell, the bytes are given to the Java Agent after the class loader has retrieved them but before Linking. Your Java Agent can create a new byte array in a valid class file format and return it or, if it is not performing a transformation, return null.

Here is an example that simply prints a message like the following:
Class: StringCoding in: java/lang
Class: StringCoding$CharsetSE in: java/lang
Class: StringCoding$StringEncoder in: java/lang
Class: Main in: schuchert/agent
Class: Shutdown in: java/lang
Class: Shutdown$Lock in: java/lang

package schuchert.agent;
 
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
 
public class ClassAndPackageNamePrintingClassFileTransformer implements ClassFileTransformer {
 
    public byte[] transform(ClassLoader loader, String fullyQualifiedClassName, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classofileBuffer) throws IllegalClassFormatException {
        String className = fullyQualifiedClassName.replaceAll(".*/", "");
        String pacakge = fullyQualifiedClassName.replaceAll("/[a-zA-Z$0-9_]*$", "");
        System.out.printf("Class: %s in: %s\n", className, pacakge);
        return null;
    }
}
How do you get all of this to work? Here's what you'll need to do:
  1. Create an Implementation of ClassFileTransformer
  2. Create a class with a premain method (could be in the first class, but but that would violate The Single Responsibility Principle).
  3. Create jar file
  4. Star the VM with a command line parameter.

And, of course, are the details for each of those steps.

Create an Implementation of ClassFileTransformer

The class above is a complete example of a class that can "transform" a just-loaded class. By itself, it really does not do much. However, if you'd like to perform some custom transformation, you could create a new byte array, add in some Java Bytecodes and then return that class instead.

Why would you do this? Here are a few examples:
  1. You're adding logging code to a class
  2. Custom implementation of AOP
  3. Instrument a class to better thread-based testing (my reason for looking into this in the first place)
  4. Etc.

Create a class with a premain method

The class file transformer is not directly added to the class loader. Instead, you create another class with a method called premain that instantiates the class and registers it. Here is a complete example:
package schuchert.agent;
 
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
 
public class RegisterMyClassFileTransformer {
    public static void premain(String agentArguments, Instrumentation instrumentation) {
        instrumentation.addTransformer(new ClassAndPackageNamePrintingClassFileTransformer());
    }
}

Why this indirection? I'm not exactly sure. However, I use it to write the registrar once and reuse it across projects. The act of registration is the same, what I want to register varies by project. The Registrar.jar file you'll download is essentially a simple "component" that allows for registration of a ClassFileTransformer you create in your project by specifying a system property.

Create Jar File

When you add a class file transformer to the class loader, you must specify the name of a jar file. You cannot simply name a class in the classpath. So if the ClassAndPackageNamePrintingClassFileTransformer is in the class path, then you need to add the class RegisterMyClassFileTransformer to a jar file and add a manifest file to specify it.

The jar file needs to have the following structure:
Top Of Jar File
    <sub directory>com
        <sub directory>javaagent
            <file>RegisterMyClassFileTransformer.class
    <sub directory>META-INF
        <file>MANIFEST.MF
The contents of the MANIFEST.MF file, at a minimum, would be:
Manifest-Version: 1.0 
Premain-Class: com.javaagent.RegisterMyClassFileTransformer

NOTE: If you happen to download the Eclipse project mentioned here, you can simply find the RecreateJar.jardesc file in the Eclipse project, right-click and select Create JAR.

Start the VM

Finally, we need to star the VM:
java -javaagent:MyJarFile.jar <A Regular Class With A Main>
Back.gif