diff --git a/core-java/pom.xml b/core-java/pom.xml
index 42e3219ac315..0b69685e14a7 100644
--- a/core-java/pom.xml
+++ b/core-java/pom.xml
@@ -173,6 +173,19 @@
c3p0
${c3p0.version}
+
+
+ org.javassist
+ javassist
+ ${javaassist.version}
+
+
+ com.sun
+ tools
+ 1.8.0
+ system
+ ${java.home}/../lib/tools.jar
+
@@ -400,6 +413,111 @@
+
+
+
+ buildAgentLoader
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ package
+
+ jar
+
+
+ agentLoader
+ target/classes
+
+
+ true
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+ com/baeldung/instrumentation/application/AgentLoader.class
+ com/baeldung/instrumentation/application/Launcher.class
+
+
+
+
+
+
+
+
+
+ buildApplication
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ package
+
+ jar
+
+
+ application
+ target/classes
+
+
+ true
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+ com/baeldung/instrumentation/application/MyAtm.class
+ com/baeldung/instrumentation/application/MyAtmApplication.class
+ com/baeldung/instrumentation/application/Launcher.class
+
+
+
+
+
+
+
+
+
+ buildAgent
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ package
+
+ jar
+
+
+ agent
+ target/classes
+
+
+ true
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+ com/baeldung/instrumentation/agent/AtmTransformer.class
+ com/baeldung/instrumentation/agent/MyInstrumentationAgent.class
+
+
+
+
+
+
+
+
@@ -453,6 +571,8 @@
1.18
0.1.5
+
+ 3.21.0-GA
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/agent/AtmTransformer.java b/core-java/src/main/java/com/baeldung/instrumentation/agent/AtmTransformer.java
new file mode 100644
index 000000000000..3c83912f5434
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/agent/AtmTransformer.java
@@ -0,0 +1,70 @@
+package com.baeldung.instrumentation.agent;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+public class AtmTransformer implements ClassFileTransformer {
+
+ private static Logger LOGGER = LoggerFactory.getLogger(AtmTransformer.class);
+
+ private static final String WITHDRAW_MONEY_METHOD = "withdrawMoney";
+
+ /** 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;
+
+ public AtmTransformer(String targetClassName, ClassLoader targetClassLoader) {
+ this.targetClassName = targetClassName;
+ this.targetClassLoader = targetClassLoader;
+ }
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ byte[] byteCode = classfileBuffer;
+
+ String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/"); //replace . with /
+ if (!className.equals(finalTargetClassName)) {
+ return byteCode;
+ }
+
+ if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
+ LOGGER.info("[Agent] Transforming class MyAtm");
+ try {
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = cp.get(targetClassName);
+ CtMethod m = cc.getDeclaredMethod(WITHDRAW_MONEY_METHOD);
+ 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("LOGGER.info(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
+
+ m.insertAfter(endBlock.toString());
+
+ byteCode = cc.toBytecode();
+ cc.detach();
+ } catch (NotFoundException | CannotCompileException | IOException e) {
+ LOGGER.error("Exception", e);
+ }
+ }
+ return byteCode;
+ }
+}
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/agent/MyInstrumentationAgent.java b/core-java/src/main/java/com/baeldung/instrumentation/agent/MyInstrumentationAgent.java
new file mode 100644
index 000000000000..685520276edc
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/agent/MyInstrumentationAgent.java
@@ -0,0 +1,59 @@
+package com.baeldung.instrumentation.agent;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.instrument.Instrumentation;
+
+public class MyInstrumentationAgent {
+ private static Logger LOGGER = LoggerFactory.getLogger(MyInstrumentationAgent.class);
+
+ public static void premain(String agentArgs, Instrumentation inst) {
+ LOGGER.info("[Agent] In premain method");
+
+ String className = "com.baeldung.instrumentation.application.MyAtm";
+ transformClass(className,inst);
+ }
+
+ public static void agentmain(String agentArgs, Instrumentation inst) {
+ LOGGER.info("[Agent] In agentmain method");
+
+ String className = "com.baeldung.instrumentation.application.MyAtm";
+ transformClass(className,inst);
+ }
+
+ private static void transformClass(String className, 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, targetClassLoader, instrumentation);
+ return;
+ } catch (Exception ex) {
+ LOGGER.error("Class [{}] not found with Class.forName");
+ }
+ // 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, targetClassLoader, instrumentation);
+ return;
+ }
+ }
+ throw new RuntimeException("Failed to find class [" + className + "]");
+ }
+
+ private static void transform(Class> clazz, ClassLoader classLoader, Instrumentation instrumentation) {
+ AtmTransformer dt = new AtmTransformer(clazz.getName(), classLoader);
+ instrumentation.addTransformer(dt, true);
+ try {
+ instrumentation.retransformClasses(clazz);
+ } catch (Exception ex) {
+ throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
+ }
+ }
+
+}
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/application/AgentLoader.java b/core-java/src/main/java/com/baeldung/instrumentation/application/AgentLoader.java
new file mode 100644
index 000000000000..2c1cd759a5df
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/application/AgentLoader.java
@@ -0,0 +1,46 @@
+package com.baeldung.instrumentation.application;
+
+import com.sun.tools.attach.VirtualMachine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Optional;
+
+/**
+ * Created by adi on 6/10/18.
+ */
+public class AgentLoader {
+ private static Logger LOGGER = LoggerFactory.getLogger(AgentLoader.class);
+
+ public static void run(String[] args) {
+ String agentFilePath = "/home/adi/Desktop/agent-1.0.0-jar-with-dependencies.jar";
+ String applicationName = "MyAtmApplication";
+
+ //iterate all jvms and get the first one that matches our application name
+ Optional jvmProcessOpt = Optional.ofNullable(VirtualMachine.list()
+ .stream()
+ .filter(jvm -> {
+ LOGGER.info("jvm:{}", jvm.displayName());
+ return jvm.displayName().contains(applicationName);
+ })
+ .findFirst().get().id());
+
+ if(!jvmProcessOpt.isPresent()) {
+ LOGGER.error("Target Application not found");
+ return;
+ }
+ File agentFile = new File(agentFilePath);
+ try {
+ String jvmPid = jvmProcessOpt.get();
+ LOGGER.info("Attaching to target JVM with PID: " + jvmPid);
+ VirtualMachine jvm = VirtualMachine.attach(jvmPid);
+ jvm.loadAgent(agentFile.getAbsolutePath());
+ jvm.detach();
+ LOGGER.info("Attached to target JVM and loaded Java agent successfully");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/application/Launcher.java b/core-java/src/main/java/com/baeldung/instrumentation/application/Launcher.java
new file mode 100644
index 000000000000..87e494baab17
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/application/Launcher.java
@@ -0,0 +1,14 @@
+package com.baeldung.instrumentation.application;
+
+/**
+ * Created by adi on 6/14/18.
+ */
+public class Launcher {
+ public static void main(String[] args) throws Exception {
+ if(args[0].equals("StartMyAtmApplication")) {
+ new MyAtmApplication().run(args);
+ } else if(args[0].equals("LoadAgent")) {
+ new AgentLoader().run(args);
+ }
+ }
+}
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtm.java b/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtm.java
new file mode 100644
index 000000000000..f826e8297572
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtm.java
@@ -0,0 +1,19 @@
+package com.baeldung.instrumentation.application;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by adi on 6/11/18.
+ */
+public class MyAtm {
+ private static Logger LOGGER = LoggerFactory.getLogger(MyAtm.class);
+
+ private static final int account = 10;
+
+ public static void withdrawMoney(int amount) throws InterruptedException {
+ Thread.sleep(2000l); //processing going on here
+ LOGGER.info("[Application] Successful Withdrawal of [{}] units!", amount);
+
+ }
+}
diff --git a/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtmApplication.java b/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtmApplication.java
new file mode 100644
index 000000000000..425511285e32
--- /dev/null
+++ b/core-java/src/main/java/com/baeldung/instrumentation/application/MyAtmApplication.java
@@ -0,0 +1,19 @@
+package com.baeldung.instrumentation.application;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MyAtmApplication {
+
+ private static Logger LOGGER = LoggerFactory.getLogger(MyAtmApplication.class);
+
+ public static void run(String[] args) throws Exception {
+ LOGGER.info("[Application] Starting ATM application");
+ MyAtm.withdrawMoney(Integer.parseInt(args[2]));
+
+ Thread.sleep(Long.valueOf(args[1]));
+
+ MyAtm.withdrawMoney(Integer.parseInt(args[3]));
+ }
+
+}
diff --git a/core-java/src/main/resources/META-INF/MANIFEST.MF b/core-java/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 000000000000..988de3193dcf
--- /dev/null
+++ b/core-java/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Agent-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent
+Can-Redefine-Classes: true
+Can-Retransform-Classes: true
+Premain-Class: com.baeldung.instrumentation.agent.MyInstrumentationAgent
+Main-Class: com.baeldung.instrumentation.application.Launcher
diff --git a/core-java/src/main/resources/log4j2.xml b/core-java/src/main/resources/log4j2.xml
new file mode 100644
index 000000000000..a824bef9b056
--- /dev/null
+++ b/core-java/src/main/resources/log4j2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file