Fixed invalid classloading when using the AspectJ + Spring AOP
- support spring aspectj weaving
- support OSGI  (atlassian jira)
  -   #1583  Enhancement PlaingClassLoaderHandler for Jira.

profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java
profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChain.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilter.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java
profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjectorTest.java profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChainTest.java profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilterTest.java profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PluginPackageFilterTest.java
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java
new file mode 100644
index 000000000000..91be6fb7a63e
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java
@@ -0,0 +1,89 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.instrument;
+
+import com.navercorp.pinpoint.exception.PinpointException;
+import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class BootstrapClassLoaderHandler implements ClassInjector {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final PluginConfig pluginConfig;
+
+    private final Object lock = new Object();
+    private boolean injectedToRoot = false;
+
+    public BootstrapClassLoaderHandler(PluginConfig pluginConfig) {
+        if (pluginConfig == null) {
+            throw new NullPointerException("pluginConfig must not be null");
+        }
+        this.pluginConfig = pluginConfig;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> Class<? extends T> injectClass(ClassLoader classLoader, String className) {
+        try {
+            if (classLoader == null) {
+                return (Class<T>)injectClass0(className);
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e);
+            throw new PinpointException("Failed to load plugin class " + className + " with classLoader " + classLoader, e);
+        }
+        throw new PinpointException("invalid ClassLoader");
+    }
+
+    private Class<?> injectClass0(String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
+        synchronized (lock) {
+            if (this.injectedToRoot == false) {
+                this.injectedToRoot = true;
+                pluginConfig.getInstrumentation().appendToBootstrapClassLoaderSearch(pluginConfig.getPluginJarFile());
+                pluginConfig.getClassPool().appendToBootstrapClassPath(pluginConfig.getPluginJarFile().getName());
+            }
+        }
+        return Class.forName(className, false, null);
+    }
+
+
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java
index 1733f61f1c9e..565ac8031d78 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjector.java
@@ -3,9 +3,9 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,20 +14,9 @@
  */
 package com.navercorp.pinpoint.profiler.instrument;
 
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.URL;
 import java.net.URLClassLoader;
-import java.util.*;
 
-import com.navercorp.pinpoint.profiler.plugin.ClassLoadingChecker;
 import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.LoaderClassPath;
-import javassist.NotFoundException;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,37 +29,18 @@
  */
 public class JarProfilerPluginClassInjector implements ClassInjector {
     private final Logger logger = LoggerFactory.getLogger(JarProfilerPluginClassInjector.class);
-    private final boolean isDebug = logger.isDebugEnabled();
-
-    private static final Method ADD_URL;
-    private static final Method DEFINE_CLASS;
-    
-    static {
-        try {
-            ADD_URL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
-            ADD_URL.setAccessible(true);
-        } catch (Exception e) {
-            throw new PinpointException("Cannot access URLClassLoader.addURL(URL)", e);
-        }
-        
-        try {
-            DEFINE_CLASS = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
-            DEFINE_CLASS.setAccessible(true);
-        } catch (Exception e) {
-            throw new PinpointException("Cannot access ClassLoader.defineClass(String, byte[], int, int)", e);
-        }
-    }
-    
-    private final PluginConfig pluginConfig;
 
-    private final Object lock = new Object();
-    private boolean injectedToRoot = false;
+    private final ClassInjector bootstrapClassLoaderHandler;
+    private final ClassInjector urlClassLoaderHandler;
+    private final ClassInjector plainClassLoaderHandler;
 
     public JarProfilerPluginClassInjector(PluginConfig pluginConfig) {
         if (pluginConfig == null) {
             throw new NullPointerException("pluginConfig must not be null");
         }
-        this.pluginConfig = pluginConfig;
+        this.bootstrapClassLoaderHandler = new BootstrapClassLoaderHandler(pluginConfig);
+        this.urlClassLoaderHandler = new URLClassLoaderHandler(pluginConfig);
+        this.plainClassLoaderHandler = new PlainClassLoaderHandler(pluginConfig);
     }
 
     @Override
@@ -78,12 +48,12 @@ public JarProfilerPluginClassInjector(PluginConfig pluginConfig) {
     public <T> Class<? extends T> injectClass(ClassLoader classLoader, String className) {
         try {
             if (classLoader == null) {
-                return (Class<T>)injectToBootstrapClassLoader(className);
+                return (Class<T>)bootstrapClassLoaderHandler.injectClass(null, className);
             } else if (classLoader instanceof URLClassLoader) {
                 final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
-                return (Class<T>)injectToURLClassLoader(urlClassLoader, className);
+                return (Class<T>)urlClassLoaderHandler.injectClass(urlClassLoader, className);
             } else {
-                return (Class<T>)injectToPlainClassLoader(classLoader, className);
+                return (Class<T>)plainClassLoaderHandler.injectClass(classLoader, className);
             }
         } catch (Exception e) {
             logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e);
@@ -91,134 +61,4 @@ public <T> Class<? extends T> injectClass(ClassLoader classLoader, String classN
         }
     }
 
-    private Class<?> injectToBootstrapClassLoader(String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
-        synchronized (lock) {
-            if (this.injectedToRoot == false) {
-                this.injectedToRoot = true;
-                pluginConfig.getInstrumentation().appendToBootstrapClassLoaderSearch(pluginConfig.getPluginJarFile());
-                pluginConfig.getClassPool().appendToBootstrapClassPath(pluginConfig.getPluginJarFile().getName());
-            }
-        }
-        
-        return Class.forName(className, false, null);
-    }
-
-    private Class<?> injectToURLClassLoader(URLClassLoader classLoader, String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
-        final URL[] urls = classLoader.getURLs();
-        if (urls != null) {
-
-            boolean hasPluginJar = false;
-            for (URL url : urls) {
-                // if (url.equals(pluginJarURL)) { fix very slow
-                // http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html
-                final String externalForm = url.toExternalForm();
-                if (pluginConfig.getPluginJarURLExternalForm().equals(externalForm)) {
-                    hasPluginJar = true;
-                    break;
-                }
-            }
-
-            if (!hasPluginJar) {
-                ADD_URL.invoke(classLoader, pluginConfig.getPluginJar());
-            }
-        }
-
-        return classLoader.loadClass(className);
-    }
-    
-    private Class<?> injectToPlainClassLoader(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, IOException, CannotCompileException, IllegalAccessException, InvocationTargetException {
-        if (isDebug) {
-            logger.debug("injectToPlainClassLoader className:{} cl:{}", className, classLoader);
-        }
-        logger.info("bootstrapCoreJarPath:{}", pluginConfig.getBootstrapCoreJarPath());
-
-        final ClassPool pool = createClassPool(classLoader);
-
-        // TODO ClassLoader + ClassName key?
-        // TODO concurrent class loading
-        final ClassLoadingChecker classLoadingChecker = new ClassLoadingChecker();
-        return injectToPlainClassLoader(pool, classLoader, className, classLoadingChecker);
-    }
-
-    private ClassPool createClassPool(ClassLoader classLoader) throws NotFoundException {
-        final ClassPool pool = new ClassPool();
-        pool.appendClassPath(pluginConfig.getBootstrapCoreJarPath());
-
-        pool.appendClassPath(new LoaderClassPath(classLoader));
-        pool.appendClassPath(pluginConfig.getPluginJarFile().getName());
-        return pool;
-    }
-
-    private Class<?> injectToPlainClassLoader(ClassPool pool, ClassLoader classLoader, String className, ClassLoadingChecker classLoadingChecker) throws NotFoundException, IOException, CannotCompileException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-        if (pluginConfig.getProfilerPackageFilter().accept(className)) {
-            if (isDebug) {
-                logger.debug("ProfilerFilter skip class {}", className);
-            }
-            return null;
-        }
-        if (!pluginConfig.getPluginPackageFilter().accept(className)) {
-            if (isDebug) {
-                logger.debug("PluginFilter skip class:{}", className);
-            }
-            return null;
-        }
-        if (!classLoadingChecker.isFirstLoad(className)) {
-            if (isDebug) {
-                logger.debug("skip already loaded class:{}", className);
-            }
-            return null;
-        }
-
-        Class<?> c = null;
-        try {
-            c = classLoader.loadClass(className);
-            if (isDebug) {
-                logger.debug("loadClass:{}", className);
-            }
-        } catch (ClassNotFoundException ex) {
-            if (isDebug) {
-                logger.debug("ClassNotFound {}", ex.getMessage());
-            }
-        }
-        if (c != null) {
-            return c;
-        }
-
-        final CtClass ct = pool.getOrNull(className);
-        if (ct == null) {
-            throw new NotFoundException(className);
-        }
-
-
-        final CtClass superClass = ct.getSuperclass();
-        if (superClass != null) {
-            if ("java.lang.Object".equals(superClass.getName())) {
-                return null;
-            }
-            injectToPlainClassLoader(pool, classLoader, superClass.getName(), classLoadingChecker);
-        }
-
-        final CtClass[] interfaces = ct.getInterfaces();
-        for (CtClass ctInterface : interfaces) {
-            injectToPlainClassLoader(pool, classLoader, ctInterface.getName(), classLoadingChecker);
-        }
-        @SuppressWarnings("unchecked")
-        final Collection<String> referenceClassList = ct.getRefClasses();
-        if (isDebug) {
-            logger.debug("target:{} referenceClassList:{}", className, referenceClassList);
-        }
-        for (String referenceClass : referenceClassList) {
-            try {
-                injectToPlainClassLoader(pool, classLoader, referenceClass, classLoadingChecker);
-            } catch (NotFoundException e) {
-                logger.warn("Skip a referenced class because of NotFoundException : {}", e.getMessage(), e);
-            }
-        }
-        if (logger.isInfoEnabled()) {
-            logger.info("defineClass pluginClass:{} cl:{}", className, classLoader);
-        }
-        final byte[] bytes = ct.toBytecode();
-        return (Class<?>)DEFINE_CLASS.invoke(classLoader, ct.getName(), bytes, 0, bytes.length);
-    }
-
 }
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java
new file mode 100644
index 000000000000..0596326dd427
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java
@@ -0,0 +1,186 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.instrument;
+
+import com.navercorp.pinpoint.exception.PinpointException;
+import com.navercorp.pinpoint.profiler.plugin.ClassLoadingChecker;
+import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import java.util.Collection;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class PlainClassLoaderHandler implements ClassInjector {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    private final boolean isDebug = logger.isDebugEnabled();
+
+    private static final Method DEFINE_CLASS;
+
+    static {
+        try {
+            DEFINE_CLASS = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
+            DEFINE_CLASS.setAccessible(true);
+        } catch (Exception e) {
+            throw new PinpointException("Cannot access ClassLoader.defineClass(String, byte[], int, int)", e);
+        }
+    }
+
+    private final PluginConfig pluginConfig;
+
+
+    public PlainClassLoaderHandler(PluginConfig pluginConfig) {
+        if (pluginConfig == null) {
+            throw new NullPointerException("pluginConfig must not be null");
+        }
+        this.pluginConfig = pluginConfig;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> Class<? extends T> injectClass(ClassLoader classLoader, String className) {
+        try {
+             return (Class<T>)injectClass0(classLoader, className);
+        } catch (Exception e) {
+            logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e);
+            throw new PinpointException("Failed to load plugin class " + className + " with classLoader " + classLoader, e);
+        }
+    }
+
+
+    private Class<?> injectClass0(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, IOException, CannotCompileException, IllegalAccessException, InvocationTargetException {
+        if (isDebug) {
+            logger.debug("injectClass0 className:{} cl:{}", className, classLoader);
+        }
+        logger.info("bootstrapCoreJarPath:{}", pluginConfig.getBootstrapCoreJarPath());
+
+        final ClassPool pool = createClassPool(classLoader);
+
+        // TODO ClassLoader + ClassName key?
+        // TODO concurrent class loading
+        final ClassLoadingChecker classLoadingChecker = new ClassLoadingChecker();
+        return injectClass0(pool, classLoader, className, classLoadingChecker);
+    }
+
+    private ClassPool createClassPool(ClassLoader classLoader) throws NotFoundException {
+        final ClassPool pool = new ClassPool();
+        final String bootstrapCoreJarPath = pluginConfig.getBootstrapCoreJarPath();
+        pool.appendClassPath(bootstrapCoreJarPath);
+
+        final LoaderClassPath loaderClassPath = new LoaderClassPath(classLoader);
+        pool.appendClassPath(loaderClassPath);
+
+        final String pluginJarFileName = pluginConfig.getPluginJarFile().getName();
+        pool.appendClassPath(pluginJarFileName);
+        return pool;
+    }
+
+    private Class<?> injectClass0(ClassPool pool, ClassLoader classLoader, String className, ClassLoadingChecker classLoadingChecker) throws NotFoundException, IOException, CannotCompileException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        if (!pluginConfig.getPluginPackageFilter().accept(className)) {
+            if (isDebug) {
+                logger.debug("PluginFilter skip class:{}", className);
+            }
+            return null;
+        }
+        if (!classLoadingChecker.isFirstLoad(className)) {
+            if (isDebug) {
+                logger.debug("skip already loaded class:{}", className);
+            }
+            return null;
+        }
+
+        Class<?> c = null;
+        try {
+            c = classLoader.loadClass(className);
+            if (isDebug) {
+                logger.debug("loadClass:{}", className);
+            }
+        } catch (ClassNotFoundException ex) {
+            if (isDebug) {
+                logger.debug("ClassNotFound {}", ex.getMessage());
+            }
+        }
+        if (c != null) {
+            return c;
+        }
+
+        final CtClass ct = pool.getOrNull(className);
+        if (ct == null) {
+            throw new NotFoundException(className);
+        }
+
+
+        final CtClass superClass = ct.getSuperclass();
+        if (superClass != null) {
+            if ("java.lang.Object".equals(superClass.getName())) {
+                return null;
+            }
+            injectClass0(pool, classLoader, superClass.getName(), classLoadingChecker);
+        }
+
+        final CtClass[] interfaces = ct.getInterfaces();
+        for (CtClass ctInterface : interfaces) {
+            injectClass0(pool, classLoader, ctInterface.getName(), classLoadingChecker);
+        }
+        @SuppressWarnings("unchecked")
+        final Collection<String> referenceClassList = ct.getRefClasses();
+        if (isDebug) {
+            logger.debug("target:{} referenceClassList:{}", className, referenceClassList);
+        }
+        for (String referenceClass : referenceClassList) {
+            try {
+                injectClass0(pool, classLoader, referenceClass, classLoadingChecker);
+            } catch (NotFoundException e) {
+                logger.warn("Skip a referenced class because of NotFoundException : {}", e.getMessage(), e);
+            }
+        }
+        if (logger.isInfoEnabled()) {
+            logger.info("defineClass pluginClass:{} cl:{}", className, classLoader);
+        }
+        final byte[] bytes = ct.toBytecode();
+        return (Class<?>)DEFINE_CLASS.invoke(classLoader, ct.getName(), bytes, 0, bytes.length);
+    }
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java
new file mode 100644
index 000000000000..1e9498a7e173
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java
@@ -0,0 +1,122 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.instrument;
+
+import com.navercorp.pinpoint.exception.PinpointException;
+import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class URLClassLoaderHandler implements ClassInjector {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private static final Method ADD_URL;
+
+    static {
+        try {
+            ADD_URL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+            ADD_URL.setAccessible(true);
+        } catch (Exception e) {
+            throw new PinpointException("Cannot access URLClassLoader.addURL(URL)", e);
+        }
+    }
+
+    private final URL pluginURL;
+    private final String pluginURLString;
+
+    public URLClassLoaderHandler(PluginConfig pluginConfig) {
+        if (pluginConfig == null) {
+            throw new NullPointerException("pluginConfig must not be null");
+        }
+        this.pluginURL = pluginConfig.getPluginJar();
+        this.pluginURLString = pluginURL.toExternalForm();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> Class<? extends T> injectClass(ClassLoader classLoader, String className) {
+        try {
+            if (classLoader instanceof URLClassLoader) {
+                final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+                return (Class<T>)injectClass0(urlClassLoader, className);
+            }
+        } catch (Exception e) {
+            logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e);
+            throw new PinpointException("Failed to load plugin class " + className + " with classLoader " + classLoader, e);
+        }
+        throw new PinpointException("invalid ClassLoader");
+    }
+
+
+
+    private Class<?> injectClass0(URLClassLoader classLoader, String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
+        final URL[] urls = classLoader.getURLs();
+        if (urls != null) {
+
+            final boolean hasPluginJar = hasPluginJar(urls);
+
+            if (!hasPluginJar) {
+                logger.debug("add Jar:{}", pluginURLString);
+                ADD_URL.invoke(classLoader, pluginURL);
+            }
+        }
+
+        return classLoader.loadClass(className);
+    }
+
+    private boolean hasPluginJar(URL[] urls) {
+        for (URL url : urls) {
+            // if (url.equals(pluginJarURL)) { fix very slow
+            // http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html
+            final String externalForm = url.toExternalForm();
+            if (pluginURLString.equals(externalForm)) {
+
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChain.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChain.java
new file mode 100644
index 000000000000..801abc09b176
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChain.java
@@ -0,0 +1,47 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class ClassNameFilterChain implements ClassNameFilter {
+
+    private final List<ClassNameFilter> filterChain;
+
+    public ClassNameFilterChain(List<ClassNameFilter> filterChain) {
+        if (filterChain == null) {
+            throw new NullPointerException("filterChain must not be null");
+        }
+        this.filterChain = new ArrayList<ClassNameFilter>(filterChain);
+    }
+
+
+    @Override
+    public boolean accept(String className) {
+        for (ClassNameFilter classNameFilter : this.filterChain) {
+            if (!classNameFilter.accept(className)) {
+                return REJECT;
+            }
+        }
+        return ACCEPT;
+    }
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilter.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilter.java
new file mode 100644
index 000000000000..5ce71bc9ec60
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilter.java
@@ -0,0 +1,74 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.plugin;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class PinpointProfilerPackageSkipFilter implements ClassNameFilter {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    private final List<String> packageList;
+
+    public PinpointProfilerPackageSkipFilter() {
+        this(getPinpointPackageList());
+    }
+
+    public PinpointProfilerPackageSkipFilter(List<String> packageList) {
+        if (packageList == null) {
+            throw new NullPointerException("packageList must not be null");
+        }
+        this.packageList = new ArrayList<String>(packageList);
+    }
+
+
+
+    @Override
+    public boolean accept(String className) {
+        if (className == null) {
+            throw new NullPointerException("className must not be null");
+        }
+
+        for (String packageName : packageList) {
+            if (className.startsWith(packageName)) {
+                if (logger.isDebugEnabled()) {
+                    logger.info("skip ProfilerPackage:{} Class:{}", packageName, className);
+                }
+                return REJECT;
+            }
+        }
+        return ACCEPT;
+    }
+
+    private static List<String> getPinpointPackageList() {
+        List<String> pinpointPackageList = new ArrayList<String>();
+        pinpointPackageList.add("com.navercorp.pinpoint.bootstrap");
+        pinpointPackageList.add("com.navercorp.pinpoint.profiler");
+        pinpointPackageList.add("com.navercorp.pinpoint.common");
+        pinpointPackageList.add("com.navercorp.pinpoint.exception");
+        // TODO move test package
+        pinpointPackageList.add("com.navercorp.pinpoint.test");
+        return pinpointPackageList;
+    }
+}
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java
index 3ea006ffe3fd..7543b58d138c 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/PluginConfig.java
@@ -19,7 +19,6 @@
 
 import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClassPool;
 import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
-import com.navercorp.pinpoint.bootstrap.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,9 +30,7 @@
 import java.net.URL;
 import java.util.Collections;
 import java.util.List;
-import java.util.jar.Attributes;
 import java.util.jar.JarFile;
-import java.util.jar.Manifest;
 
 /**
  * @author Woonduk Kang(emeroad)
@@ -50,15 +47,14 @@ public class PluginConfig {
     private String pluginJarURLExternalForm;
 
     private final ProfilerPlugin plugin;
-    private final Manifest manifest;
 
     private final Instrumentation instrumentation;
     private final InstrumentClassPool classPool;
     private final String bootstrapCoreJarPath;
+
     private final ClassNameFilter pluginPackageFilter;
-    private final ClassNameFilter profilerPackageFilter;
 
-    public PluginConfig(URL pluginJar, ProfilerPlugin plugin, Instrumentation instrumentation, InstrumentClassPool classPool, String bootstrapCoreJarPath) {
+    public PluginConfig(URL pluginJar, ProfilerPlugin plugin, Instrumentation instrumentation, InstrumentClassPool classPool, String bootstrapCoreJarPath, ClassNameFilter pluginPackageFilter) {
         if (pluginJar == null) {
             throw new NullPointerException("pluginJar must not be null");
         }
@@ -68,37 +64,16 @@ public PluginConfig(URL pluginJar, ProfilerPlugin plugin, Instrumentation instru
         this.pluginJar = pluginJar;
         this.pluginJarFile = createJarFile(pluginJar);
         this.plugin = plugin;
-        this.manifest = this.getManifest();
 
         this.instrumentation = instrumentation;
         this.classPool = classPool;
         this.bootstrapCoreJarPath = bootstrapCoreJarPath;
 
-        final List<String> pluginPackageList = getPluginPackage(manifest);
-        if (logger.isInfoEnabled()) {
-            logger.info("{} Plugin Package:{}", plugin.getClass(), pluginPackageList);
-        }
-        this.pluginPackageFilter = new PluginPackageFilter(pluginPackageList);
-        this.profilerPackageFilter = new PinpointProfilerPackageFilter();
+        this.pluginPackageFilter = pluginPackageFilter;
     }
 
-    private Manifest getManifest() {
-        try {
-            return pluginJarFile.getManifest();
-        } catch (IOException e) {
-            // return empty
-            return new Manifest();
-        }
-    }
 
-    public List<String> getPluginPackage(Manifest manifest) {
-        final Attributes attributes = manifest.getMainAttributes();
-        final String pluginPackage = attributes.getValue(PINPOINT_PLUGIN_PACKAGE);
-        if (pluginPackage == null) {
-            return DEFAULT_PINPOINT_PLUGIN_PACKAGE_NAME;
-        }
-        return StringUtils.splitAndTrim(pluginPackage, ",");
-    }
+
 
     public ProfilerPlugin getPlugin() {
         return plugin;
@@ -146,7 +121,5 @@ public ClassNameFilter getPluginPackageFilter() {
         return pluginPackageFilter;
     }
 
-    public ClassNameFilter getProfilerPackageFilter() {
-        return profilerPackageFilter;
-    }
-}
+
+}
\ No newline at end of file
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java
index df996756da4b..7a4d8b8e30a9 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java
@@ -14,14 +14,23 @@
  */
 package com.navercorp.pinpoint.profiler.plugin;
 
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
 
 import com.navercorp.pinpoint.bootstrap.instrument.GuardInstrumentContext;
 import com.navercorp.pinpoint.bootstrap.instrument.InstrumentContext;
 import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate;
 import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware;
+import com.navercorp.pinpoint.bootstrap.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,6 +48,8 @@ public class ProfilerPluginLoader {
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final DefaultAgent agent;
 
+    private final ClassNameFilter profilerPackageFilter = new PinpointProfilerPackageSkipFilter();
+
     public ProfilerPluginLoader(DefaultAgent agent) {
         if (agent == null) {
             throw new NullPointerException("agent must not be null");
@@ -51,31 +62,87 @@ public List<DefaultProfilerPluginContext> load(URL[] pluginJars) {
         List<String> disabled = agent.getProfilerConfig().getDisabledPlugins();
         
         for (URL jar : pluginJars) {
-            List<ProfilerPlugin> plugins = PluginLoader.load(ProfilerPlugin.class, new URL[] { jar });
-            
+
+            final JarFile pluginJarFile = createJarFile(jar);
+            final List<String> pluginPackageList = getPluginPackage(pluginJarFile);
+
+            final ClassNameFilter pluginFilterChain = createPluginFilterChain(pluginPackageList);
+
+            final List<ProfilerPlugin> plugins = PluginLoader.load(ProfilerPlugin.class, new URL[] { jar });
+
             for (ProfilerPlugin plugin : plugins) {
                 if (disabled.contains(plugin.getClass().getName())) {
                     logger.info("Skip disabled plugin: {}", plugin.getClass().getName());
                     continue;
                 }
+                if (logger.isInfoEnabled()) {
+                    logger.info("{} Plugin {}:{}", plugin.getClass(), PluginConfig.PINPOINT_PLUGIN_PACKAGE, pluginPackageList);
+                }
                 
-                logger.info("Loading plugin: {}", plugin.getClass().getName());
+                logger.info("Loading plugin:{} pluginPackage:{}", plugin.getClass().getName(), plugin);
 
-                PluginConfig pluginConfig = new PluginConfig(jar, plugin, agent.getInstrumentation(), agent.getClassPool(), agent.getBootstrapCoreJar());
+                PluginConfig pluginConfig = new PluginConfig(jar, plugin, agent.getInstrumentation(), agent.getClassPool(), agent.getBootstrapCoreJar(), pluginFilterChain);
                 final DefaultProfilerPluginContext context = setupPlugin(pluginConfig);
                 pluginContexts.add(context);
             }
         }
         
-        
+
         return pluginContexts;
     }
 
+    private ClassNameFilter createPluginFilterChain(List<String> packageList) {
+
+        final ClassNameFilter pluginPackageFilter = new PluginPackageFilter(packageList);
+
+        final List<ClassNameFilter> chain = Arrays.asList(profilerPackageFilter, pluginPackageFilter);
+
+        final ClassNameFilter filterChain = new ClassNameFilterChain(chain);
+
+        return filterChain;
+    }
+
+    private JarFile createJarFile(URL pluginJar) {
+        try {
+            final URI uri = pluginJar.toURI();
+            return new JarFile(new File(uri));
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("URISyntax error. " + e.getCause(), e);
+        } catch (IOException e) {
+            throw new RuntimeException("IO error. " + e.getCause(), e);
+        }
+    }
+    private Manifest getManifest(JarFile pluginJarFile) {
+        try {
+            return pluginJarFile.getManifest();
+        } catch (IOException ex) {
+            logger.info("{} IoError :{}", pluginJarFile.getName(), ex.getMessage(), ex);
+            return null;
+        }
+    }
+
+    public List<String> getPluginPackage(JarFile pluginJarFile) {
+
+        final Manifest manifest =  getManifest(pluginJarFile);
+        if (manifest == null) {
+            return PluginConfig.DEFAULT_PINPOINT_PLUGIN_PACKAGE_NAME;
+        }
+
+        final Attributes attributes = manifest.getMainAttributes();
+        final String pluginPackage = attributes.getValue(PluginConfig.PINPOINT_PLUGIN_PACKAGE);
+        if (pluginPackage == null) {
+            return PluginConfig.DEFAULT_PINPOINT_PLUGIN_PACKAGE_NAME;
+        }
+        return StringUtils.splitAndTrim(pluginPackage, ",");
+    }
+
+
     private GuardInstrumentContext preparePlugin(ProfilerPlugin plugin, InstrumentContext context) {
+
         final GuardInstrumentContext guardInstrumentContext = new GuardInstrumentContext(context);
         if (plugin instanceof TransformTemplateAware) {
             if (logger.isDebugEnabled()) {
-                logger.debug("setTransformTemplate {}", plugin.getClass().getName());
+                logger.debug("{}.setTransformTemplate", plugin.getClass().getName());
             }
             final TransformTemplate transformTemplate = new TransformTemplate(guardInstrumentContext);
             ((TransformTemplateAware) plugin).setTransformTemplate(transformTemplate);
@@ -92,6 +159,9 @@ private DefaultProfilerPluginContext setupPlugin(PluginConfig pluginConfig) {
         try {
             // WARN external plugin api
             final ProfilerPlugin plugin = pluginConfig.getPlugin();
+            if (logger.isInfoEnabled()) {
+                logger.info("{} Plugin setup", plugin.getClass().getName());
+            }
             plugin.setup(guardPluginContext);
         } finally {
             guardPluginContext.close();

diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjectorTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjectorTest.java
new file mode 100644
index 000000000000..b7147acbc0ce
--- /dev/null
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/JarProfilerPluginClassInjectorTest.java
@@ -0,0 +1,129 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.instrument;
+
+import com.navercorp.pinpoint.bootstrap.LibClass;
+import com.navercorp.pinpoint.bootstrap.PinpointURLClassLoader;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClassPool;
+import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
+import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
+import com.navercorp.pinpoint.profiler.plugin.PluginPackageFilter;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.Arrays;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class JarProfilerPluginClassInjectorTest {
+
+    public static final String CONTEXT_TYPE_MATCH_CLASS_LOADER = "org.springframework.context.support.ContextTypeMatchClassLoader";
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Test
+    public void testInjectClass() throws Exception {
+        final URL sampleJar = getSampleJar(Logger.class);
+
+        final ClassLoader contextTypeMatchClassLoader = createContextTypeMatchClassLoader(new URL[]{sampleJar});
+
+        InstrumentClassPool pool = Mockito.mock(InstrumentClassPool.class);
+        final Instrumentation instrumentation = Mockito.mock(Instrumentation.class);
+        final ProfilerPlugin profilerPlugin = Mockito.mock(ProfilerPlugin.class);
+
+
+//        final PluginPackageFilter filter = new PluginPackageFilter(Arrays.asList("test"));
+        final String packageName = logger.getClass().getPackage().getName();
+        final PluginPackageFilter filter = new PluginPackageFilter(Arrays.asList(packageName));
+        PluginConfig pluginConfig = new PluginConfig(sampleJar, profilerPlugin, instrumentation, pool, sampleJar.getPath(), filter);
+
+        PlainClassLoaderHandler injector = new PlainClassLoaderHandler(pluginConfig);
+        final Class<?> loggerClass = injector.injectClass(contextTypeMatchClassLoader, logger.getClass().getName());
+
+        logger.debug("ClassLoader{}", loggerClass.getClassLoader());
+        Assert.assertEquals("check className", loggerClass.getName(), "org.slf4j.impl.Log4jLoggerAdapter");
+        Assert.assertEquals("check ClassLoader", loggerClass.getClassLoader().getClass().getName(), CONTEXT_TYPE_MATCH_CLASS_LOADER);
+
+    }
+
+    private ClassLoader createContextTypeMatchClassLoader(URL[] urlArray) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
+        final ClassLoader classLoader = this.getClass().getClassLoader();
+        final Class<ClassLoader> aClass = (Class<ClassLoader>) classLoader.loadClass(CONTEXT_TYPE_MATCH_CLASS_LOADER);
+        final Constructor<ClassLoader> constructor = aClass.getConstructor(ClassLoader.class);
+        ReflectionUtils.makeAccessible(constructor);
+
+        final LibClass libClassFilter = new LibClass() {
+            @Override
+            public boolean onLoadClass(String clazzName) {
+                if (clazzName.startsWith("org.slf4j")) {
+                    logger.debug("Loading {}", clazzName);
+                    return ON_LOAD_CLASS;
+                }
+                return DELEGATE_PARENT;
+            }
+        };
+        PinpointURLClassLoader testClassLoader = new PinpointURLClassLoader(urlArray, ClassLoader.getSystemClassLoader(), libClassFilter);
+
+        final ClassLoader contextTypeMatchClassLoader = constructor.newInstance(testClassLoader);
+
+        logger.debug("cl:{}",contextTypeMatchClassLoader);
+
+//        final Method excludePackage = aClass.getMethod("excludePackage", String.class);
+//        ReflectionUtils.invokeMethod(excludePackage, contextTypeMatchClassLoader, "org.slf4j");
+
+
+        return contextTypeMatchClassLoader;
+    }
+
+
+    private URL getSampleJar(Class clazz) {
+        final CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
+        final URL location = codeSource.getLocation();
+
+        logger.debug("url:{}", location);
+
+        return location;
+    }
+
+}
\ No newline at end of file
diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChainTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChainTest.java
new file mode 100644
index 000000000000..12920ad04752
--- /dev/null
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/ClassNameFilterChainTest.java
@@ -0,0 +1,43 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.plugin;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class ClassNameFilterChainTest {
+
+    @Test
+    public void testAccept() throws Exception {
+        PluginPackageFilter include = new PluginPackageFilter(Arrays.asList("com.include"));
+        PinpointProfilerPackageSkipFilter exclude = new PinpointProfilerPackageSkipFilter(Arrays.asList("com.exclude"));
+
+        ClassNameFilterChain chain = new ClassNameFilterChain(Arrays.asList(include, exclude));
+
+
+        Assert.assertTrue(chain.accept("com.include"));
+        Assert.assertFalse(chain.accept("com.exclude"));
+
+        Assert.assertFalse(chain.accept("unknown"));
+    }
+}
\ No newline at end of file
diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilterTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilterTest.java
new file mode 100644
index 000000000000..3207cb84a9de
--- /dev/null
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PinpointProfilerPackageSkipFilterTest.java
@@ -0,0 +1,37 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.plugin;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class PinpointProfilerPackageSkipFilterTest {
+
+    @Test
+    public void testAccept() throws Exception {
+
+        PinpointProfilerPackageSkipFilter filter = new PinpointProfilerPackageSkipFilter();
+        Assert.assertFalse("skip", filter.accept("com.navercorp.pinpoint.bootstrap.test.class"));
+
+        Assert.assertTrue("include", filter.accept("test"));
+
+    }
+}
\ No newline at end of file
diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PluginPackageFilterTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PluginPackageFilterTest.java
new file mode 100644
index 000000000000..7d5fb6da87aa
--- /dev/null
+++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/PluginPackageFilterTest.java
@@ -0,0 +1,40 @@
+/*
+ * *
+ *  * Copyright 2016 NAVER Corp.
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *     http://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package com.navercorp.pinpoint.profiler.plugin;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Woonduk Kang(emeroad)
+ */
+public class PluginPackageFilterTest {
+
+    @Test
+    public void testAccept() throws Exception {
+        PluginPackageFilter filter = new PluginPackageFilter(Arrays.asList("com.plugin"));
+
+        Assert.assertTrue(filter.accept("com.plugin.test.module"));
+
+        Assert.assertFalse(filter.accept("test"));
+    }
+}
\ No newline at end of file
