Skip to content

Commit

Permalink
Enhance ThreadTransformCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
HaojunRen committed Nov 23, 2023
1 parent 0c120d2 commit a77633f
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ public class ThreadConstant {
public static final String THREAD_SCAN_PACKAGES = "thread.scan.packages";
public static final String THREAD_REQUEST_DECORATOR_ENABLED = "thread.request.decorator.enabled";
public static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable";
public static final String RUNNABLE_IMPL_METHOD_NAME = "run";
public static final String CALLABLE_CLASS_NAME = "java.util.concurrent.Callable";
public static final String CALLABLE_IMPL_METHOD_NAME = "call";
public static final String SUPPLIER_CLASS_NAME = "java.util.function.Supplier";
public static final String SUPPLIER_IMPL_METHOD_NAME = "get";

public static final String THREAD_SCAN_PACKAGES_DELIMITERS = ";";
public static final String CONSTRUCTOR_INTERCEPTOR = String.format("%s.before(this);\n", ThreadConstructorInterceptor.class.getName());
public static final String RUN_BEFORE_INTERCEPTOR = String.format("%s.before(this);\n", ThreadCallInterceptor.class.getName());
public static final String RUN_AFTER_INTERCEPTOR = String.format("%s.after(this);\n", ThreadCallInterceptor.class.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,16 @@
* @author zifeihan
* @version 1.0
*/

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;

import com.nepxion.discovery.agent.async.AsyncContextAccessor;
import com.nepxion.discovery.agent.callback.TransformCallback;
import com.nepxion.discovery.agent.callback.TransformTemplate;
import com.nepxion.discovery.agent.config.Config;
import com.nepxion.discovery.agent.logger.AgentLogger;
import com.nepxion.discovery.agent.matcher.MatcherFactory;
import com.nepxion.discovery.agent.matcher.MatcherOperator;
import com.nepxion.discovery.agent.plugin.Plugin;
import com.nepxion.discovery.agent.util.ClassInfo;
import com.nepxion.discovery.agent.util.StringUtil;

public class ThreadPlugin extends Plugin {
Expand Down Expand Up @@ -74,132 +62,26 @@ public void install(TransformTemplate transformTemplate) {
LOG.info(String.format("%s install successfully", this.getClass().getSimpleName()));
}

public static class RunnableTransformCallback implements TransformCallback {
public static class RunnableTransformCallback extends ThreadTransformCallback {
@Override
public byte[] doInTransform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
try {
ClassInfo classInfo = new ClassInfo(className, classfileBuffer, classLoader);
CtClass ctClass = classInfo.getCtClass();

addField(ctClass, AsyncContextAccessor.class);

CtConstructor[] declaredConstructors = ctClass.getDeclaredConstructors();
for (CtConstructor ctConstructor : declaredConstructors) {
ctConstructor.insertAfter(ThreadConstant.CONSTRUCTOR_INTERCEPTOR);
}

CtMethod ctMethod = ctClass.getDeclaredMethod("run");
if (null != ctMethod) {
ctMethod.insertBefore(ThreadConstant.RUN_BEFORE_INTERCEPTOR);
ctMethod.insertAfter(ThreadConstant.RUN_AFTER_INTERCEPTOR);
}

return ctClass.toBytecode();
} catch (Exception e) {
LOG.warn(String.format("Transform %s error,message:", className), e);

return null;
}
public String getImplMethodName() {
return ThreadConstant.RUNNABLE_IMPL_METHOD_NAME;
}
}

public static void addField(CtClass ctClass, Class<?> clazz) {
try {
ClassPool aDefault = ClassPool.getDefault();
CtClass makeInterface = aDefault.makeInterface(clazz.getName());
ctClass.addInterface(makeInterface);

Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 2) {
throw new IllegalArgumentException("accessorType has to declare 2 methods. " + clazz.getName() + " has " + methods.length);
}

Method getter;
Method setter;

if (methods[0].getParameterTypes().length == 0) {
getter = methods[0];
setter = methods[1];
} else {
getter = methods[1];
setter = methods[0];
}

Class<?> fieldType = getter.getReturnType();
String fieldName = fieldType.getSimpleName().toLowerCase();
String field = String.format("private %s %s;", fieldType.getName(), fieldName);

CtField f1 = CtField.make(field, ctClass);
ctClass.addField(f1);

String getMethod = String.format("public %s %s(){return %s;}", fieldType.getName(), getter.getName(), fieldName);
String setMethod = String.format("public void %s(%s %s){this.%s = %s;}", setter.getName(), fieldType.getName(), fieldName, fieldName, fieldName);

CtMethod m1 = CtMethod.make(getMethod, ctClass);
CtMethod m2 = CtMethod.make(setMethod, ctClass);
ctClass.addMethod(m1);
ctClass.addMethod(m2);
} catch (Exception e) {
LOG.warn(String.format("Add field %s error, message:", clazz.getName()), e);
}
}
public static class CallableTransformCallback extends ThreadTransformCallback {

public static class CallableTransformCallback implements TransformCallback {
@Override
public byte[] doInTransform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
try {
ClassInfo classInfo = new ClassInfo(className, classfileBuffer, classLoader);
CtClass ctClass = classInfo.getCtClass();

addField(ctClass, AsyncContextAccessor.class);

CtConstructor[] declaredConstructors = ctClass.getDeclaredConstructors();
for (CtConstructor ctConstructor : declaredConstructors) {
ctConstructor.insertAfter(ThreadConstant.CONSTRUCTOR_INTERCEPTOR);
}

CtMethod ctMethod = ctClass.getDeclaredMethod("call");
if (null != ctMethod) {
ctMethod.insertBefore(ThreadConstant.RUN_BEFORE_INTERCEPTOR);
ctMethod.insertAfter(ThreadConstant.RUN_AFTER_INTERCEPTOR);
}

return ctClass.toBytecode();
} catch (Exception e) {
LOG.warn(String.format("Transform %s error, message:", className), e);

return null;
}
public String getImplMethodName() {
return ThreadConstant.CALLABLE_IMPL_METHOD_NAME;
}
}

public static class SupplierTransformCallback implements TransformCallback {
@Override
public byte[] doInTransform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
try {
ClassInfo classInfo = new ClassInfo(className, classfileBuffer, classLoader);
CtClass ctClass = classInfo.getCtClass();

addField(ctClass, AsyncContextAccessor.class);

CtConstructor[] declaredConstructors = ctClass.getDeclaredConstructors();
for (CtConstructor ctConstructor : declaredConstructors) {
ctConstructor.insertAfter(ThreadConstant.CONSTRUCTOR_INTERCEPTOR);
}

CtMethod ctMethod = ctClass.getDeclaredMethod("get");
if (null != ctMethod) {
ctMethod.insertBefore(ThreadConstant.RUN_BEFORE_INTERCEPTOR);
ctMethod.insertAfter(ThreadConstant.RUN_AFTER_INTERCEPTOR);
}
public static class SupplierTransformCallback extends ThreadTransformCallback {

return ctClass.toBytecode();
} catch (Exception e) {
LOG.warn(String.format("Transform %s error, message:", className), e);

return null;
}
@Override
public String getImplMethodName() {
return ThreadConstant.SUPPLIER_IMPL_METHOD_NAME;
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.nepxion.discovery.agent.plugin.thread;

/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author zifeihan
* @version 1.0
*/

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

import java.lang.reflect.Method;
import java.security.ProtectionDomain;

import com.nepxion.discovery.agent.async.AsyncContextAccessor;
import com.nepxion.discovery.agent.callback.TransformCallback;
import com.nepxion.discovery.agent.logger.AgentLogger;
import com.nepxion.discovery.agent.util.ClassInfo;

public abstract class ThreadTransformCallback implements TransformCallback {
private static final AgentLogger LOG = AgentLogger.getLogger(ThreadTransformCallback.class.getName());

@Override
public byte[] doInTransform(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
try {
ClassInfo classInfo = new ClassInfo(className, classfileBuffer, classLoader);
CtClass ctClass = classInfo.getCtClass();

addField(ctClass, AsyncContextAccessor.class);

CtConstructor[] declaredConstructors = ctClass.getDeclaredConstructors();
for (CtConstructor ctConstructor : declaredConstructors) {
ctConstructor.insertAfter(ThreadConstant.CONSTRUCTOR_INTERCEPTOR);
}

String implMethodName = getImplMethodName();
CtMethod ctMethod = ctClass.getDeclaredMethod(implMethodName);
if (null != ctMethod) {
ctMethod.insertBefore(ThreadConstant.RUN_BEFORE_INTERCEPTOR);
ctMethod.insertAfter(ThreadConstant.RUN_AFTER_INTERCEPTOR);
}

return ctClass.toBytecode();
} catch (Exception e) {
LOG.warn(String.format("Transform %s error, message:", className), e);

return null;
}
}

public void addField(CtClass ctClass, Class<?> clazz) {
try {
ClassPool aDefault = ClassPool.getDefault();
CtClass makeInterface = aDefault.makeInterface(clazz.getName());
ctClass.addInterface(makeInterface);

Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 2) {
throw new IllegalArgumentException("accessorType has to declare 2 methods. " + clazz.getName() + " has " + methods.length);
}

Method getter;
Method setter;

if (methods[0].getParameterTypes().length == 0) {
getter = methods[0];
setter = methods[1];
} else {
getter = methods[1];
setter = methods[0];
}

Class<?> fieldType = getter.getReturnType();
String fieldName = fieldType.getSimpleName().toLowerCase();
String field = String.format("private %s %s;", fieldType.getName(), fieldName);

CtField f1 = CtField.make(field, ctClass);
ctClass.addField(f1);

String getMethod = String.format("public %s %s(){return %s;}", fieldType.getName(), getter.getName(), fieldName);
String setMethod = String.format("public void %s(%s %s){this.%s = %s;}", setter.getName(), fieldType.getName(), fieldName, fieldName, fieldName);

CtMethod m1 = CtMethod.make(getMethod, ctClass);
CtMethod m2 = CtMethod.make(setMethod, ctClass);
ctClass.addMethod(m1);
ctClass.addMethod(m2);
} catch (Exception e) {
LOG.warn(String.format("Add field %s error, message:", clazz.getName()), e);
}
}

public abstract String getImplMethodName();
}

0 comments on commit a77633f

Please # to comment.