Commit 14fc924c authored by 吴晟's avatar 吴晟
Browse files

Fix interceptor instance singleton bug, and add some test cases.

parent 5c369aef
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -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();
+47 −38
Original line number Diff line number Diff line
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.
 * <p>
 * The <code>InterceptorInstanceLoader</code> 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.
 * <p>
 *
 * 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 {
@@ -31,14 +38,10 @@ public class InterceptorInstanceLoader {
        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 (inst == null) {
            if (InterceptorInstanceLoader.class.getClassLoader().equals(targetClassLoader)) {
            return (T) targetClassLoader.loadClass(className).newInstance();
        }

                inst = targetClassLoader.loadClass(className).newInstance();
            } else {
                instanceLoadLock.lock();
                try {
                    try {
@@ -49,15 +52,19 @@ public class InterceptorInstanceLoader {
                        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;
    }

    /**
@@ -72,7 +79,8 @@ public class InterceptorInstanceLoader {
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static <T> T loadBinary(String className, ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
    private static <T> 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);
        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 targetClassLoader the classloader, which should load the interceptor.
     * @param <T>
     * @return interceptor instance.
     */
    private static <T> T findLoadedClass(String className, ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
    private static <T> T findLoadedClass(String className,
        ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        Method defineClassMethod = null;
        Class<?> targetClassLoaderType = targetClassLoader.getClass();
        while (defineClassMethod == null && targetClassLoaderType != null) {
+17 −0
Original line number Diff line number Diff line
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);
    }
}
+46 −0
Original line number Diff line number Diff line
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"));
    }
}
+18 −0
Original line number Diff line number Diff line
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));
    }
}
Loading