commit
e71bebaf6a
|
|
@ -35,6 +35,12 @@
|
|||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.5.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<version>1.5.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
package com.a.eye.skywalking.api.plugin;
|
||||
|
||||
import com.a.eye.skywalking.api.plugin.utility.ClassFileExtraction;
|
||||
|
||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
|
||||
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.none;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
public class AbstractClassEnhancePluginDefineTest {
|
||||
static final String WEAVE_CLASS = "com.a.eye.skywalking.api.plugin.TargetObject";
|
||||
static final String INTERCEPTOR_CLASS = "com.a.eye.skywalking.api.plugin.MockPluginInterceptor";
|
||||
static final String WEAVE_INSTANCE_METHOD_NAME = "instanceMethod";
|
||||
static final String WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME = "instanceMethodWithException";
|
||||
static final String WEAVE_STATIC_METHOD_NAME = "staticMethod";
|
||||
private ClassLoader classLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(),
|
||||
ClassFileExtraction.of(TargetObject.class),
|
||||
null,
|
||||
ByteArrayClassLoader.PersistenceHandler.MANIFEST,
|
||||
PackageDefinitionStrategy.NoOp.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weaveInstanceMethod() throws Exception {
|
||||
ByteBuddyAgent.install();
|
||||
ClassFileTransformer classFileTransformer = new AgentBuilder.Default()
|
||||
.with(AgentBuilder.PoolStrategy.Default.FAST)
|
||||
.ignore(none())
|
||||
.type(ElementMatchers.is(TargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer())
|
||||
.installOnByteBuddyAgent();
|
||||
|
||||
try {
|
||||
Class<?> type = classLoader.loadClass(TargetObject.class.getName());
|
||||
assertThat(type.getDeclaredMethod(WEAVE_INSTANCE_METHOD_NAME).invoke(type.getDeclaredConstructor(String.class).newInstance("a"))
|
||||
, CoreMatchers.<Object>is(WEAVE_INSTANCE_METHOD_NAME + "a"));
|
||||
} finally {
|
||||
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void weaveInstanceMethodWITEXCEPTION() throws Exception {
|
||||
ByteBuddyAgent.install();
|
||||
ClassFileTransformer classFileTransformer = new AgentBuilder.Default()
|
||||
.with(AgentBuilder.PoolStrategy.Default.FAST)
|
||||
.ignore(none())
|
||||
.type(ElementMatchers.is(TargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer())
|
||||
.installOnByteBuddyAgent();
|
||||
|
||||
try {
|
||||
Class<?> type = classLoader.loadClass(TargetObject.class.getName());
|
||||
type.getDeclaredMethod(WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME).invoke(type.getDeclaredConstructor(String.class).newInstance("a"));
|
||||
} finally {
|
||||
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void weaveStaticMethod() throws Exception {
|
||||
ByteBuddyAgent.install();
|
||||
ClassFileTransformer classFileTransformer = new AgentBuilder.Default()
|
||||
.with(AgentBuilder.PoolStrategy.Default.FAST)
|
||||
.ignore(none())
|
||||
.type(ElementMatchers.is(TargetObject.class), ElementMatchers.is(classLoader)).transform(new MockTargetObjectTransformer())
|
||||
.installOnByteBuddyAgent();
|
||||
|
||||
try {
|
||||
Class<?> type = classLoader.loadClass(TargetObject.class.getName());
|
||||
assertThat(type.getDeclaredMethod(WEAVE_STATIC_METHOD_NAME).invoke(type), CoreMatchers.<Object>is(WEAVE_STATIC_METHOD_NAME + "_STATIC"));
|
||||
} finally {
|
||||
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MockTargetObjectTransformer implements AgentBuilder.Transformer {
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
|
||||
try {
|
||||
DynamicType.Builder newBuilder = transformInstanceMethod(builder);
|
||||
return transformStaticMethod(newBuilder);
|
||||
} catch (Exception exception) {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicType.Builder<?> transformStaticMethod(DynamicType.Builder newBuilder) {
|
||||
MockPluginStaticMethodInstrumentation staticMethodInstrumentation = new MockPluginStaticMethodInstrumentation();
|
||||
return staticMethodInstrumentation.define(WEAVE_CLASS, newBuilder);
|
||||
}
|
||||
|
||||
private DynamicType.Builder transformInstanceMethod(DynamicType.Builder<?> builder) {
|
||||
MockPluginInstanceMethodInstrumentation instrumentation = new MockPluginInstanceMethodInstrumentation();
|
||||
return instrumentation.define(WEAVE_CLASS, builder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.a.eye.skywalking.api.plugin;
|
||||
|
||||
import com.a.eye.skywalking.api.plugin.bytebuddy.AllObjectDefaultMethodsMatch;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.ConstructorInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
|
||||
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.INTERCEPTOR_CLASS;
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_CLASS;
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_INSTANCE_METHOD_NAME;
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
public class MockPluginInstanceMethodInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
|
||||
@Override
|
||||
protected String enhanceClassName() {
|
||||
return WEAVE_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
|
||||
return new ConstructorInterceptPoint[]{
|
||||
new ConstructorInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getConstructorMatcher() {
|
||||
return takesArgument(0, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConstructorInterceptor() {
|
||||
return INTERCEPTOR_CLASS;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
|
||||
return new InstanceMethodsInterceptPoint[]{
|
||||
new InstanceMethodsInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getMethodsMatcher() {
|
||||
return named(WEAVE_INSTANCE_METHOD_NAME).and(not(AllObjectDefaultMethodsMatch.INSTANCE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodsInterceptor() {
|
||||
return INTERCEPTOR_CLASS;
|
||||
}
|
||||
},
|
||||
new InstanceMethodsInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getMethodsMatcher() {
|
||||
return named(WEAVE_INSTANCE_WITH_EXCEPTION_METHOD_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodsInterceptor() {
|
||||
return INTERCEPTOR_CLASS;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.a.eye.skywalking.api.plugin;
|
||||
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.ConstructorInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceConstructorInterceptor;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.StaticMethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
|
||||
|
||||
public class MockPluginInterceptor implements InstanceMethodsAroundInterceptor, StaticMethodsAroundInterceptor, InstanceConstructorInterceptor {
|
||||
@Override
|
||||
public void beforeMethod(StaticMethodInvokeContext interceptorContext, MethodInterceptResult result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterMethod(StaticMethodInvokeContext interceptorContext, Object ret) {
|
||||
return ret + "_STATIC";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMethodException(Throwable t, MethodInvokeContext interceptorContext) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret) {
|
||||
return ret + String.valueOf(context.get("VALUE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMethodException(Throwable t, EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConstruct(EnhancedClassInstanceContext context, ConstructorInvokeContext interceptorContext) {
|
||||
context.set("VALUE" , interceptorContext.allArguments()[0]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.a.eye.skywalking.api.plugin;
|
||||
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.StaticMethodsInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine;
|
||||
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.INTERCEPTOR_CLASS;
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_CLASS;
|
||||
import static com.a.eye.skywalking.api.plugin.AbstractClassEnhancePluginDefineTest.WEAVE_STATIC_METHOD_NAME;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
public class MockPluginStaticMethodInstrumentation extends ClassStaticMethodsEnhancePluginDefine {
|
||||
@Override
|
||||
protected String enhanceClassName() {
|
||||
return WEAVE_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
|
||||
return new StaticMethodsInterceptPoint[]{
|
||||
new StaticMethodsInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getMethodsMatcher() {
|
||||
return named(WEAVE_STATIC_METHOD_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodsInterceptor() {
|
||||
return INTERCEPTOR_CLASS;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.a.eye.skywalking.api.plugin;
|
||||
|
||||
public class TargetObject {
|
||||
|
||||
private String value;
|
||||
|
||||
public TargetObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String instanceMethod() {
|
||||
return "instanceMethod";
|
||||
}
|
||||
|
||||
public String instanceMethodWithException() {
|
||||
throw new RuntimeException("test exception");
|
||||
}
|
||||
|
||||
public static String staticMethod() {
|
||||
return "staticMethod";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.a.eye.skywalking.api.plugin.utility;
|
||||
|
||||
import net.bytebuddy.ClassFileVersion;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.field.FieldDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
|
||||
import net.bytebuddy.implementation.bytecode.StackManipulation;
|
||||
import net.bytebuddy.jar.asm.ClassReader;
|
||||
import net.bytebuddy.jar.asm.ClassWriter;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class ClassFileExtraction {
|
||||
|
||||
private static final int CA = 0xCA, FE = 0xFE, BA = 0xBA, BE = 0xBE;
|
||||
|
||||
public static Map<String, byte[]> of(Class<?>... type) throws IOException {
|
||||
Map<String, byte[]> result = new HashMap<String, byte[]>();
|
||||
for (Class<?> aType : type) {
|
||||
result.put(aType.getName(), extract(aType));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] extract(Class<?> type, AsmVisitorWrapper asmVisitorWrapper) throws IOException {
|
||||
ClassReader classReader = new ClassReader(type.getName());
|
||||
ClassWriter classWriter = new ClassWriter(classReader, AsmVisitorWrapper.NO_FLAGS);
|
||||
classReader.accept(asmVisitorWrapper.wrap(new TypeDescription.ForLoadedType(type),
|
||||
classWriter,
|
||||
new IllegalContext(),
|
||||
TypePool.Empty.INSTANCE,
|
||||
AsmVisitorWrapper.NO_FLAGS,
|
||||
AsmVisitorWrapper.NO_FLAGS), AsmVisitorWrapper.NO_FLAGS);
|
||||
return classWriter.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] extract(Class<?> type) throws IOException {
|
||||
return extract(type, new AsmVisitorWrapper.Compound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassFileExtraction() throws Exception {
|
||||
byte[] binaryFoo = extract(Foo.class);
|
||||
assertThat(binaryFoo.length > 4, is(true));
|
||||
assertThat(binaryFoo[0], is(new Integer(CA).byteValue()));
|
||||
assertThat(binaryFoo[1], is(new Integer(FE).byteValue()));
|
||||
assertThat(binaryFoo[2], is(new Integer(BA).byteValue()));
|
||||
assertThat(binaryFoo[3], is(new Integer(BE).byteValue()));
|
||||
}
|
||||
|
||||
private static class Foo {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
private static class IllegalContext implements Implementation.Context {
|
||||
|
||||
@Override
|
||||
public TypeDescription register(AuxiliaryType auxiliaryType) {
|
||||
throw new AssertionError("Did not expect method call");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType) {
|
||||
throw new AssertionError("Did not expect method call");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDescription getInstrumentedType() {
|
||||
throw new AssertionError("Did not expect method call");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassFileVersion getClassFileVersion() {
|
||||
throw new AssertionError("Did not expect method call");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue