I am using a tomcat7 instance to run a Java application. My application needs the Java instrumentation exposed. This is done with a javaagent and I pass the agent at startup to the JVM in the setenv.bat script.
set JAVA_OPTS=%JAVA_OPTS% -javaagent:"C:\path\to\agent.jar"
In the manifest file I have the required section:
Premain-Class: package.name.agent.ExposeInstrumentation
In the premain method of the agent class assign the instrumentation provided by the JVM to a static variable accessible through a static method
public final class ExposeInstrumentation {
private static Instrumentation s_instrumentation;
public static void premain(String arguments, Instrumentation instrumentation) {
s_instrumentation = instrumentation;
}
public static Instrumentation getInstrumentation() {
return s_instrumentation;
}
}
But in my code when I do this:
Instrumentation instrumentation = ExposeInstrumentation.getInstrumentation();
getInstrumentation() returns null;
What is the problem?
UPDATE
I did some further debugging and premain gets executed and s_instrumentation receives the instrumentation, but when I call getInstrumentation later on in my code s_instumentation is set to null. This is strange I tought the value remains valid thought the life of the program.
I assume that you are loading the class ExposeInstrumentation
twice. Once by the application class loader which is child-first (reverse-order) and once via the Java agent where the class is automatically loaded by the system class loader. As a result, the ExposeInstrumentation
class is loaded twice where you access the one from your application where the field is not set.
You can solve this by explicitly accessing the class loaded by the system class loader:
class ExposeInstrumentation {
// public to assure accessability
public static Instrumentation s_instrumentation;
public static void premain(String arguments, Instrumentation inst) {
s_instrumentation = inst;
}
public static Instrumentation getInstrumentation() {
try {
return (Instrumentation) ClassLoader.getSystemClassLoader()
.loadClass(ExposeInstrumentation.class.getName())
.getDeclaredField("s_instrumentation")
.get(null);
} catch(Exception e) {
return null;
}
}
}
You can also check out the Byte Buddy Agent project that offers this functionality and more (runtime installation) of an agent. With Byte Buddy, you can simply call ByteBuddyAgent.getInstrumentation()
.