Add comments in sniffer module. More to add later.

This commit is contained in:
wusheng 2017-01-03 19:31:37 +08:00
parent a944427df8
commit 8563997b20
11 changed files with 220 additions and 86 deletions

View File

@ -6,9 +6,23 @@ import com.a.eye.skywalking.util.StringUtil;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.pool.TypePool.Resolution;
public abstract class AbstractClassEnhancePluginDefine{
/**
* Basic abstract class of all sky-walking auto-instrumentation plugins.
* <p>
* It provides the outline of enhancing the target class.
* If you want to know more about enhancing, you should go to see {@link com.a.eye.skywalking.plugin.interceptor.enhance.ClassEnhancePluginDefine}
*/
public abstract class AbstractClassEnhancePluginDefine {
private static ILog logger = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);
/**
* Main entrance of enhancing the class.
*
* @param transformClassName target class.
* @param builder byte-buddy's builder to manipulate target class's bytecode.
* @return be defined builder.
* @throws PluginException, when set builder failure.
*/
public DynamicType.Builder<?> define(String transformClassName, DynamicType.Builder<?> builder) throws PluginException {
String interceptorDefineClassName = this.getClass().getName();
@ -48,15 +62,22 @@ public abstract class AbstractClassEnhancePluginDefine{
protected abstract DynamicType.Builder<?> enhance(String enhanceOriginClassName, DynamicType.Builder<?> newClassBuilder) throws PluginException;
/**
* 返回要被增强的类应当返回类全名或前匹配(返回*号结尾)
* Define the classname of target class.
*
* @return
* @return class full name.
*/
protected abstract String enhanceClassName();
/**
* 返回一个类名的列表
* 如果列表中的类在JVM中存在,则enhance可以会尝试生效
* Witness classname list.
* Why need witness classname? Let's see like this:
* A library existed two released versions (like 1.0, 2.0), which include the same target classes,
* but because of version iterator, they may have the same name, but different methods, or different method arguments list.
* So,
* if I want to target the particular version (let's say 1.0 for example), version number is obvious not an option
* this is the moment you need "Witness classes".
* You can add any classes only in this particular release version ( something like class com.company.1.x.A, only in 1.0 ),
* and you can achieve the goal.
*
* @return
*/

View File

@ -10,8 +10,6 @@ public class PluginDefineCategory {
private final Map<String, AbstractClassEnhancePluginDefine> exactClassEnhancePluginDefineMapping =
new HashMap<String, AbstractClassEnhancePluginDefine>();
private final Map<String, AbstractClassEnhancePluginDefine> blurryClassEnhancePluginDefineMapping =
new HashMap<String, AbstractClassEnhancePluginDefine>();
private PluginDefineCategory(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
@ -21,13 +19,7 @@ public class PluginDefineCategory {
continue;
}
if (enhanceClassName.endsWith("*")) {
// 加上. 为了区分 com.ai.test com.ai.test1
blurryClassEnhancePluginDefineMapping
.put(enhanceClassName.substring(0, enhanceClassName.length() - 1), plugin);
} else {
exactClassEnhancePluginDefineMapping.put(enhanceClassName, plugin);
}
}
}
@ -38,27 +30,11 @@ public class PluginDefineCategory {
return pluginDefineCategory;
}
public Map<String, AbstractClassEnhancePluginDefine> getExactClassEnhancePluginDefineMapping() {
return pluginDefineCategory.exactClassEnhancePluginDefineMapping;
}
public Map<String, AbstractClassEnhancePluginDefine> getBlurryClassEnhancePluginDefineMapping() {
return blurryClassEnhancePluginDefineMapping;
}
public AbstractClassEnhancePluginDefine findPluginDefine(String enhanceClassName) {
if (exactClassEnhancePluginDefineMapping.containsKey(enhanceClassName)) {
return exactClassEnhancePluginDefineMapping.get(enhanceClassName);
}
for (Map.Entry<String, AbstractClassEnhancePluginDefine> entry : blurryClassEnhancePluginDefineMapping
.entrySet()) {
if (enhanceClassName.startsWith(entry.getKey())) {
return entry.getValue();
}
}
return null;
}
}

View File

@ -14,8 +14,9 @@ import java.util.Arrays;
import java.util.List;
/**
* 替代应用函数的main函数入口确保在程序入口处运行 <br/>
* 用于替代-javaagent的另一种模式 <br/>
* A test entrance for enhancing class.
* This should be used only in bytecode-manipulate test.
* And make sure, all classes which need to be enhanced, must not be loaded.
*
* @author wusheng
*/
@ -25,6 +26,15 @@ public class TracingBootstrap {
private TracingBootstrap() {
}
/**
* Main entrance for testing.
* @param args includes target classname ( which exists "public static void main(String[] args)" ) and arguments list.
* @throws PluginException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static void main(String[] args)
throws PluginException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException {

View File

@ -4,26 +4,57 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 被增强的类实例需扩展的context属性用于在不同的方法或者构造函数间保存实例
* Enhanced instance field type.
*
* Any plugins({@link com.a.eye.skywalking.plugin.AbstractClassEnhancePluginDefine}'s subclass) override
* {@link com.a.eye.skywalking.plugin.interceptor.enhance.ClassEnhancePluginDefine#getConstructorsInterceptPoints}
* and
* {@link com.a.eye.skywalking.plugin.interceptor.enhance.ClassEnhancePluginDefine#getInstanceMethodsInterceptPoints}
* will add a field with this type.
*
* @author wusheng
*
*/
public class EnhancedClassInstanceContext {
/**
* extend field, can store any instance as you want.
*/
private Map<Object, Object> context = new ConcurrentHashMap<Object, Object>();
/**
* store a new instance or override it.
* @param key
* @param value
*/
public void set(Object key, Object value){
context.put(key, value);
}
/**
* get an stored instance, if it is existed.
* @param key
* @return null or stored instance.
*/
public Object get(Object key){
return context.get(key);
}
/**
* judge whether stores by the key.
* @param key
* @return true, if stored a instance by the key.
*/
public boolean isContain(Object key){
return context.containsKey(key);
}
/**
* get an stored instance, if it is existed.
* @param key
* @param type
* @param <T> expected stored instance's type
* @return null or stored instance.
*/
public <T> T get(Object key, Class<T> type){
return (T)this.get(key);
}

View File

@ -17,11 +17,31 @@ import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.not;
/**
* This class controls all enhance operations, including enhance constructors, instance methods and static methods.
* All the enhances base on three types interceptor point: {@link ConstructorInterceptPoint}, {@link InstanceMethodsInterceptPoint} and {@link StaticMethodsInterceptPoint}
* If plugin is going to enhance constructors, instance methods, or both,
* {@link ClassEnhancePluginDefine} will add a field of {@link EnhancedClassInstanceContext} type.
*
* @author wusheng
*/
public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {
private static ILog logger = LogManager.getLogger(ClassEnhancePluginDefine.class);
/**
* New field name.
*/
public static final String contextAttrName = "_$EnhancedClassInstanceContext";
/**
* Begin to define how to enhance class.
* After invoke this method, only means definition is finished.
*
* @param enhanceOriginClassName target class name
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
* @throws PluginException
*/
@Override
protected DynamicType.Builder<?> enhance(String enhanceOriginClassName, DynamicType.Builder<?> newClassBuilder) throws PluginException {
newClassBuilder = this.enhanceClass(enhanceOriginClassName, newClassBuilder);
@ -31,6 +51,14 @@ public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePlugi
return newClassBuilder;
}
/**
* Enhance a class to intercept constructors and class instance methods.
*
* @param enhanceOriginClassName target class name
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
* @throws PluginException
*/
private DynamicType.Builder<?> enhanceInstance(String enhanceOriginClassName, DynamicType.Builder<?> newClassBuilder) throws PluginException {
ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
@ -65,17 +93,14 @@ public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePlugi
/**
* 2. enhance constructors
*/
newClassBuilder = newClassBuilder.constructor(ElementMatchers.<MethodDescription>any())
.intercept(SuperMethodCall.INSTANCE.andThen(
MethodDelegation.to(new DefaultClassConstructorInterceptor())
.appendParameterBinder(FieldProxy.Binder.install(FieldGetter.class, FieldSetter.class))));
newClassBuilder = newClassBuilder.constructor(ElementMatchers.<MethodDescription>any()).intercept(SuperMethodCall.INSTANCE
.andThen(MethodDelegation.to(new DefaultClassConstructorInterceptor()).appendParameterBinder(FieldProxy.Binder.install(FieldGetter.class, FieldSetter.class))));
if (existedConstructorInterceptPoint) {
for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
.intercept(SuperMethodCall.INSTANCE.andThen(
MethodDelegation.to(new ClassConstructorInterceptor(constructorInterceptPoint.getConstructorInterceptor()))
.appendParameterBinder(FieldProxy.Binder.install(FieldGetter.class, FieldSetter.class))));
newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(
MethodDelegation.to(new ClassConstructorInterceptor(constructorInterceptPoint.getConstructorInterceptor()))
.appendParameterBinder(FieldProxy.Binder.install(FieldGetter.class, FieldSetter.class))));
}
}
@ -92,20 +117,37 @@ public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePlugi
}
ClassInstanceMethodsInterceptor classMethodInterceptor = new ClassInstanceMethodsInterceptor(interceptor);
newClassBuilder = newClassBuilder
.method(not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher()))
.intercept(MethodDelegation.to(classMethodInterceptor));
newClassBuilder =
newClassBuilder.method(not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher())).intercept(MethodDelegation.to(classMethodInterceptor));
}
}
return newClassBuilder;
}
/**
* Constructor methods intercept point. See {@link ConstructorInterceptPoint}
*
* @return collections of {@link ConstructorInterceptPoint}
*/
protected abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();
/**
* Instance methods intercept point. See {@link InstanceMethodsInterceptPoint}
*
* @return collections of {@link InstanceMethodsInterceptPoint}
*/
protected abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();
/**
* Enhance a class to intercept class static methods.
*
* @param enhanceOriginClassName target class name
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
* @throws PluginException
*/
private DynamicType.Builder<?> enhanceClass(String enhanceOriginClassName, DynamicType.Builder<?> newClassBuilder) throws PluginException {
StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
@ -121,18 +163,16 @@ public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePlugi
ClassStaticMethodsInterceptor classMethodInterceptor = new ClassStaticMethodsInterceptor(interceptor);
newClassBuilder = newClassBuilder
.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
.intercept(MethodDelegation.to(classMethodInterceptor));
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher())).intercept(MethodDelegation.to(classMethodInterceptor));
}
return newClassBuilder;
}
/**
* 返回需要被增强的方法列表
* Static methods intercept point. See {@link StaticMethodsInterceptPoint}
*
* @return
* @return collections of {@link StaticMethodsInterceptPoint}
*/
protected abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
}

View File

@ -3,17 +3,21 @@ package com.a.eye.skywalking.plugin.interceptor.enhance;
import com.a.eye.skywalking.plugin.interceptor.StaticMethodsInterceptPoint;
/**
* 仅增强拦截实例方法
*
* @author wusheng
* Plugins, which only need enhance class static methods.
* Actually, inherit from {@link ClassInstanceMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}.
* Just override {@link ClassEnhancePluginDefine#getStaticMethodsInterceptPoints},
* and return {@link null}, which means nothing to enhance.
*
* @author wusheng
*/
public abstract class ClassInstanceMethodsEnhancePluginDefine extends
ClassEnhancePluginDefine {
public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
@Override
protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints(){
return null;
}
/**
* @return null, means enhance no static methods.
*/
@Override
protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
return null;
}
}

View File

@ -4,21 +4,28 @@ import com.a.eye.skywalking.plugin.interceptor.ConstructorInterceptPoint;
import com.a.eye.skywalking.plugin.interceptor.InstanceMethodsInterceptPoint;
/**
* 仅增强拦截类级别静态方法
*
* @author wusheng
* Plugins, which only need enhance class static methods.
* Actually, inherit from {@link ClassStaticMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}.
* Just override {@link ClassEnhancePluginDefine#getConstructorsInterceptPoints} and {@link ClassEnhancePluginDefine#getInstanceMethodsInterceptPoints},
* and return {@link null}, which means nothing to enhance.
*
* @author wusheng
*/
public abstract class ClassStaticMethodsEnhancePluginDefine extends
ClassEnhancePluginDefine {
public abstract class ClassStaticMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
@Override
protected ConstructorInterceptPoint[] getConstructorsInterceptPoints(){
return null;
}
/**
* @return null, means enhance no constructors.
*/
@Override
protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return null;
}
@Override
protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints(){
return null;
}
/**
* @return null, means enhance no instance methods.
*/
@Override
protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return null;
}
}

View File

@ -1,9 +1,12 @@
package com.a.eye.skywalking.plugin.interceptor.enhance;
/**
* Instance method invoke context.
* Beside all in {@link MethodInvokeContext}, plus Object instance ref.
*
* @author wusheng
*/
public class InstanceMethodInvokeContext extends MethodInvokeContext {
/**
* 代理类实例
*/
private Object objInst;
InstanceMethodInvokeContext(Object objInst, String methodName, Object[] allArguments, Class<?>[] argumentsTypes) {
@ -11,6 +14,10 @@ public class InstanceMethodInvokeContext extends MethodInvokeContext {
this.objInst = objInst;
}
/**
*
* @return the target instance's ref.
*/
public Object inst() {
return objInst;
}

View File

@ -2,10 +2,35 @@ package com.a.eye.skywalking.plugin.interceptor.enhance;
import com.a.eye.skywalking.plugin.interceptor.EnhancedClassInstanceContext;
/**
*
* A interceptor, which intercept method's invocation.
* The target methods will be defined in {@link ClassEnhancePluginDefine}'s subclass, most likely in {@link ClassInstanceMethodsEnhancePluginDefine}
*
*/
public interface InstanceMethodsAroundInterceptor {
/**
* called before target method invocation.
* @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
* @param interceptorContext method context, includes class name, method name, etc.
* @param result change this result, if you want to truncate the method.
*/
void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result);
/**
* called after target method invocation. Even method's invocation triggers an exception.
* @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
* @param interceptorContext method context, includes class name, method name, etc.
* @param ret the method's return original value.
* @return the method's actual return value.
*/
Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret);
/**
* called when occur exception.
* @param t the exception occur.
* @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
* @param interceptorContext method context, includes class name, method name, etc.
*/
void handleMethodException(Throwable t, EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext);
}

View File

@ -1,20 +1,14 @@
package com.a.eye.skywalking.plugin.interceptor.enhance;
/**
* 方法执行拦截上下文
* Method invoke context.
* Include method name, arguments list and argument types list.
*
* @author wusheng
*/
public class MethodInvokeContext {
/**
* 方法名称
*/
private String methodName;
/**
* 方法参数
*/
private Object[] allArguments;
private Class<?>[] argumentTypes;
MethodInvokeContext(String methodName, Object[] allArguments,Class<?>[] argumentTypes) {
@ -23,14 +17,23 @@ public class MethodInvokeContext {
this.argumentTypes = argumentTypes;
}
/**
* @return arguments list.
*/
public Object[] allArguments() {
return this.allArguments;
}
/**
* @return method name.
*/
public String methodName() {
return methodName;
}
/**
* @return argument types list.
*/
public Class<?>[] argumentTypes(){
return argumentTypes;
}

View File

@ -1,5 +1,11 @@
package com.a.eye.skywalking.plugin.interceptor.enhance;
/**
* Static method invoke context.
* Beside all in {@link MethodInvokeContext}, plus the class type.
*
* @author wusheng
*/
public class StaticMethodInvokeContext extends MethodInvokeContext {
/**
* 代理类名
@ -11,6 +17,10 @@ public class StaticMethodInvokeContext extends MethodInvokeContext {
this.clazz = clazz;
}
/**
*
* @return the target class
*/
public Class claszz() {
return clazz;
}