-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2. Update Javassist
- Loading branch information
Showing
17 changed files
with
482 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ Java™ Platform Standard Ed. 8 | |
jdkJre | ||
Lambda | ||
javap | ||
instrumentation | ||
asm | ||
javassistTutorial | ||
javassistLog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Java Instrumentation | ||
|
||
## Introduction | ||
[Java Instrumentation API](https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html) provides the ability to add byte-code to existing compiled Java classes. | ||
|
||
## Key Components | ||
- Agent – is a jar file containing agent and transformer class files. | ||
- Agent Class – A java class file, containing a method named `premain`. | ||
- Manifest – `manifest.mf` file containing the `Premain-Class` property. | ||
- Transformer – A Java class file implementing the interface `ClassFileTransformer`. | ||
|
||
## What Is a Java Agent | ||
In general, a java agent is just a specially crafted jar file. **It utilizes the [Instrumentation API](https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html) that the JVM provides to alter existing byte-code that is loaded in a JVM.** | ||
|
||
For an agent to work, we need to define two methods: | ||
- `premain` – will statically load the agent using -javaagent parameter at JVM startup | ||
- `agentmain` – will dynamically load the agent into the JVM using the [Java Attach API](https://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/package-summary.html) | ||
|
||
### Instrumentation Activity Sequence | ||
![Java-Instrumentation-Activity-Flow](https://s1.wailian.download/2020/02/08/Java-Instrumentation-Activity-Flow-min.jpg) | ||
|
||
## Loading a Java Agent | ||
We have two types of load: | ||
- static – makes use of the `premain` to load the agent using `-javaagent` option | ||
- dynamic – makes use of the `agentmain` to load the agent into the JVM using the [Java Attach API](https://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/package-summary.html) | ||
|
||
### Static Load | ||
Loading a Java agent at application startup is called static load. Static load modifies the byte-code at startup time before any code is executed. | ||
|
||
Keep in mind that the static load uses the premain method, which will run before any application code runs, to get it running we can execute: | ||
``` | ||
java -javaagent:agent.jar -jar application.jar | ||
``` | ||
|
||
`Launcher` -> args: `StartMyAtmApplication 2 7 8` | ||
|
||
### Dynamic Load | ||
**The procedure of loading a Java agent into an already running JVM is called dynamic load.** The agent is attached using the [Java Attach API](https://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/package-summary.html). | ||
|
||
A more complex scenario is when we already have our ATM application running in production and we want to add the total time of transactions dynamically without downtime for our application. | ||
``` | ||
VirtualMachine jvm = VirtualMachine.attach(jvmPid); | ||
jvm.loadAgent(agentFile.getAbsolutePath()); | ||
jvm.detach(); | ||
``` | ||
|
||
- Starting the Application: `java -jar application.jar StartMyAtmApplication` | ||
- Attaching Java Agent: `java -jar application.jar LoadAgent` | ||
* `Launcher` -> args: `LoadAgent` | ||
- Check Application Logs | ||
|
||
### APIs | ||
- `addTransformer` – adds a transformer to the instrumentation engine | ||
- `getAllLoadedClasses` – returns an array of all classes currently loaded by the JVM | ||
- `retransformClasses` – facilitates the instrumentation of already loaded classes by adding byte-code | ||
- `removeTransformer` – unregisters the supplied transformer | ||
- `redefineClasses` – redefine the supplied set of classes using the supplied class files, meaning that the class will be fully replaced, not modified as with `retransformClasses` | ||
|
||
## Creating a Java Agent | ||
1. Create the `premain` and `agentmain` Methods: `Premain-transformClass(String className, Instrumentation instrumentation)` | ||
2. Defining Our `ClassFileTransformer`: `AtmTransformer` | ||
3. Creating an Agent Manifest File | ||
- We can find the full list of manifest attributes in the [Instrumentation Package](https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html) official documentation. | ||
``` | ||
Agent-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent | ||
Can-Redefine-Classes: true | ||
Can-Retransform-Classes: true | ||
Premain-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent | ||
``` | ||
|
||
## References | ||
- [Guide to Java Instrumentation](https://www.baeldung.com/java-instrumentation) | ||
- [Java Instrumentation](https://javapapers.com/core-java/java-instrumentation/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
jdk8/src/main/java/t5750/asm/visitor/LogMethodClassVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package t5750.asm.visitor; | ||
|
||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
|
||
public class LogMethodClassVisitor extends ClassVisitor { | ||
private String className; | ||
|
||
public LogMethodClassVisitor(ClassVisitor cv, String pClassName) { | ||
super(Opcodes.ASM6, cv); | ||
className = pClassName; | ||
} | ||
|
||
@Override | ||
public MethodVisitor visitMethod(int access, String name, String desc, | ||
String signature, String[] exceptions) { | ||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, | ||
exceptions); | ||
return new PrintMessageMethodVisitor(access, mv, name, className); | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
jdk8/src/main/java/t5750/asm/visitor/PrintMessageMethodVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package t5750.asm.visitor; | ||
|
||
import static org.objectweb.asm.Opcodes.ASM6; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.objectweb.asm.AnnotationVisitor; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
|
||
import t5750.module.log.util.LogUtil; | ||
|
||
public class PrintMessageMethodVisitor extends MethodVisitor { | ||
private String methodName; | ||
private String className; | ||
private boolean isAnnotationPresent = false; | ||
private List parameterIndexes = new ArrayList<>(); | ||
|
||
public PrintMessageMethodVisitor(int access, MethodVisitor mv, | ||
String methodName, String className) { | ||
super(Opcodes.ASM6, mv); | ||
this.methodName = methodName; | ||
this.className = className; | ||
} | ||
|
||
@Override | ||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | ||
if (LogUtil.ASM_METHOD_ANNOTATION.equals(desc)) { | ||
isAnnotationPresent = true; | ||
return new AnnotationVisitor(Opcodes.ASM6, | ||
super.visitAnnotation(desc, visible)) { | ||
public AnnotationVisitor visitArray(String name, Object value) { | ||
if ("fields".equals(name)) { | ||
return new AnnotationVisitor(ASM6, | ||
super.visitArray(name)) { | ||
public void visit(String name, Object value) { | ||
parameterIndexes.add((String) value); | ||
super.visit(name, value); | ||
} | ||
}; | ||
} else { | ||
return super.visitArray(name); | ||
} | ||
} | ||
}; | ||
} | ||
return super.visitAnnotation(desc, visible); | ||
} | ||
|
||
@Override | ||
public void visitCode() { | ||
if (isAnnotationPresent) { | ||
// create string builder | ||
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", | ||
"Ljava/io/PrintStream;"); | ||
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); | ||
mv.visitInsn(Opcodes.DUP); | ||
// add everything to the string builder | ||
mv.visitLdcInsn("A call was made to method \""); | ||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", | ||
"", "(Ljava/lang/String;)V", false); | ||
mv.visitLdcInsn(methodName); | ||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", | ||
"append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", | ||
false); | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
jdk8/src/main/java/t5750/instrument/AsmLogClassTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package t5750.instrument; | ||
|
||
import java.lang.instrument.ClassFileTransformer; | ||
import java.lang.instrument.IllegalClassFormatException; | ||
import java.security.ProtectionDomain; | ||
|
||
import org.objectweb.asm.ClassReader; | ||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.ClassWriter; | ||
|
||
import t5750.asm.visitor.LogMethodClassVisitor; | ||
|
||
public class AsmLogClassTransformer implements ClassFileTransformer { | ||
public byte[] transform(ClassLoader loader, String className, | ||
Class classBeingRedefined, ProtectionDomain protectionDomain, | ||
byte[] classfileBuffer) throws IllegalClassFormatException { | ||
ClassReader cr = new ClassReader(classfileBuffer); | ||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); | ||
ClassVisitor cv = new LogMethodClassVisitor(cw, className); | ||
cr.accept(cv, 0); | ||
return cw.toByteArray(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.