Sobre la base de este tutorial que intenta conseguir un agente de Java para el trabajo. https://www.baeldung.com/java-instrumentation#loading-a-java-agent
Yo conseguir [Agent] Transforming class TestApplication
tengo ningún error, pero no puedo ver ningún efecto de transformar la clase.
Con el tiempo me gustaría conseguir tanto la carga estática y carga dinámica de trabajo, pero por ahora se centran en la forma estática.
public class Static_Agent {
public static void premain(String agentArgs, Instrumentation inst) {
String[] tokens = agentArgs.split(";");
String className = tokens[0];
String methodName = tokens[1];
System.out.println(">> "+className);
System.out.println(">> "+methodName);
transformClass(className, methodName, inst);
}
public static void transformClass(String className, String methodName, Instrumentation instrumentation) {
Class<?> targetCls = null;
ClassLoader targetClassLoader = null;
// see if we can get the class using forName
try {
targetCls = Class.forName(className);
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
} catch (Exception ex) {
ex.printStackTrace();
}
// otherwise iterate all loaded classes and find what we want
for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
if(clazz.getName().equals(className)) {
targetCls = clazz;
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
}
}
throw new RuntimeException("Failed to find class [" + className + "]");
}
public static void transform(Class<?> clazz, String methodName, ClassLoader classLoader, Instrumentation instrumentation) {
Transformer dt = new Transformer(clazz.getName(), methodName, classLoader);
instrumentation.addTransformer(dt, true);
try {
instrumentation.retransformClasses(clazz);
} catch (Exception ex) {
throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
}
}
}
public class Transformer implements ClassFileTransformer {
/** The internal form class name of the class to transform */
private String targetClassName;
/** The class loader of the class we want to transform */
private ClassLoader targetClassLoader;
private String targetMethodName;
public Transformer(String targetClassName, String targetMethodName, ClassLoader targetClassLoader) {
this.targetClassName = targetClassName;
this.targetClassLoader = targetClassLoader;
this.targetMethodName = targetMethodName;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
m.addLocalVariable("startTime", CtClass.longType);
m.insertBefore("startTime = System.currentTimeMillis();");
StringBuilder endBlock = new StringBuilder();
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
byteCode = cc.toBytecode();
cc.detach();
} catch (Exception e) {
System.out.println("Exception"+e);
}
}
return byteCode;
}
}
public class TestApplication {
public static void main(String[] args) {
try {
TestApplication.run();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void run() throws Exception {
System.out.println("--- start ---");
while (true) {
test();
Thread.sleep(4_000);
}
}
static int count = 0;
public static void test() {
System.out.println(count++);
}
}
Pongo en marcha con:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -jar application.jar
En caso de que ayuda, el proyecto está aquí: https://github.com/clankill3r/java_agent
Editar:
En el Transformer.java cerca del final del archivo que uso e.printStackTrace();
ahora.
Obtuve el siguiente error:
[Agent] Transforming clase TestApplication javassist.NotFoundException: doeke.application.TestApplication en javassist.ClassPool.get (ClassPool.java:436) en doeke.transformer.Transformer.transform (Transformer.java:48) en java.instrument / java. lang.instrument.ClassFileTransformer.transform (ClassFileTransformer.java:246) en java.instrument / sun.instrument.TransformerManager.transform (TransformerManager.java:188) en java.instrument / sun.instrument.InstrumentationImpl.transform (InstrumentationImpl.java: 563) en java.instrument / sun.instrument.InstrumentationImpl.retransformClasses0 (Método nativo) en java.instrument / sun.instrument.InstrumentationImpl.retransformClasses (InstrumentationImpl.java:167) en doeke.static_agent.Static_Agent.transform (Static_Agent.java: 56) a doeke.static_agent.Static_Agent.transformClass (Static_Agent.java:34) en doeke.static_agent.Static_Agent.premain (Static_Agent.java:22) en java.base / jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Método Nativo) en java.base / jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62) a java.base / jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) en java.base / java.lang.reflect.Method.invoke (Method.java:566) en java.instrument / sun.instrument. InstrumentationImpl.loadClassAndStartAgent (InstrumentationImpl.java:513) en java.instrument / sun.instrument.InstrumentationImpl.loadClassAndCallPremain (InstrumentationImpl.java:525)reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) en java.base / java.lang.reflect.Method.invoke (Method.java:566) en java.instrument / sun.instrument.InstrumentationImpl.loadClassAndStartAgent (InstrumentationImpl.java: 513) en java.instrument / sun.instrument.InstrumentationImpl.loadClassAndCallPremain (InstrumentationImpl.java:525)reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43) en java.base / java.lang.reflect.Method.invoke (Method.java:566) en java.instrument / sun.instrument.InstrumentationImpl.loadClassAndStartAgent (InstrumentationImpl.java: 513) en java.instrument / sun.instrument.InstrumentationImpl.loadClassAndCallPremain (InstrumentationImpl.java:525)
--- comienzo ---
0
1
Gracias por plantear esta cuestión que me permita tener la oportunidad de echar un vistazo de Java instrumentación.
Después de pasar algún tiempo para la comprobación cruzada de sus códigos de muestra y el tutorial proporcionado. El problema no es de los códigos de programación, pero la forma en cómo poner en marcha su programa.
Si agrega algunos registradores con el método transform () en Transformer.java, se encuentra que la ruta de código se rompe después de ejecutar:
ClassPool cp = ClassPool.getDefault();
Y, después de reemplazar la excepción captura de código en el mismo método a partir de:
} catch (Exception e) {
a:
} catch (NotFoundException | CannotCompileException | IOException e) {
Sería dar a sus más pistas de la siguiente manera:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: javassist/NotFoundException
at doeke.static_agent.Static_Agent.transform(Static_Agent.java:60)
at doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:40)
at doeke.static_agent.Static_Agent.premain(Static_Agent.java:28)
... 6 more
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 9 more
FATAL ERROR in native method: processing of -javaagent failed
Hasta este punto, la causa raíz es más evidente. Es porque mientras que el lanzamiento del programa, esas clases pertinentes javassist (por ejemplo ClassPool, CtClass, CtMethod, etc.) no pueden referirse a sus bibliotecas correspondientes durante el tiempo de ejecución.
Por lo tanto, la solución es :
Suponiendo que ha exportado el static_agent.jar en la misma carpeta "acumulación" a partir de application.jar
toda otra estructura de carpetas siguen siendo los mismos, como se muestra en su github proporcionado
vamos a "CD" a la acumulación carpeta en la consola de comandos
la revisión del programa original lanzamiento de la escritura de la siguiente
El sistema operativo Windows:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar;application.jar doeke.application.TestApplication
Unix / Linux OS:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar:application.jar doeke.application.TestApplication
Se podría finalmente obtener su resultado esperado:
[Agent] In premain method.
>> doeke.application.TestApplication
>> test
[Agent] Transforming class
--- start ---
0
[Application] Withdrawal operation completed in:0 seconds!
1
[Application] Withdrawal operation completed in:0 seconds!
EDITAR
Además, quiero pegar algunos códigos con respecto a la forma de insertar códigos en medio de un método a través javassist.
En caso de que el método de ensayo () en TestApplication.java se cambia como:
line 30 public static void test() {
line 31 System.out.println(count++);
line 32
line 33 System.out.println("Last line of test() method");
line 34 }
Supongamos que queremos añadir una línea entre el recuento y la ========= , digamos "Esta es la línea de separación", que el resultado se vería así:
1
-- This is line separator --
Last line of test() method
Luego, en el método de transformación (...) de Transformer.java, podría agregar una línea de código como de abajo:
m.insertAt(32,"System.out.println(\"-- This is line separator --\");");
lo que hace que se convierte en:
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
// Step 1 Preparation
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
// Step 2 Declare variables
m.addLocalVariable("startTime", CtClass.longType);
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
// Step 3 Insertion of extra logics/implementation
m.insertBefore("startTime = System.currentTimeMillis();");
m.insertAt(32,"System.out.println(\"-- This is line separator --\");");
StringBuilder endBlock = new StringBuilder();
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
// Step 4 Detach from ClassPool and clean up stuff
byteCode = cc.toBytecode();
cc.detach();
} catch (NotFoundException | CannotCompileException | IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
Por último, sería obtener un resultado como el de abajo de imprimir el código en el medio de un método:
[Agent] In premain method.
className=doeke.application.TestApplication
methodName=test
>> doeke.application.TestApplication
>> test
[Agent] Transforming class TestApplication
--- start ---
0
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
1
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!
2
-- This is line separator --
=========
[Application] Withdrawal operation completed in:0 seconds!