diff --git a/agent/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6PluginClassLoaderIT.java b/agent/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6PluginClassLoaderIT.java deleted file mode 100644 index 4e781d275..000000000 --- a/agent/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6PluginClassLoaderIT.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2014 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.lang.reflect.Constructor; -import java.net.URL; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifierHolder; -import com.navercorp.pinpoint.test.plugin.JvmArgument; -import com.navercorp.pinpoint.test.plugin.JvmVersion; -import com.navercorp.pinpoint.test.plugin.OnClassLoader; -import com.navercorp.pinpoint.test.plugin.PinpointPluginTestSuite; - -/** - * @author Jongho Moon - * - */ -@RunWith(PinpointPluginTestSuite.class) -@JvmVersion(6) -@OnClassLoader(child=false) -@JvmArgument("-Dcom.navercorp.pinpoint.plugin.classloader=" + Java6PluginClassLoaderIT.JAVA_6_PLUGIN_CLASSLOADER) -public class Java6PluginClassLoaderIT { - public static final String JAVA_6_PLUGIN_CLASSLOADER = "com.navercorp.pinpoint.profiler.plugin.Java6PluginClassLoader"; - - @Test - public void test() throws Exception { - ClassLoader parent = ClassLoader.getSystemClassLoader(); - ClassLoader child = createChildClassLoader(parent); - - final int run = 10000; - final int threadNum = 16; - Thread[] thread = new Thread[threadNum]; - - for (int i = 0; i < threadNum; i++) { - if (i % 2 == 0) { - thread[i] = new Thread(new Parent(parent, child, run)); - } else { - thread[i] = new Thread(new Child(child, run)); - } - } - - for (int i = 0; i < threadNum; i++) { - thread[i].start(); - } - - System.out.println("Wait workers..."); - - for (int i = 0; i < threadNum; i++) { - thread[i].join(); - } - } - - private ClassLoader createChildClassLoader(ClassLoader parent) throws Exception { - Class type = PluginTestVerifierHolder.getInstance().getClass().getClassLoader().loadClass(JAVA_6_PLUGIN_CLASSLOADER).asSubclass(ClassLoader.class); - Constructor c = type.getConstructor(URL[].class, ClassLoader.class); - return c.newInstance(new URL[0], parent); - } - - - private static class Parent implements Runnable { - private final ClassLoader parent; - private final ClassLoader child; - private final int count; - - public Parent(ClassLoader parent, ClassLoader child, int count) { - this.parent = parent; - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - System.out.println(this + ": " + i); - synchronized(parent) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } - - private static class Child implements Runnable { - private final ClassLoader child; - private final int count; - - public Child(ClassLoader child, int count) { - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - System.out.println(this + ": " + i); - synchronized(child) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } -} diff --git a/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/ProfilerPluginContext.java b/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/ProfilerPluginContext.java index f216add23..9e0d0a9db 100644 --- a/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/ProfilerPluginContext.java +++ b/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/ProfilerPluginContext.java @@ -91,8 +91,6 @@ public interface ProfilerPluginContext { public InterceptorGroup getInterceptorGroup(String name); - public PluginClassLoaderFactory getClassLoaderFactory(); - /** * Add a {@link ApplicationTypeDetector} to Pinpoint agent. * diff --git a/profiler-optional/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoaderTest.java b/profiler-optional/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoaderTest.java deleted file mode 100644 index 4a91e6d55..000000000 --- a/profiler-optional/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoaderTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2014 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.net.URL; - -import org.junit.Test; - -/** - * @author Jongho Moon - * - */ -public class Java7PluginClassLoaderTest { - - @Test - public void test() throws Exception { - ClassLoader parent = ClassLoader.getSystemClassLoader(); - ClassLoader child = new Java7PluginClassLoader(new URL[0], parent); - - final int run = 10000; - final int threadNum = 16; - Thread[] thread = new Thread[threadNum]; - - for (int i = 0; i < threadNum; i++) { - if (i % 2 == 0) { - thread[i] = new Thread(new Parent(parent, child, run)); - } else { - thread[i] = new Thread(new Child(child, run)); - } - } - - for (int i = 0; i < threadNum; i++) { - thread[i].start(); - } - - System.out.println("Wait workers..."); - - for (int i = 0; i < threadNum; i++) { - thread[i].join(); - } - } - - - private static class Parent implements Runnable { - private final ClassLoader parent; - private final ClassLoader child; - private final int count; - - public Parent(ClassLoader parent, ClassLoader child, int count) { - this.parent = parent; - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - synchronized(parent) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } - - private static class Child implements Runnable { - private final ClassLoader child; - private final int count; - - public Child(ClassLoader child, int count) { - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - synchronized(child) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } -} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/ClassLoaderResolver.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/ClassLoaderResolver.java deleted file mode 100644 index d540e8537..000000000 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/ClassLoaderResolver.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2014 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; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; -import java.security.ProtectionDomain; - -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtMethod; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.navercorp.pinpoint.exception.PinpointException; - -/** - * @author Jongho Moon - * - */ -public class ClassLoaderResolver { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - public Class resolve(Instrumentation inst) { - String pluginClassLoaderName = null; - - String specified = System.getProperty("com.navercorp.pinpoint.plugin.classloader"); - - if (specified == null) { - pluginClassLoaderName = resolveClassLoaderType(); - } else { - pluginClassLoaderName = specified; - } - - logger.info("Use plugin class loader: " + pluginClassLoaderName); - - boolean redefineClassLoader = "com.navercorp.pinpoint.profiler.plugin.Java6PluginClassLoader".equals(pluginClassLoaderName); - - try { - if (redefineClassLoader) { - retransformClassLoader(inst); - } - - return Class.forName(pluginClassLoaderName).asSubclass(ClassLoader.class); - } catch (ClassNotFoundException e) { - throw new NoClassDefFoundError("Cannot find plugin class loader: " + pluginClassLoaderName); - } catch (Exception e) { - throw new PinpointException("Failed to prepare " + pluginClassLoaderName, e); - } - } - - private String resolveClassLoaderType() { - String pluginClassLoaderName; - boolean java6 = isJava6(); - boolean unsafe = hasUnsafe(); - - if (java6) { - if (unsafe) { - pluginClassLoaderName = "com.navercorp.pinpoint.profiler.plugin.Java6UnsafePluginClassLoader"; - } else { - pluginClassLoaderName = "com.navercorp.pinpoint.profiler.plugin.Java6PluginClassLoader"; - } - } else { - pluginClassLoaderName = "com.navercorp.pinpoint.profiler.plugin.Java7PluginClassLoader"; - } - return pluginClassLoaderName; - } - - private void retransformClassLoader(Instrumentation inst) throws UnmodifiableClassException { - ClassLoaderTransformer transformer = new ClassLoaderTransformer(); - inst.addTransformer(transformer, true); - inst.retransformClasses(ClassLoader.class); - inst.removeTransformer(transformer); - } - - private boolean hasUnsafe() { - try { - Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe").setAccessible(true); - } catch (Exception e) { - return false; - } - - return true; - } - - private boolean isJava6() { - try { - ClassLoader.class.getDeclaredMethod("registerAsParallelCapable"); - } catch (Exception e) { - return true; - } - - return false; - } - - private static class ClassLoaderTransformer implements ClassFileTransformer { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - logger.info("in transform: " + className); - - if (className.equals("java/lang/ClassLoader")) { - logger.info("Transform ClassLoader"); - - try { - ClassPool classPool = new ClassPool(true); - CtClass c = classPool.get("java.lang.ClassLoader"); - CtMethod m = c.getMethod("loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;"); - - m.insertBefore("com.navercorp.pinpoint.bootstrap.ClassLoaderLock.lock(this);"); - m.insertAfter("com.navercorp.pinpoint.bootstrap.ClassLoaderLock.unlock(this);", true); - - logger.info("Transform ClassLoader Finished"); - - return c.toBytecode(); - } catch (Exception e) { - return null; - } - } - - return null; - } - - } -} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java index e9e8dd4dc..91043a312 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java @@ -18,8 +18,6 @@ package com.navercorp.pinpoint.profiler; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; -import java.net.URL; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -38,9 +36,7 @@ import com.navercorp.pinpoint.bootstrap.instrument.ByteCodeInstrumentor; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerBinder; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; -import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; import com.navercorp.pinpoint.bootstrap.sampler.Sampler; -import com.navercorp.pinpoint.common.plugin.PluginLoader; import com.navercorp.pinpoint.common.service.ServiceTypeRegistryService; import com.navercorp.pinpoint.common.trace.ServiceType; import com.navercorp.pinpoint.profiler.context.DefaultServerMetaDataHolder; @@ -53,8 +49,8 @@ import com.navercorp.pinpoint.profiler.interceptor.InterceptorRegistryBinder; import com.navercorp.pinpoint.profiler.interceptor.bci.JavaAssistByteCodeInstrumentor; import com.navercorp.pinpoint.profiler.logging.Slf4jLoggerBinder; import com.navercorp.pinpoint.profiler.monitor.AgentStatMonitor; -import com.navercorp.pinpoint.profiler.plugin.DefaultPluginClassLoaderFactory; import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; +import com.navercorp.pinpoint.profiler.plugin.ProfilerPluginLoader; import com.navercorp.pinpoint.profiler.receiver.CommandDispatcher; import com.navercorp.pinpoint.profiler.receiver.service.EchoService; import com.navercorp.pinpoint.profiler.receiver.service.ThreadDumpService; @@ -106,7 +102,6 @@ public class DefaultAgent implements Agent { private final InterceptorRegistryBinder interceptorRegistryBinder; private final ServiceTypeRegistryService serviceTypeRegistryService; - private final DefaultPluginClassLoaderFactory pluginClassLoaderFactory; private final List pluginContexts; @@ -160,9 +155,7 @@ public class DefaultAgent implements Agent { logger.info("DefaultAgent classLoader:{}", this.getClass().getClassLoader()); } - Class classLoaderType = new ClassLoaderResolver().resolve(instrumentation); - pluginClassLoaderFactory = new DefaultPluginClassLoaderFactory(classLoaderType, agentOption.getPluginJars()); - pluginContexts = loadProfilerPlugins(agentOption.getPluginJars()); + pluginContexts = loadPlugins(agentOption); this.classFileTransformer = new ClassFileTransformerDispatcher(this, byteCodeInstrumentor, pluginContexts); retransformService.setRetransformEventListener(classFileTransformer); @@ -199,6 +192,10 @@ public class DefaultAgent implements Agent { this.agentStatMonitor = new AgentStatMonitor(this.statDataSender, this.agentInformation.getAgentId(), this.agentInformation.getStartTime()); } + protected List loadPlugins(AgentOption agentOption) { + return new ProfilerPluginLoader(this).load(agentOption.getPluginJars()); + } + private CommandDispatcher createCommandDispatcher() { CommandDispatcher commandDispatcher = new CommandDispatcher(); @@ -207,22 +204,6 @@ public class DefaultAgent implements Agent { return commandDispatcher; } - private List loadProfilerPlugins(URL[] pluginJars) { - List plugins = PluginLoader.load(ProfilerPlugin.class, pluginJars); - List pluginContexts = new ArrayList(plugins.size()); - - for (ProfilerPlugin plugin : plugins) { - logger.info("Loading plugin: {}", plugin.getClass().getName()); - - DefaultProfilerPluginContext context = new DefaultProfilerPluginContext(this); - plugin.setup(context); - context.markInitialized(); - pluginContexts.add(context); - } - - return pluginContexts; - } - public ByteCodeInstrumentor getByteCodeInstrumentor() { return byteCodeInstrumentor; } @@ -383,10 +364,6 @@ public class DefaultAgent implements Agent { return serviceTypeRegistryService; } - public DefaultPluginClassLoaderFactory getPluginClassLoaderFactory() { - return pluginClassLoaderFactory; - } - @Override public void start() { synchronized (this) { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultPluginClassLoaderFactory.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultPluginClassLoaderFactory.java deleted file mode 100644 index 626ded437..000000000 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultPluginClassLoaderFactory.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014 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.io.Closeable; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.navercorp.pinpoint.bootstrap.plugin.PluginClassLoaderFactory; -import com.navercorp.pinpoint.exception.PinpointException; - -public class DefaultPluginClassLoaderFactory implements PluginClassLoaderFactory { - private static final Logger logger = LoggerFactory.getLogger(DefaultPluginClassLoaderFactory.class); - private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager(); - - private final Constructor classLoaderConstructor; - private final URL[] pluginJars; - private final ConcurrentHashMap cache = new ConcurrentHashMap(); - private final AtomicReference forBootstrapClassLoader = new AtomicReference(); - - - - public DefaultPluginClassLoaderFactory(Class classLoaderType, URL[] pluginJars) { - try { - this.classLoaderConstructor = classLoaderType.getDeclaredConstructor(URL[].class, ClassLoader.class); - } catch (Exception e) { - throw new PinpointException("Cannot find constructor with parameter (URL[], ClassLoader): " + classLoaderType); - } - - this.pluginJars = pluginJars; - } - - @Override - public ClassLoader get(ClassLoader loader) { - if (loader == null) { - // boot class loader - return getForBootstrap(); - } else { - return getForPlain(loader); - } - } - - private ClassLoader getForPlain(ClassLoader loader) { - final ClassLoader forPlugin = cache.get(loader); - if (forPlugin != null) { - return forPlugin; - } - - final ClassLoader newInstance = createPluginClassLoader(pluginJars, loader); - final ClassLoader before = cache.putIfAbsent(loader, newInstance); - if (before == null) { - return newInstance; - } else { - close (newInstance); - return before; - } - } - - private ClassLoader getForBootstrap() { - final ClassLoader forPlugin = forBootstrapClassLoader.get(); - - if (forPlugin != null) { - return forPlugin; - } - - // Strictly, should pass null as parent class loader. - // But if so, All the types used by interceptors have to be loaded by bootstrap class loader. - // So we use system class loader as parent. - final ClassLoader newInstance = createPluginClassLoader(pluginJars, ClassLoader.getSystemClassLoader()); - boolean success = forBootstrapClassLoader.compareAndSet(null, newInstance); - - if (success) { - return newInstance; - } else { - close (newInstance); - return forBootstrapClassLoader.get(); - } - } - - private ClassLoader createPluginClassLoader(final URL[] urls, final ClassLoader parent) { - if (SECURITY_MANAGER != null) { - return AccessController.doPrivileged(new PrivilegedAction() { - public ClassLoader run() { - return createPluginClassLoader0(urls, parent); - } - }); - } else { - return createPluginClassLoader0(urls, parent); - } - } - - private ClassLoader createPluginClassLoader0(URL[] urls, ClassLoader parent) { - try { - return (ClassLoader)classLoaderConstructor.newInstance(urls, parent); - } catch (Exception e) { - throw new PinpointException("Failed to create plugin classloader instance", e); - } - } - - private void close(ClassLoader classLoader) { - if (classLoader == null) { - return; - } - // after jdk 1.7 - if (classLoader instanceof Closeable) { - try { - ((Closeable)classLoader).close(); - } catch (IOException e) { - logger.warn("ClassLoader.close() fail. Cause: " + e.getMessage(), e); - } - } - } - -} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultProfilerPluginContext.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultProfilerPluginContext.java index 8743938c3..9d8c11060 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultProfilerPluginContext.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/DefaultProfilerPluginContext.java @@ -29,7 +29,6 @@ import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.instrument.ByteCodeInstrumentor; import com.navercorp.pinpoint.bootstrap.interceptor.group.InterceptorGroup; import com.navercorp.pinpoint.bootstrap.plugin.ApplicationTypeDetector; -import com.navercorp.pinpoint.bootstrap.plugin.PluginClassLoaderFactory; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginContext; import com.navercorp.pinpoint.bootstrap.plugin.transformer.ClassFileTransformerBuilder; import com.navercorp.pinpoint.profiler.DefaultAgent; @@ -38,6 +37,7 @@ import com.navercorp.pinpoint.profiler.util.NameValueList; public class DefaultProfilerPluginContext implements ProfilerPluginContext { private final DefaultAgent agent; + private final ProfilerPluginClassLoader classInjector; private final List serverTypeDetectors = new ArrayList(); private final List classTransformers = new ArrayList(); @@ -53,8 +53,9 @@ public class DefaultProfilerPluginContext implements ProfilerPluginContext { private boolean initialized = false; - public DefaultProfilerPluginContext(DefaultAgent agent) { + public DefaultProfilerPluginContext(DefaultAgent agent, ProfilerPluginClassLoader classInjector) { this.agent = agent; + this.classInjector = classInjector; } @Override @@ -152,10 +153,9 @@ public class DefaultProfilerPluginContext implements ProfilerPluginContext { serverTypeDetectors.add(detector); } } - - @Override - public PluginClassLoaderFactory getClassLoaderFactory() { - return agent.getPluginClassLoaderFactory(); + + public ProfilerPluginClassLoader getClassInjector() { + return classInjector; } public List getClassEditors() { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/JarProfilerPluginClassLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/JarProfilerPluginClassLoader.java new file mode 100644 index 000000000..1842c5fe9 --- /dev/null +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/JarProfilerPluginClassLoader.java @@ -0,0 +1,171 @@ +/** + * Copyright 2014 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.io.File; +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.Collection; +import java.util.jar.JarFile; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.LoaderClassPath; +import javassist.NotFoundException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.navercorp.pinpoint.exception.PinpointException; + +/** + * @author Jongho Moon + * + */ +public class JarProfilerPluginClassLoader implements ProfilerPluginClassLoader { + private static final Logger logger = LoggerFactory.getLogger(JarProfilerPluginClassLoader.class); + + 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); + } + } + + public static JarProfilerPluginClassLoader of(URL pluginJar) { + try { + JarFile jarFile = new JarFile(new File(pluginJar.toURI())); + return new JarProfilerPluginClassLoader(pluginJar, jarFile); + } catch (Exception e) { + logger.warn("Failed to get JarFile {}", pluginJar, e); + return null; + } + } + + private final URL pluginJarURL; + private final JarFile pluginJar; + + + private JarProfilerPluginClassLoader(URL pluginJarURL, JarFile pluginJar) { + this.pluginJarURL = pluginJarURL; + this.pluginJar = pluginJar; + } + + @Override + @SuppressWarnings("unchecked") + public Class loadClass(ClassLoader classLoader, String className) { + ClassLoader targetClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader; + + try { + if (targetClassLoader instanceof URLClassLoader) { + return (Class)loadFromURLClassLoader((URLClassLoader)targetClassLoader, className); + } else { + return (Class)loadFromOtherClassLoader(targetClassLoader, className); + } + } catch (Exception e) { + logger.warn("Failed to load plugin class {} with classLoader {}", className, targetClassLoader, e); + throw new PinpointException("Failed to load plugin class " + className + " with classLoader " + targetClassLoader, e); + } + } + + private Class loadFromURLClassLoader(URLClassLoader classLoader, String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + URL[] urls = classLoader.getURLs(); + + boolean hasPluginJar = false; + for (URL url : urls) { + if (url.equals(pluginJarURL)) { + hasPluginJar = true; + break; + } + } + + if (!hasPluginJar) { + ADD_URL.invoke(classLoader, pluginJarURL); + } + + return classLoader.loadClass(className); + } + + private Class loadFromOtherClassLoader(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, IOException, CannotCompileException, IllegalAccessException, InvocationTargetException { + ClassPool pool = new ClassPool(); + + pool.appendClassPath(new LoaderClassPath(classLoader)); + pool.appendClassPath(pluginJar.getName()); + + return loadFromOtherClassLoader(pool, classLoader, className); + } + + private Class loadFromOtherClassLoader(ClassPool pool, ClassLoader classLoader, String className) throws NotFoundException, IOException, CannotCompileException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Class c = null; + + try { + c = classLoader.loadClass(className); + } catch (ClassNotFoundException e) { + + } + + if (c != null) { + return c; + } + + CtClass ct = pool.get(className); + + if (ct == null) { + throw new NotFoundException(className); + } + + + CtClass superClass = ct.getSuperclass(); + + if (superClass != null) { + loadFromOtherClassLoader(pool, classLoader, superClass.getName()); + } + + CtClass[] interfaces = ct.getInterfaces(); + + for (CtClass i : interfaces) { + loadFromOtherClassLoader(pool, classLoader, i.getName()); + } + + Collection refs = ct.getRefClasses(); + + for (String ref : refs) { + try { + loadFromOtherClassLoader(pool, classLoader, ref); + } catch (NotFoundException e) { + logger.warn("Skip a referenced class because of NotFoundException : ", e); + } + } + + 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/plugin/Java6PluginClassLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java6PluginClassLoader.java deleted file mode 100644 index 0a3710bd4..000000000 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java6PluginClassLoader.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014 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.net.URL; -import java.net.URLClassLoader; - -import com.navercorp.pinpoint.bootstrap.ClassLoaderLock; - -/** - * @author Jongho Moon - * @author emeroad - */ -public class Java6PluginClassLoader extends URLClassLoader { - - public Java6PluginClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - ClassLoaderLock.register(parent, this); - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - ClassLoaderLock.lock(this); - - try { - Class c = findLoadedClass(name); - if (c == null) { - try { - c = getParent().loadClass(name); - } catch (ClassNotFoundException e) { - } - - if (c == null) { - c = findClass(name); - } - } - if (resolve) { - resolveClass(c); - } - return c; - } finally { - ClassLoaderLock.unlock(this); - } - } -} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoader.java deleted file mode 100644 index a20762e2d..000000000 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoader.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2014 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.lang.reflect.Field; -import java.net.URL; -import java.net.URLClassLoader; - -import sun.misc.Unsafe; - -/** - * @author Jongho Moon - * @author emeroad - */ -@SuppressWarnings("restriction") -public class Java6UnsafePluginClassLoader extends URLClassLoader { - - private static final Unsafe unsafe; - - static { - try { - Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - unsafe = (Unsafe)f.get(null); - - // Ensure required classes are loaded - while (!unsafe.tryMonitorEnter(Java6UnsafePluginClassLoader.class)) { } - Java6UnsafePluginClassLoader.class.wait(0, 1); - unsafe.monitorExit(Java6UnsafePluginClassLoader.class); - } catch (Exception e) { - throw new RuntimeException("Failed to get unsafe", e); - } - } - - private final ClassLoader parent; - - public Java6UnsafePluginClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - this.parent = parent; - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - Class c = null; - - synchronized (this) { - c = findLoadedClass(name); - - if (c == null) { - try { - while (!unsafe.tryMonitorEnter(parent)) { - try { - this.wait(0, 1); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted", e); - } - } - - try { - c = parent.loadClass(name); - } finally { - unsafe.monitorExit(parent); - } - - } catch (ClassNotFoundException e) { - - } - - if (c == null) { - c = findClass(name); - } - } - - if (resolve) { - resolveClass(c); - } - - this.notify(); - } - - return c; - } -} diff --git a/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/PluginClassLoaderFactory.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginClassLoader.java similarity index 77% rename from bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/PluginClassLoaderFactory.java rename to profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginClassLoader.java index 9ea61c063..bc2a94f74 100644 --- a/bootstrap/src/main/java/com/navercorp/pinpoint/bootstrap/plugin/PluginClassLoaderFactory.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginClassLoader.java @@ -12,14 +12,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.navercorp.pinpoint.bootstrap.plugin; +package com.navercorp.pinpoint.profiler.plugin; /** * @author Jongho Moon * */ -public interface PluginClassLoaderFactory { +public interface ProfilerPluginClassLoader { - public ClassLoader get(ClassLoader loader); + public Class loadClass(ClassLoader targetClassLoader, String className); } \ 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 new file mode 100644 index 000000000..659b3972f --- /dev/null +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/ProfilerPluginLoader.java @@ -0,0 +1,61 @@ +/** + * Copyright 2014 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.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; +import com.navercorp.pinpoint.common.plugin.PluginLoader; +import com.navercorp.pinpoint.profiler.DefaultAgent; + +/** + * @author Jongho Moon + * + */ +public class ProfilerPluginLoader { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final DefaultAgent agent; + + public ProfilerPluginLoader(DefaultAgent agent) { + this.agent = agent; + } + + public List load(URL[] pluginJars) { + List pluginContexts = new ArrayList(pluginJars.length); + + for (URL jar : pluginJars) { + List plugins = PluginLoader.load(ProfilerPlugin.class, new URL[] { jar }); + + for (ProfilerPlugin plugin : plugins) { + logger.info("Loading plugin: {}", plugin.getClass().getName()); + + ProfilerPluginClassLoader classInjector = JarProfilerPluginClassLoader.of(jar); + DefaultProfilerPluginContext context = new DefaultProfilerPluginContext(agent, classInjector); + plugin.setup(context); + context.markInitialized(); + pluginContexts.add(context); + } + } + + + return pluginContexts; + } + +} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/TypeUtils.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/TypeUtils.java index f11b07142..f557cbbfe 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/TypeUtils.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/TypeUtils.java @@ -60,23 +60,7 @@ public final class TypeUtils { return result; } - - @SuppressWarnings("unchecked") - public static Class loadClass(ClassLoader loader, String className) { - Thread currentThread = Thread.currentThread(); - ClassLoader backup = currentThread.getContextClassLoader(); - currentThread.setContextClassLoader(loader); - - try { - return (Class)loader.loadClass(className); - } catch (ClassNotFoundException e) { - throw new PinpointException("Cannot load class: " + className, e); - } finally { - currentThread.setContextClassLoader(backup); - } - } - - + public static T findAnnotation(Annotation[] annotations, Class type) { for (Annotation a : annotations) { if (a.annotationType() == type) { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/AnnotatedInterceptorInjector.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/AnnotatedInterceptorInjector.java index 4af6befdb..5d63e9e6d 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/AnnotatedInterceptorInjector.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/AnnotatedInterceptorInjector.java @@ -54,7 +54,7 @@ public class AnnotatedInterceptorInjector implements MethodRecipe { } int inject(ClassLoader targetClassLoader, InstrumentClass targetClass, MethodInfo targetMethod) throws Exception { - Class interceptorType = TypeUtils.loadClass(targetClassLoader, interceptorClassName); + Class interceptorType = pluginContext.getClassInjector().loadClass(targetClassLoader, interceptorClassName); InterceptorFactory factory = createInterceptorFactory(interceptorType); Interceptor interceptor = factory.getInterceptor(targetClassLoader, targetClass, targetMethod); diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/TargetAnnotatedInterceptorInjector.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/TargetAnnotatedInterceptorInjector.java index 4bf1af35c..868cd5a93 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/TargetAnnotatedInterceptorInjector.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/interceptor/TargetAnnotatedInterceptorInjector.java @@ -29,7 +29,6 @@ import com.navercorp.pinpoint.bootstrap.plugin.annotation.TargetMethod; import com.navercorp.pinpoint.bootstrap.plugin.annotation.Targets; import com.navercorp.pinpoint.exception.PinpointException; import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; -import com.navercorp.pinpoint.profiler.plugin.TypeUtils; import com.navercorp.pinpoint.profiler.plugin.objectfactory.AutoBindingObjectFactory; import com.navercorp.pinpoint.profiler.plugin.transformer.ClassCookBook; import com.navercorp.pinpoint.profiler.plugin.transformer.ClassRecipe; @@ -46,16 +45,16 @@ import com.navercorp.pinpoint.profiler.plugin.transformer.MethodTransformer; public class TargetAnnotatedInterceptorInjector implements ClassRecipe { private final DefaultProfilerPluginContext pluginContext; - private final String interceptorName; + private final String interceptorClassName; private final Object[] providedArguments; private final String groupName; private final ExecutionPolicy executionPoint; - public TargetAnnotatedInterceptorInjector(DefaultProfilerPluginContext pluginContext, String interceptorName, Object[] providedArguments, String groupName, ExecutionPolicy executionPoint) { + public TargetAnnotatedInterceptorInjector(DefaultProfilerPluginContext pluginContext, String interceptorClassName, Object[] providedArguments, String groupName, ExecutionPolicy executionPoint) { this.pluginContext = pluginContext; - this.interceptorName = interceptorName; + this.interceptorClassName = interceptorClassName; this.providedArguments = providedArguments; this.groupName = groupName; this.executionPoint = executionPoint; @@ -63,9 +62,9 @@ public class TargetAnnotatedInterceptorInjector implements ClassRecipe { @Override public void edit(ClassLoader classLoader, InstrumentClass target) throws Throwable { - Class interceptorType = TypeUtils.loadClass(classLoader, interceptorName); + Class interceptorType = pluginContext.getClassInjector().loadClass(classLoader, interceptorClassName); - AnnotatedInterceptorInjector injector = new AnnotatedInterceptorInjector(pluginContext, interceptorName, providedArguments, groupName, executionPoint); + AnnotatedInterceptorInjector injector = new AnnotatedInterceptorInjector(pluginContext, interceptorClassName, providedArguments, groupName, executionPoint); ClassRecipe recipe = createMethodEditor(classLoader, interceptorType, target, injector); recipe.edit(classLoader, target); @@ -106,7 +105,7 @@ public class TargetAnnotatedInterceptorInjector implements ClassRecipe { } if (editors.isEmpty()) { - throw new PinpointException("No target is specified. At least one of @Targets, @TargetMethod, @TargetConstructor, @TargetFilter must present. interceptor: " + interceptorName); + throw new PinpointException("No target is specified. At least one of @Targets, @TargetMethod, @TargetConstructor, @TargetFilter must present. interceptor: " + interceptorClassName); } return editors.size() == 1 ? editors.get(0) : new ClassCookBook(editors); @@ -116,7 +115,7 @@ public class TargetAnnotatedInterceptorInjector implements ClassRecipe { String methodName = annotation.name(); if (methodName == null) { - throw new PinpointException("name() of @TargetMethod cannot be null: " + interceptorName); + throw new PinpointException("name() of @TargetMethod cannot be null: " + interceptorClassName); } String[] parameterTypeNames = annotation.paramTypes(); @@ -133,7 +132,7 @@ public class TargetAnnotatedInterceptorInjector implements ClassRecipe { String type = annotation.type(); if (type == null) { - throw new PinpointException("type of @TargetFilter is null: " + interceptorName); + throw new PinpointException("type of @TargetFilter is null: " + interceptorClassName); } AutoBindingObjectFactory filterFactory = new AutoBindingObjectFactory(pluginContext, targetClass, classLoader); @@ -147,7 +146,7 @@ public class TargetAnnotatedInterceptorInjector implements ClassRecipe { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TargetAnnotatedInterceptorInjector [interceptorClass="); - builder.append(interceptorName); + builder.append(interceptorClassName); if (providedArguments != null) { builder.append(", constructorArguments="); diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/objectfactory/AutoBindingObjectFactory.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/objectfactory/AutoBindingObjectFactory.java index 72f00631d..d86cb18bf 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/objectfactory/AutoBindingObjectFactory.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/objectfactory/AutoBindingObjectFactory.java @@ -26,29 +26,30 @@ import com.navercorp.pinpoint.bootstrap.interceptor.group.InterceptorGroup; import com.navercorp.pinpoint.bootstrap.plugin.ObjectRecipe; import com.navercorp.pinpoint.bootstrap.plugin.ObjectRecipe.ByConstructor; import com.navercorp.pinpoint.bootstrap.plugin.ObjectRecipe.ByStaticFactoryMethod; -import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginContext; import com.navercorp.pinpoint.exception.PinpointException; -import com.navercorp.pinpoint.profiler.plugin.TypeUtils; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; /** * @author Jongho Moon * */ public class AutoBindingObjectFactory { + private final DefaultProfilerPluginContext pluginContext; private final ClassLoader classLoader; private final PinpointTypeArgumentProvider pinpointResolver; - public AutoBindingObjectFactory(ProfilerPluginContext pluginContext, InstrumentClass targetClass, ClassLoader classLoader) { + public AutoBindingObjectFactory(DefaultProfilerPluginContext pluginContext, InstrumentClass targetClass, ClassLoader classLoader) { this(pluginContext, null, targetClass, null, classLoader); } - public AutoBindingObjectFactory(ProfilerPluginContext pluginContext, InterceptorGroup interceptorGroup, InstrumentClass targetClass, MethodInfo targetMethod, ClassLoader classLoader) { + public AutoBindingObjectFactory(DefaultProfilerPluginContext pluginContext, InterceptorGroup interceptorGroup, InstrumentClass targetClass, MethodInfo targetMethod, ClassLoader classLoader) { + this.pluginContext = pluginContext; this.classLoader = classLoader; this.pinpointResolver = new PinpointTypeArgumentProvider(pluginContext, interceptorGroup, targetClass, targetMethod); } public Object createInstance(ObjectRecipe recipe) { - Class type = TypeUtils.loadClass(classLoader, recipe.getClassName()); + Class type = pluginContext.getClassInjector().loadClass(classLoader, recipe.getClassName()); ArgumentsResolver argumentsResolver = getParameterResolvers(classLoader, recipe.getArguments()); if (recipe instanceof ByConstructor) { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DedicatedClassFileTransformer.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DedicatedClassFileTransformer.java index f1a05487f..bb866c4db 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DedicatedClassFileTransformer.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DedicatedClassFileTransformer.java @@ -23,45 +23,34 @@ import com.navercorp.pinpoint.bootstrap.instrument.ByteCodeInstrumentor; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; import com.navercorp.pinpoint.bootstrap.instrument.matcher.Matcher; import com.navercorp.pinpoint.bootstrap.instrument.matcher.Matchers; -import com.navercorp.pinpoint.bootstrap.plugin.PluginClassLoaderFactory; import com.navercorp.pinpoint.bootstrap.plugin.transformer.PinpointClassFileTransformer; import com.navercorp.pinpoint.exception.PinpointException; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; public class DedicatedClassFileTransformer implements PinpointClassFileTransformer { private final ByteCodeInstrumentor instrumentor; - private final PluginClassLoaderFactory classLoaderFactory; private final String targetClassName; private final ClassRecipe recipe; - public DedicatedClassFileTransformer(ByteCodeInstrumentor instrumentor, PluginClassLoaderFactory classLoaderFactory, String targetClassName, ClassRecipe recipe) { + public DedicatedClassFileTransformer(ByteCodeInstrumentor instrumentor, String targetClassName, ClassRecipe recipe) { this.instrumentor = instrumentor; - this.classLoaderFactory = classLoaderFactory; - this.targetClassName = targetClassName; this.recipe = recipe; } @Override public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - ClassLoader forPlugin = classLoaderFactory.get(classLoader); - - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(forPlugin); - try { InstrumentClass target = instrumentor.getClass(classLoader, className, classfileBuffer); - recipe.edit(forPlugin, target); + recipe.edit(classLoader, target); return target.toBytecode(); } catch (PinpointException e) { throw e; } catch (Throwable e) { String msg = "Fail to invoke plugin class recipe: " + toString(); throw new PinpointException(msg, e); - } finally { - Thread.currentThread().setContextClassLoader(old); } } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DefaultClassFileTransformerBuilder.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DefaultClassFileTransformerBuilder.java index f0f38aa71..0f32160ce 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DefaultClassFileTransformerBuilder.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/plugin/transformer/DefaultClassFileTransformerBuilder.java @@ -123,7 +123,7 @@ public class DefaultClassFileTransformerBuilder implements ClassFileTransformerB @Override public PinpointClassFileTransformer build() { ClassRecipe recipe = buildClassRecipe(); - return new DedicatedClassFileTransformer(pluginContext.getByteCodeInstrumentor(), pluginContext.getClassLoaderFactory(), targetClassName, recipe); + return new DedicatedClassFileTransformer(pluginContext.getByteCodeInstrumentor(), targetClassName, recipe); } private ClassRecipe buildClassRecipe() { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/test/MockAgent.java b/profiler/src/main/java/com/navercorp/pinpoint/test/MockAgent.java index 8bcbc883e..a6d0ea765 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/test/MockAgent.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/test/MockAgent.java @@ -19,25 +19,30 @@ package com.navercorp.pinpoint.test; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.thrift.TBase; + import com.navercorp.pinpoint.bootstrap.AgentOption; import com.navercorp.pinpoint.bootstrap.DefaultAgentOption; +import com.navercorp.pinpoint.bootstrap.config.ProfilerConfig; +import com.navercorp.pinpoint.bootstrap.context.ServerMetaDataHolder; +import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; +import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier.ExpectedAnnotation; +import com.navercorp.pinpoint.common.plugin.PluginLoader; import com.navercorp.pinpoint.common.service.DefaultAnnotationKeyRegistryService; import com.navercorp.pinpoint.common.service.DefaultServiceTypeRegistryService; import com.navercorp.pinpoint.common.trace.ServiceType; - -import org.apache.thrift.TBase; - -import com.navercorp.pinpoint.bootstrap.config.ProfilerConfig; -import com.navercorp.pinpoint.bootstrap.context.ServerMetaDataHolder; -import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier.ExpectedAnnotation; +import com.navercorp.pinpoint.exception.PinpointException; import com.navercorp.pinpoint.profiler.DefaultAgent; import com.navercorp.pinpoint.profiler.context.Span; import com.navercorp.pinpoint.profiler.context.SpanEvent; import com.navercorp.pinpoint.profiler.context.storage.StorageFactory; import com.navercorp.pinpoint.profiler.interceptor.GlobalInterceptorRegistryBinder; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; +import com.navercorp.pinpoint.profiler.plugin.ProfilerPluginClassLoader; import com.navercorp.pinpoint.profiler.receiver.CommandDispatcher; import com.navercorp.pinpoint.profiler.sender.DataSender; import com.navercorp.pinpoint.profiler.sender.EnhancedDataSender; @@ -108,6 +113,25 @@ public class MockAgent extends DefaultAgent { return new ResettableServerMetaDataHolder(vmArgs); } + @Override + protected List loadPlugins(AgentOption agentOption) { + List pluginContexts = new ArrayList(); + ProfilerPluginClassLoader classInjector = new TestProfilerPluginClassLoader(); + + List plugins = PluginLoader.load(ProfilerPlugin.class, ClassLoader.getSystemClassLoader()); + + for (ProfilerPlugin plugin : plugins) { + DefaultProfilerPluginContext context = new DefaultProfilerPluginContext(this, classInjector); + plugin.setup(context); + context.markInitialized(); + pluginContexts.add(context); + } + + + return pluginContexts; + + } + public static String toString(Span span) { StringBuilder builder = new StringBuilder(); builder.append('('); diff --git a/profiler-optional/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java similarity index 50% rename from profiler-optional/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoader.java rename to profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java index 2e4fea3a2..0d63d3e74 100644 --- a/profiler-optional/src/main/java/com/navercorp/pinpoint/profiler/plugin/Java7PluginClassLoader.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java @@ -1,40 +1,35 @@ -/* +/** * Copyright 2014 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.test; -package com.navercorp.pinpoint.profiler.plugin; - -import java.net.URL; -import java.net.URLClassLoader; +import com.navercorp.pinpoint.exception.PinpointException; +import com.navercorp.pinpoint.profiler.plugin.ProfilerPluginClassLoader; /** * @author Jongho Moon - * @author emeroad + * */ -public class Java7PluginClassLoader extends URLClassLoader { - static { - ClassLoader.registerAsParallelCapable(); - } - - public Java7PluginClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - } +public class TestProfilerPluginClassLoader implements ProfilerPluginClassLoader { + @SuppressWarnings("unchecked") @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - // for debugging - return super.loadClass(name, resolve); + public Class loadClass(ClassLoader targetClassLoader, String className) { + try { + return (Class) targetClassLoader.loadClass(className); + } catch (ClassNotFoundException e) { + throw new PinpointException("Cannot find class: " + className, e); + } } } diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultClassEditorBuilderTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultClassEditorBuilderTest.java index 398451498..f84dd31a3 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultClassEditorBuilderTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultClassEditorBuilderTest.java @@ -20,8 +20,6 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.lang.instrument.ClassFileTransformer; -import java.net.URL; -import java.net.URLClassLoader; import org.junit.Test; @@ -36,6 +34,7 @@ import com.navercorp.pinpoint.bootstrap.interceptor.MethodDescriptor; import com.navercorp.pinpoint.bootstrap.plugin.transformer.MethodTransformerBuilder; import com.navercorp.pinpoint.profiler.DefaultAgent; import com.navercorp.pinpoint.profiler.plugin.transformer.DefaultClassFileTransformerBuilder; +import com.navercorp.pinpoint.test.TestProfilerPluginClassLoader; public class DefaultClassEditorBuilderTest { public static final String SCOPE_NAME = "test"; @@ -47,11 +46,9 @@ public class DefaultClassEditorBuilderTest { InstrumentClass aClass = mock(InstrumentClass.class); MethodInfo aMethod = mock(MethodInfo.class); MethodDescriptor aDescriptor = mock(MethodDescriptor.class); - DefaultPluginClassLoaderFactory classLoaderFactory = mock(DefaultPluginClassLoaderFactory.class); DefaultAgent agent = mock(DefaultAgent.class); ClassLoader classLoader = getClass().getClassLoader(); - ClassLoader childClassLoader = new URLClassLoader(new URL[0], classLoader); String className = "someClass"; String methodName = "someMethod"; byte[] classFileBuffer = new byte[0]; @@ -60,8 +57,6 @@ public class DefaultClassEditorBuilderTest { when(agent.getByteCodeInstrumentor()).thenReturn(instrumentor); when(agent.getTraceContext()).thenReturn(traceContext); - when(agent.getPluginClassLoaderFactory()).thenReturn(classLoaderFactory); - when(classLoaderFactory.get(classLoader)).thenReturn(childClassLoader); when(instrumentor.getClass(classLoader, className, classFileBuffer)).thenReturn(aClass); when(aClass.getDeclaredMethod(methodName, parameterTypeNames)).thenReturn(aMethod); when(aMethod.getName()).thenReturn(methodName); @@ -69,7 +64,8 @@ public class DefaultClassEditorBuilderTest { when(aMethod.getDescriptor()).thenReturn(aDescriptor); when(aClass.addInterceptor(eq(methodName), eq(parameterTypeNames), isA(Interceptor.class))).thenReturn(0); - DefaultProfilerPluginContext context = new DefaultProfilerPluginContext(agent); + DefaultProfilerPluginContext context = new DefaultProfilerPluginContext(agent, new TestProfilerPluginClassLoader()); + DefaultClassFileTransformerBuilder builder = new DefaultClassFileTransformerBuilder(context, "TargetClass"); builder.injectMetadata("a", "java.util.HashMap"); builder.injectFieldAccessor("someField"); diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultInterceptorFactoryTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultInterceptorFactoryTest.java index 7b158870c..2050bbeb4 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultInterceptorFactoryTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/DefaultInterceptorFactoryTest.java @@ -37,6 +37,7 @@ import com.navercorp.pinpoint.profiler.plugin.TestInterceptors.TestInterceptor0; import com.navercorp.pinpoint.profiler.plugin.TestInterceptors.TestInterceptor1; import com.navercorp.pinpoint.profiler.plugin.TestInterceptors.TestInterceptor2; import com.navercorp.pinpoint.profiler.plugin.interceptor.AnnotatedInterceptorFactory; +import com.navercorp.pinpoint.test.TestProfilerPluginClassLoader; public class DefaultInterceptorFactoryTest { private final DefaultProfilerPluginContext pluginContext = mock(DefaultProfilerPluginContext.class); @@ -50,6 +51,7 @@ public class DefaultInterceptorFactoryTest { public void setUp() { reset(instrumentor, traceContext, aClass, aMethod); when(pluginContext.getTraceContext()).thenReturn(traceContext); + when(pluginContext.getClassInjector()).thenReturn(new TestProfilerPluginClassLoader()); when(aMethod.getDescriptor()).thenReturn(descriptor); } diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoaderTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoaderTest.java deleted file mode 100644 index 972132f52..000000000 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/plugin/Java6UnsafePluginClassLoaderTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2014 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.net.URL; - -import org.junit.Test; - -/** - * @author Jongho Moon - * - */ -public class Java6UnsafePluginClassLoaderTest { - - @Test - public void test() throws Exception { - ClassLoader parent = ClassLoader.getSystemClassLoader(); - ClassLoader child = new Java6UnsafePluginClassLoader(new URL[0], parent); - - final int run = 10000; - final int threadNum = 16; - Thread[] thread = new Thread[threadNum]; - - for (int i = 0; i < threadNum; i++) { - if (i % 2 == 0) { - thread[i] = new Thread(new Parent(parent, child, run)); - } else { - thread[i] = new Thread(new Child(child, run)); - } - } - - for (int i = 0; i < threadNum; i++) { - thread[i].start(); - } - - System.out.println("Wait workers..."); - - for (int i = 0; i < threadNum; i++) { - thread[i].join(); - } - } - - - private static class Parent implements Runnable { - private final ClassLoader parent; - private final ClassLoader child; - private final int count; - - public Parent(ClassLoader parent, ClassLoader child, int count) { - this.parent = parent; - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - synchronized(parent) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } - - private static class Child implements Runnable { - private final ClassLoader child; - private final int count; - - public Child(ClassLoader child, int count) { - this.child = child; - this.count = count; - } - - @Override - public void run() { - for (int i = 0; i < count; i++) { - synchronized(child) { - try { - child.loadClass("no.such.Class"); - } catch (ClassNotFoundException e) { - // ignore - } - } - } - } - } -}