diff --git a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/assist/NoCocurrencyAceessObject.java b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/assist/NoCocurrencyAceessObject.java
index 98b695033..4f69939e2 100644
--- a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/assist/NoCocurrencyAceessObject.java
+++ b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/assist/NoCocurrencyAceessObject.java
@@ -5,14 +5,15 @@ import com.a.eye.skywalking.api.plugin.interceptor.InterceptorException;
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
/**
- * {@link NoCocurrencyAceessObject} is an abstract class,
- * works for class's methods call each others, which these methods should be intercepted.
+ * {@link NoCocurrencyAceessObject} is method invocation counter,
+ * when {@link #whenEnter(EnhancedClassInstanceContext, Runnable)}, counter + 1;
+ * and when {@link #whenExist(EnhancedClassInstanceContext, Runnable)}, counter -1;
*
- * At this scenario, only the first access should be intercepted.
+ * When, and only when, the first enter and last exist, also meaning first access, the Runnable is called.
*
* @author wusheng
*/
-public abstract class NoCocurrencyAceessObject implements InstanceMethodsAroundInterceptor {
+public class NoCocurrencyAceessObject {
protected String invokeCounterKey = "__$invokeCounterKey";
protected Object invokeCounterInstLock = new Object();
diff --git a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/loader/InterceptorInstanceLoader.java b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/loader/InterceptorInstanceLoader.java
index 78addb934..4479cdb22 100644
--- a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/loader/InterceptorInstanceLoader.java
+++ b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/plugin/interceptor/loader/InterceptorInstanceLoader.java
@@ -1,5 +1,8 @@
package com.a.eye.skywalking.api.plugin.interceptor.loader;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
import com.a.eye.skywalking.logging.ILog;
import com.a.eye.skywalking.logging.LogManager;
import java.io.BufferedInputStream;
@@ -12,12 +15,16 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
- * The Classloader controller.
- * This is a very important class in sky-walking's auto-instrumentation mechanism.
- * If you want to fully understand why need this, and how it works, you need have knowledge about Classloader appointment mechanism.
- *
+ * The InterceptorInstanceLoader is a classes finder and container.
+ *
+ * This is a very important class in sky-walking's auto-instrumentation mechanism. If you want to fully understand why
+ * need this, and how it works, you need have knowledge about Classloader appointment mechanism.
+ *
* The loader will load a class, and focus the target class loader (be intercepted class's classloader) loads it.
- *
+ *
+ * If the target class and target class loader are same, the loaded classes( {@link InstanceConstructorInterceptor},
+ * {@link InstanceMethodsAroundInterceptor} and {@link StaticMethodsAroundInterceptor} implementations) stay in singleton.
+ *
* Created by wusheng on 16/8/2.
*/
public class InterceptorInstanceLoader {
@@ -28,43 +35,43 @@ public class InterceptorInstanceLoader {
private static ReentrantLock instanceLoadLock = new ReentrantLock();
public static T load(String className, ClassLoader targetClassLoader)
- throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
+ throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
String instanceKey = className + "_OF_" + targetClassLoader.getClass().getName() + "@" + Integer.toHexString(targetClassLoader.hashCode());
Object inst = INSTANCE_CACHE.get(instanceKey);
- if (inst != null) {
- return (T) inst;
- }
-
- if (InterceptorInstanceLoader.class.getClassLoader().equals(targetClassLoader)) {
- return (T) targetClassLoader.loadClass(className).newInstance();
- }
-
- instanceLoadLock.lock();
- try {
- try {
- inst = findLoadedClass(className, targetClassLoader);
- if (inst == null) {
- inst = loadBinary(className, targetClassLoader);
+ if (inst == null) {
+ if (InterceptorInstanceLoader.class.getClassLoader().equals(targetClassLoader)) {
+ inst = targetClassLoader.loadClass(className).newInstance();
+ } else {
+ instanceLoadLock.lock();
+ try {
+ try {
+ inst = findLoadedClass(className, targetClassLoader);
+ if (inst == null) {
+ inst = loadBinary(className, targetClassLoader);
+ }
+ if (inst == null) {
+ throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.");
+ }
+ } catch (Exception e) {
+ throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.", e);
+ }
+ } finally {
+ instanceLoadLock.unlock();
}
- if (inst == null) {
- throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.");
- }
- INSTANCE_CACHE.put(instanceKey, inst);
- return (T) inst;
- } catch (Exception e) {
- throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.", e);
}
- } finally {
- instanceLoadLock.unlock();
+ if (inst != null) {
+ INSTANCE_CACHE.put(instanceKey, inst);
+ }
}
+ return (T)inst;
}
/**
* load class from class binary files.
* Most likely all the interceptor implementations should be loaded by this.
*
- * @param className interceptor class name.
+ * @param className interceptor class name.
* @param targetClassLoader the classloader, which should load the interceptor.
* @param
* @return interceptor instance.
@@ -72,7 +79,8 @@ public class InterceptorInstanceLoader {
* @throws IllegalAccessException
* @throws InstantiationException
*/
- private static T loadBinary(String className, ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ private static T loadBinary(String className,
+ ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
String path = "/" + className.replace('.', '/').concat(".class");
byte[] data = null;
BufferedInputStream is = null;
@@ -111,21 +119,22 @@ public class InterceptorInstanceLoader {
}
}
defineClassMethod.setAccessible(true);
- logger.debug("load binary code of {} to classload {}", className, targetClassLoader);
- Class> type = (Class>) defineClassMethod.invoke(targetClassLoader, className, data, 0, data.length, null);
- return (T) type.newInstance();
+ logger.debug("load binary code of {} to classloader {}", className, targetClassLoader);
+ Class> type = (Class>)defineClassMethod.invoke(targetClassLoader, className, data, 0, data.length, null);
+ return (T)type.newInstance();
}
/**
* Find loaded class in the current classloader.
- * Just in case some classes have already been loaded for some reasons.s
+ * Just in case some classes have already been loaded for some reason.
*
- * @param className interceptor class name.
+ * @param className interceptor class name.
* @param targetClassLoader the classloader, which should load the interceptor.
* @param
* @return interceptor instance.
*/
- private static T findLoadedClass(String className, ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
+ private static T findLoadedClass(String className,
+ ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
Method defineClassMethod = null;
Class> targetClassLoaderType = targetClassLoader.getClass();
while (defineClassMethod == null && targetClassLoaderType != null) {
@@ -136,10 +145,10 @@ public class InterceptorInstanceLoader {
}
}
defineClassMethod.setAccessible(true);
- Class> type = (Class>) defineClassMethod.invoke(targetClassLoader, className);
+ Class> type = (Class>)defineClassMethod.invoke(targetClassLoader, className);
if (type == null) {
return null;
}
- return (T) type.newInstance();
+ return (T)type.newInstance();
}
}
diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/boot/ServiceManagerTest.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/boot/ServiceManagerTest.java
new file mode 100644
index 000000000..233a77f26
--- /dev/null
+++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/boot/ServiceManagerTest.java
@@ -0,0 +1,17 @@
+package com.a.eye.skywalking.api.boot;
+
+import com.a.eye.skywalking.api.context.ContextManager;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author wusheng
+ */
+public class ServiceManagerTest {
+ @Test
+ public void testBoot() {
+ ServiceManager.INSTANCE.boot();
+ ContextManager manager = ServiceManager.INSTANCE.findService(ContextManager.class);
+ Assert.assertNotNull(manager);
+ }
+}
diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/assist/NoCocurrencyAceessObjectTest.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/assist/NoCocurrencyAceessObjectTest.java
new file mode 100644
index 000000000..bcf2304a8
--- /dev/null
+++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/assist/NoCocurrencyAceessObjectTest.java
@@ -0,0 +1,46 @@
+package com.a.eye.skywalking.api.plugin.assist;
+
+import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
+import com.a.eye.skywalking.api.plugin.interceptor.assist.NoCocurrencyAceessObject;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author wusheng
+ */
+public class NoCocurrencyAceessObjectTest {
+ @Test
+ public void testEntraExitCounter(){
+ NoCocurrencyAceessObject object = new NoCocurrencyAceessObject();
+ final EnhancedClassInstanceContext context = new EnhancedClassInstanceContext();
+ object.whenEnter(context, new Runnable() {
+ @Override
+ public void run() {
+ context.set("firstEntrance", true);
+ }
+ });
+ object.whenEnter(context, new Runnable() {
+ @Override
+ public void run() {
+ context.set("secondEntrance", true);
+ }
+ });
+ object.whenExist(context, new Runnable() {
+ @Override
+ public void run() {
+ context.set("firstExit", true);
+ }
+ });
+ object.whenExist(context, new Runnable() {
+ @Override
+ public void run() {
+ context.set("lastEntrance", true);
+ }
+ });
+
+ Assert.assertTrue(!context.isContain("secondEntrance"));
+ Assert.assertTrue(!context.isContain("firstExit"));
+ Assert.assertTrue(context.isContain("firstEntrance"));
+ Assert.assertTrue(context.isContain("lastEntrance"));
+ }
+}
diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/interceptor/EnhancedClassInstanceContextTest.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/interceptor/EnhancedClassInstanceContextTest.java
new file mode 100644
index 000000000..154afe7fc
--- /dev/null
+++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/interceptor/EnhancedClassInstanceContextTest.java
@@ -0,0 +1,18 @@
+package com.a.eye.skywalking.api.plugin.interceptor;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author wusheng
+ */
+public class EnhancedClassInstanceContextTest {
+ @Test
+ public void test(){
+ EnhancedClassInstanceContext context = new EnhancedClassInstanceContext();
+ context.set("key", "value");
+ Assert.assertTrue(context.isContain("key"));
+ Assert.assertEquals("value", context.get("key"));
+ Assert.assertEquals("value", context.get("key", String.class));
+ }
+}
diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/InterceptorInstanceLoaderTest.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/InterceptorInstanceLoaderTest.java
new file mode 100644
index 000000000..0c229750d
--- /dev/null
+++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/InterceptorInstanceLoaderTest.java
@@ -0,0 +1,33 @@
+package com.a.eye.skywalking.api.plugin.loader;
+
+import com.a.eye.skywalking.api.plugin.interceptor.loader.InterceptorInstanceLoader;
+import java.lang.reflect.InvocationTargetException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author wusheng
+ */
+public class InterceptorInstanceLoaderTest {
+ @Test
+ public void load() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ ClassLoader mockClassLoader = new ClassLoader() {
+ @Override public Class> loadClass(String name) throws ClassNotFoundException {
+ return super.loadClass(name);
+ }
+ };
+ Object obj = InterceptorInstanceLoader.load("com.a.eye.skywalking.api.plugin.loader.NeverUsedTestClass", mockClassLoader);
+ Assert.assertTrue(obj != null);
+
+ Object obj2 = InterceptorInstanceLoader.load("com.a.eye.skywalking.api.plugin.loader.NeverUsedTestClass", mockClassLoader);
+ Assert.assertTrue(obj != null);
+ Assert.assertEquals(obj, obj2);
+
+ Object obj3 = InterceptorInstanceLoader.load("com.a.eye.skywalking.api.plugin.loader.NeverUsedTestClass", InterceptorInstanceLoaderTest.class.getClassLoader());
+ Assert.assertTrue(obj3 != null);
+
+ Object obj4 = InterceptorInstanceLoader.load("com.a.eye.skywalking.api.plugin.loader.NeverUsedTestClass", InterceptorInstanceLoaderTest.class.getClassLoader());
+ Assert.assertTrue(obj4 != null);
+ Assert.assertEquals(obj3, obj4);
+ }
+}
diff --git a/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/NeverUsedTestClass.java b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/NeverUsedTestClass.java
new file mode 100644
index 000000000..03977d001
--- /dev/null
+++ b/skywalking-sniffer/skywalking-api/src/test/java/com/a/eye/skywalking/api/plugin/loader/NeverUsedTestClass.java
@@ -0,0 +1,10 @@
+package com.a.eye.skywalking.api.plugin.loader;
+
+/**
+ * This class is only used in {@link InterceptorInstanceLoaderTest},
+ * never be created manually.
+ *
+ * @author wusheng
+ */
+public class NeverUsedTestClass {
+}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/jedis-2.x-plugin/src/main/java/com/a/eye/skywalking/plugin/jedis/v2/JedisMethodInterceptor.java b/skywalking-sniffer/skywalking-sdk-plugin/jedis-2.x-plugin/src/main/java/com/a/eye/skywalking/plugin/jedis/v2/JedisMethodInterceptor.java
index 01b9655c7..4cb17f016 100644
--- a/skywalking-sniffer/skywalking-sdk-plugin/jedis-2.x-plugin/src/main/java/com/a/eye/skywalking/plugin/jedis/v2/JedisMethodInterceptor.java
+++ b/skywalking-sniffer/skywalking-sdk-plugin/jedis-2.x-plugin/src/main/java/com/a/eye/skywalking/plugin/jedis/v2/JedisMethodInterceptor.java
@@ -4,6 +4,7 @@ import com.a.eye.skywalking.api.context.ContextManager;
import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
import com.a.eye.skywalking.api.plugin.interceptor.assist.NoCocurrencyAceessObject;
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.util.StringUtil;
import com.a.eye.skywalking.trace.Span;
@@ -16,7 +17,7 @@ import com.a.eye.skywalking.trace.tag.Tags;
*
* @author zhangxin
*/
-public class JedisMethodInterceptor extends NoCocurrencyAceessObject {
+public class JedisMethodInterceptor extends NoCocurrencyAceessObject implements InstanceMethodsAroundInterceptor {
/**
* The key name that redis connection information in {@link EnhancedClassInstanceContext#context}.
*/