I'm just experimenting with Java Instrumentation because it's very interesting and I'd like to know more about it. I'm using it in conjunction with the javassist library to make bytecode manipulation much easier, and the "tools" library which is included in JDK install.
Here is my main class:
public class MainClass {
public static boolean first = true;
static{
AgentClass.initialize();
}
public static void loadAgent(){
String path = System.getProperty("user.dir") + "\\AgentJar.jar";
String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
int p = nameOfRunningVM.indexOf('@');
String pid = nameOfRunningVM.substring(0, p);
try {
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(path, "");
vm.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static void main(String[] args){
System.out.println("First run-through, code should be modified once.");
new Hello().hello();
first = false;
try {
AgentClass.getInstrumentation().retransformClasses(Class.forName("test.Hello"));
} catch (Exception e){
e.printStackTrace();
}
System.out.println("Second run-through, code should be modified twice.");
new Hello().hello();
}
}
Here is the "Hello" class:
public class Hello {
public void hello(){
System.out.println("Hello World!");
}
}
Here is the FileTransformer class:
public class FileTransformer implements ClassFileTransformer{
private static boolean first = true;
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (!className.contains("Hello"))
return null;
else{
byte[] result;
CtClass cc = null;
try {
cc = ClassPool.getDefault().get("test.Hello");
CtMethod method = cc.getDeclaredMethod("hello");
if (MainClass.first){
System.out.println("In transformer: first");
method.insertAfter("System.out.println(\"Modified First Time!\");");
}else{
System.out.println("In transformer: second");
method.insertAfter("System.out.println(\"I modified it again.!\");");
}
cc.writeFile();
result = cc.toBytecode();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return result;
}
}
}
The agent class is in another jar, it's a basic implementation of it:
public class AgentClass {
protected static Instrumentation inst;
private static boolean added = false;
public static void agentmain(String args, Instrumentation inst){
AgentClass.inst = inst;
if (!added)
inst.addTransformer(new FileTransformer());
}
public static void premain(String args, Instrumentation inst){
AgentClass.inst = inst;
inst.addTransformer(new FileTransformer());
added = true;
}
public static void initialize(){
if (inst == null){
MainClass.loadAgent();
}
}
public static Instrumentation getInstrumentation(){
return inst;
}
}
When I run, I encounter no errors. However, the output is not how I would expect it to be.
Here is the output I get:
First run-through, code should be modified once.
In transformer: first
Hello World!
Modified First Time!
Second run-through, code should be modified twice.
Hello World!
Modified First Time!
You might notice that there is no line that reads "I modified it again!"
Any help is appreciated.