From a77633f0e9891f39f1d139e7cae98a6e2347ec9c Mon Sep 17 00:00:00 2001 From: HaojunRen <1394997@qq.com> Date: Thu, 23 Nov 2023 19:00:15 +0800 Subject: [PATCH] Enhance ThreadTransformCallback --- .../agent/plugin/thread/ThreadConstant.java | 5 +- .../agent/plugin/thread/ThreadPlugin.java | 140 ++---------------- .../thread/ThreadTransformCallback.java | 99 +++++++++++++ 3 files changed, 114 insertions(+), 130 deletions(-) create mode 100644 discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadTransformCallback.java diff --git a/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadConstant.java b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadConstant.java index f767dc1..b8e6df7 100644 --- a/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadConstant.java +++ b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadConstant.java @@ -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()); -} +} \ No newline at end of file diff --git a/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadPlugin.java b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadPlugin.java index 150f94e..6d9de08 100644 --- a/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadPlugin.java +++ b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadPlugin.java @@ -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 { @@ -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; } } - -} +} \ No newline at end of file diff --git a/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadTransformCallback.java b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadTransformCallback.java new file mode 100644 index 0000000..a611960 --- /dev/null +++ b/discovery-agent-starter/src/main/java/com/nepxion/discovery/agent/plugin/thread/ThreadTransformCallback.java @@ -0,0 +1,99 @@ +package com.nepxion.discovery.agent.plugin.thread; + +/** + *
Title: Nepxion Discovery
+ *Description: Nepxion Discovery
+ *Copyright: Copyright (c) 2017-2050
+ *Company: Nepxion
+ * @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(); +} \ No newline at end of file