diff --git a/agent/pom.xml b/agent/pom.xml
index 4af2ea1..4e81fbd 100755
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -8,11 +8,15 @@
0.0.2-SNAPSHOT
jar
+
+ 8.0.1
+
+
org.ow2.asm
- asm
- 8.0.1
+ asm-tree
+ ${asm-lib-version}
diff --git a/agent/src/main/java/com/alibaba/testable/constant/Const.java b/agent/src/main/java/com/alibaba/testable/constant/Const.java
new file mode 100644
index 0000000..9ab1b5c
--- /dev/null
+++ b/agent/src/main/java/com/alibaba/testable/constant/Const.java
@@ -0,0 +1,11 @@
+package com.alibaba.testable.constant;
+
+/**
+ * @author flin
+ */
+public class Const {
+
+ public static final String DOT = ".";
+ public static final String SLASH = "/";
+
+}
diff --git a/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java b/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java
new file mode 100644
index 0000000..77acb45
--- /dev/null
+++ b/agent/src/main/java/com/alibaba/testable/transformer/TestableClassTransformer.java
@@ -0,0 +1,33 @@
+package com.alibaba.testable.transformer;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+
+import java.io.IOException;
+
+/**
+ * @author flin
+ */
+public class TestableClassTransformer implements Opcodes {
+
+ private final ClassNode cn = new ClassNode();
+
+ public TestableClassTransformer(String className) throws IOException {
+ ClassReader cr = new ClassReader(className);
+ cr.accept(cn, 0);
+ }
+
+ public byte[] getBytes() {
+ transform();
+ ClassWriter cw = new ClassWriter( 0);
+ cn.accept(cw);
+ return cw.toByteArray();
+ }
+
+ private void transform() {
+
+ }
+
+}
diff --git a/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java b/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java
index 9fa54bf..147a8da 100644
--- a/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java
+++ b/agent/src/main/java/com/alibaba/testable/transformer/TestableFileTransformer.java
@@ -1,14 +1,13 @@
package com.alibaba.testable.transformer;
-import com.alibaba.testable.visitor.MethodRecordVisitor;
-import com.alibaba.testable.visitor.TestableVisitor;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
+import com.alibaba.testable.util.ClassUtil;
-import java.lang.annotation.Annotation;
+import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
+import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -17,55 +16,40 @@ import java.util.Set;
public class TestableFileTransformer implements ClassFileTransformer {
private static final String ENABLE_TESTABLE = "com.alibaba.testable.annotation.EnableTestable";
- private static final String DOT = ".";
- private static final String SLASH = "/";
+ private static final String ENABLE_TESTABLE_INJECT = "com.alibaba.testable.annotation.EnableTestableInject";
private static final String TEST_POSTFIX = "Test";
private static final Set loadedClassNames = new HashSet();
public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
- if (null == loader || null == className) {
+ if (isSystemClass(loader, className)) {
// Ignore system class
return null;
}
- String dotClassName = className.replace(SLASH, DOT);
- loadedClassNames.add(dotClassName);
- MethodRecordVisitor methodRecordVisitor = getMemberMethods(classfileBuffer, checkTestClass(dotClassName));
- if (!methodRecordVisitor.isNeedTransform()) {
+
+ List annotations = ClassUtil.getAnnotations(className);
+ List testAnnotations = ClassUtil.getAnnotations(className + TEST_POSTFIX);
+ if (!isNeedTransform(annotations, testAnnotations)) {
// Neither EnableTestable on test class, nor EnableTestableInject on source class
return null;
}
- ClassReader reader = new ClassReader(classfileBuffer);
- ClassWriter writer = new ClassWriter(reader, 0);
- reader.accept(new TestableVisitor(writer, methodRecordVisitor.getMethods()), 0);
- return writer.toByteArray();
- }
-
- private boolean checkTestClass(String dotClassName) {
- String testClassName = dotClassName + TEST_POSTFIX;
- if (loadedClassNames.contains(testClassName)) {
- try {
- Class> testClazz = Class.forName(testClassName);
- for (Annotation a : testClazz.getAnnotations()) {
- if (a.annotationType().getName().equals(ENABLE_TESTABLE)) {
- return true;
- }
- }
- } catch (ClassNotFoundException e) {
- return false;
- }
+ try {
+ return new TestableClassTransformer(className).getBytes();
+ } catch (IOException e) {
+ return null;
}
- return false;
}
- private MethodRecordVisitor getMemberMethods(byte[] classfileBuffer, boolean needTransform) {
- ClassReader reader = new ClassReader(classfileBuffer);
- ClassWriter writer = new ClassWriter(reader, 0);
- MethodRecordVisitor visitor = new MethodRecordVisitor(writer, needTransform);
- reader.accept(visitor, 0);
- return visitor;
+ private boolean isSystemClass(ClassLoader loader, String className) {
+ return !(loader instanceof URLClassLoader) || null == className || className.startsWith("jdk/");
+ }
+
+ private boolean isNeedTransform(List annotations, List testAnnotations) {
+ return annotations != null &&
+ (annotations.contains(ENABLE_TESTABLE_INJECT) ||
+ (testAnnotations != null && testAnnotations.contains(ENABLE_TESTABLE)));
}
}
diff --git a/agent/src/main/java/com/alibaba/testable/util/ClassUtil.java b/agent/src/main/java/com/alibaba/testable/util/ClassUtil.java
new file mode 100644
index 0000000..651da3b
--- /dev/null
+++ b/agent/src/main/java/com/alibaba/testable/util/ClassUtil.java
@@ -0,0 +1,33 @@
+package com.alibaba.testable.util;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.ClassNode;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.alibaba.testable.constant.Const.*;
+
+/**
+ * @author flin
+ */
+public class ClassUtil {
+
+ public static List getAnnotations(String className) {
+ try {
+ List annotations = new ArrayList();
+ ClassNode cn = new ClassNode();
+ new ClassReader(className).accept(cn, 0);
+ for (AnnotationNode an : cn.visibleAnnotations) {
+ String annotationName = an.desc.replace(SLASH, DOT).substring(1, an.desc.length() - 1);
+ annotations.add(annotationName);
+ }
+ return annotations;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/agent/src/main/java/com/alibaba/testable/visitor/MethodRecordVisitor.java b/agent/src/main/java/com/alibaba/testable/visitor/MethodRecordVisitor.java
deleted file mode 100755
index 4eb1124..0000000
--- a/agent/src/main/java/com/alibaba/testable/visitor/MethodRecordVisitor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.alibaba.testable.visitor;
-
-import com.alibaba.testable.model.MethodInfo;
-import org.objectweb.asm.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MethodRecordVisitor extends ClassVisitor {
-
- /**
- * Member methods
- */
- private final List methods = new ArrayList();
- private boolean needTransform;
-
- private static final String ENABLE_TESTABLE_INJECT = "Lcom/alibaba/testable/annotation/EnableTestableInject;";
-
- public List getMethods() {
- return methods;
- }
-
- public boolean isNeedTransform() {
- return needTransform;
- }
-
- public MethodRecordVisitor(ClassWriter cw, boolean needTransform) {
- super(Opcodes.ASM8, cw);
- this.needTransform = needTransform;
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
- if (descriptor.equals(ENABLE_TESTABLE_INJECT)) {
- needTransform = true;
- }
- return super.visitAnnotation(descriptor, visible);
- }
-
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- methods.add(new MethodInfo(access, name, desc, signature, exceptions));
- return super.visitMethod(access, name, desc, signature, exceptions);
- }
-}