From 0a9c0e4a1a1eefb9a00c687ae4007065bf0ffc75 Mon Sep 17 00:00:00 2001 From: jaehong-kim Date: Wed, 14 Dec 2016 19:07:12 +0900 Subject: [PATCH] add ClassInjector.getResourceAsStream(). --- .../instrument/GuardInstrumentContext.java | 9 ++ .../instrument/InstrumentContext.java | 5 + .../profiler/instrument/ASMClass.java | 6 +- .../instrument/ASMClassNodeAdapter.java | 46 ++++---- .../profiler/instrument/ASMClassPool.java | 2 +- .../profiler/instrument/ASMClassWriter.java | 18 +-- .../BootstrapClassLoaderHandler.java | 31 ++++- .../profiler/instrument/ClassInjector.java | 8 +- ...ClassPoolBase_PlainClassLoaderHandler.java | 6 + .../JarProfilerPluginClassInjector.java | 20 +++- .../LegacyProfilerPluginClassInjector.java | 11 ++ .../instrument/PlainClassLoaderHandler.java | 106 ++++++++++++++---- .../instrument/URLClassLoaderHandler.java | 34 ++++-- .../DefaultSimpleClassMetadata.java | 13 ++- .../classreading/SimpleClassMetadata.java | 3 + .../plugin/DefaultProfilerPluginContext.java | 14 ++- .../test/TestProfilerPluginClassLoader.java | 14 ++- .../instrument/ASMAspectWeaverTest.java | 48 +++++++- .../instrument/ASMClassNodeAdapterTest.java | 69 +++++++++--- .../instrument/ASMClassNodeLoader.java | 48 +++++++- .../profiler/instrument/ASMClassTest.java | 14 +++ .../instrument/ASMClassWriterTest.java | 48 +++++++- 22 files changed, 467 insertions(+), 106 deletions(-) diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/GuardInstrumentContext.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/GuardInstrumentContext.java index 91c68c88c6f9..e96f8ca76491 100644 --- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/GuardInstrumentContext.java +++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/GuardInstrumentContext.java @@ -20,8 +20,11 @@ import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope; +import java.io.InputStream; + /** * @author Woonduk Kang(emeroad) + * @author jaehong.kim */ public class GuardInstrumentContext implements InstrumentContext { private final InstrumentContext instrumentContext; @@ -65,6 +68,12 @@ public Class injectClass(ClassLoader targetClassLoader, String return instrumentContext.injectClass(targetClassLoader, className); } + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + checkOpen(); + return instrumentContext.getResourceAsStream(targetClassLoader, className); + } + @Override public void addClassFileTransformer(ClassLoader classLoader, String targetClassName, TransformCallback transformCallback) { checkOpen(); diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/InstrumentContext.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/InstrumentContext.java index 2cb8af6b5d97..86da89c18525 100644 --- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/InstrumentContext.java +++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/instrument/InstrumentContext.java @@ -20,8 +20,11 @@ import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope; +import java.io.InputStream; + /** * @author Woonduk Kang(emeroad) + * @author jaehong.kim */ public interface InstrumentContext { @@ -35,6 +38,8 @@ public interface InstrumentContext { Class injectClass(ClassLoader targetClassLoader, String className); + InputStream getResourceAsStream(ClassLoader targetClassLoader, String className); + void addClassFileTransformer(ClassLoader classLoader, String targetClassName, TransformCallback transformCallback); void addClassFileTransformer(String targetClassName, TransformCallback transformCallback); diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java index 32106317614f..93be6751ae77 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java @@ -62,7 +62,7 @@ public class ASMClass implements InstrumentClass { private boolean modified = false; public ASMClass(final InstrumentContext pluginContext, final InterceptorRegistryBinder interceptorRegistryBinder, final ClassLoader classLoader, final ClassNode classNode) { - this(pluginContext, interceptorRegistryBinder, classLoader, new ASMClassNodeAdapter(classLoader, classNode)); + this(pluginContext, interceptorRegistryBinder, classLoader, new ASMClassNodeAdapter(pluginContext, classLoader, classNode)); } public ASMClass(final InstrumentContext pluginContext, final InterceptorRegistryBinder interceptorRegistryBinder, final ClassLoader classLoader, final ASMClassNodeAdapter classNode) { @@ -183,7 +183,7 @@ public void weave(final String adviceClassInternalName) throws InstrumentExcepti throw new NotFoundInstrumentException("advice class internal name must not be null"); } - final ASMClassNodeAdapter adviceClassNode = ASMClassNodeAdapter.get(this.classLoader, JavaAssistUtils.javaNameToJvmName(adviceClassInternalName)); + final ASMClassNodeAdapter adviceClassNode = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, JavaAssistUtils.javaNameToJvmName(adviceClassInternalName)); if (adviceClassNode == null) { throw new NotFoundInstrumentException(adviceClassInternalName + " not found."); } @@ -200,7 +200,7 @@ public InstrumentMethod addDelegatorMethod(final String methodName, final String throw new InstrumentException(getName() + " already have method(" + methodName + ")."); } - final ASMClassNodeAdapter superClassNode = ASMClassNodeAdapter.get(this.classLoader, this.classNode.getSuperClassName()); + final ASMClassNodeAdapter superClassNode = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, this.classNode.getSuperClassName()); if (superClassNode == null) { throw new NotFoundInstrumentException(getName() + " not found super class(" + this.classNode.getSuperClassName() + ")"); } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapter.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapter.java index 2ebaab953df3..8a8bc28d1442 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapter.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapter.java @@ -15,6 +15,7 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentContext; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; @@ -41,29 +42,18 @@ */ public class ASMClassNodeAdapter { - public static ASMClassNodeAdapter get(final ClassLoader classLoader, final String className) { - return get(classLoader, className, false); + public static ASMClassNodeAdapter get(final InstrumentContext pluginContext, final ClassLoader classLoader, final String className) { + return get(pluginContext, classLoader, className, false); } - public static ASMClassNodeAdapter get(final ClassLoader classLoader, final String className, final boolean skipCode) { - if (className == null) { - throw new IllegalArgumentException("class name must not be null."); - } - - ClassLoader aClassLoader = classLoader; - if (aClassLoader == null) { - // bootstrap class loader. - aClassLoader = ClassLoader.getSystemClassLoader(); - } - - if (aClassLoader == null) { - // not initialized system classloader. - return null; + public static ASMClassNodeAdapter get(final InstrumentContext pluginContext, final ClassLoader classLoader, final String className, final boolean skipCode) { + if (pluginContext == null || className == null) { + throw new IllegalArgumentException("plugin context or class name must not be null."); } InputStream in = null; try { - in = aClassLoader.getResourceAsStream(className + ".class"); + in = pluginContext.getResourceAsStream(classLoader, className + ".class"); if (in != null) { final ClassReader classReader = new ClassReader(in); final ClassNode classNode = new ClassNode(); @@ -73,7 +63,7 @@ public static ASMClassNodeAdapter get(final ClassLoader classLoader, final Strin classReader.accept(classNode, 0); } - return new ASMClassNodeAdapter(classLoader, classNode, skipCode); + return new ASMClassNodeAdapter(pluginContext, classLoader, classNode, skipCode); } } catch (IOException ignored) { // not found class. @@ -89,15 +79,17 @@ public static ASMClassNodeAdapter get(final ClassLoader classLoader, final Strin return null; } + private final InstrumentContext pluginContext; private final ClassLoader classLoader; private final ClassNode classNode; private final boolean skipCode; - public ASMClassNodeAdapter(final ClassLoader classLoader, final ClassNode classNode) { - this(classLoader, classNode, false); + public ASMClassNodeAdapter(final InstrumentContext pluginContext, final ClassLoader classLoader, final ClassNode classNode) { + this(pluginContext, classLoader, classNode, false); } - public ASMClassNodeAdapter(final ClassLoader classLoader, final ClassNode classNode, final boolean skipCode) { + public ASMClassNodeAdapter(final InstrumentContext pluginContext, final ClassLoader classLoader, final ClassNode classNode, final boolean skipCode) { + this.pluginContext = pluginContext; this.classLoader = classLoader; this.classNode = classNode; this.skipCode = skipCode; @@ -219,7 +211,7 @@ public boolean hasMethod(final String methodName, final String desc) { if (this.classNode.superName != null) { // skip code. - final ASMClassNodeAdapter classNode = ASMClassNodeAdapter.get(this.classLoader, this.classNode.superName, true); + final ASMClassNodeAdapter classNode = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, this.classNode.superName, true); if (classNode != null) { return classNode.hasMethod(methodName, desc); } @@ -248,7 +240,7 @@ public ASMFieldNodeAdapter getField(final String fieldName, final String fieldDe continue; } - final ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(this.classLoader, interfaceClassName, true); + final ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, interfaceClassName, true); if (classNodeAdapter != null) { final ASMFieldNodeAdapter fieldNode = classNodeAdapter.getField(fieldName, fieldDesc); if (fieldNode != null) { @@ -260,7 +252,7 @@ public ASMFieldNodeAdapter getField(final String fieldName, final String fieldDe // find super class. if (this.classNode.superName != null) { - final ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(this.classLoader, this.classNode.superName, true); + final ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, this.classNode.superName, true); if (classNodeAdapter != null) { final ASMFieldNodeAdapter fieldNode = classNodeAdapter.getField(fieldName, fieldDesc); if (fieldNode != null) { @@ -437,7 +429,7 @@ public boolean subclassOf(final String superInternalName) { } // skip code. - classNode = ASMClassNodeAdapter.get(this.classLoader, superClassName, true); + classNode = ASMClassNodeAdapter.get(this.pluginContext, this.classLoader, superClassName, true); } return false; @@ -455,7 +447,7 @@ public List getInnerClasses() { continue; } // skip code. - ASMClassNodeAdapter adapter = get(this.classLoader, node.name, true); + ASMClassNodeAdapter adapter = get(this.pluginContext, this.classLoader, node.name, true); if (adapter != null) { innerClasses.add(adapter); } @@ -472,7 +464,7 @@ public byte[] toByteArray() { flags = ClassWriter.COMPUTE_MAXS; } - final ClassWriter classWriter = new ASMClassWriter(this.classNode.name, this.classNode.superName, flags, this.classLoader); + final ClassWriter classWriter = new ASMClassWriter(this.pluginContext, this.classNode.name, this.classNode.superName, flags, this.classLoader); this.classNode.accept(classWriter); return classWriter.toByteArray(); } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassPool.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassPool.java index 0bb6a37b9cb2..87c8c99843b4 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassPool.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassPool.java @@ -54,7 +54,7 @@ public InstrumentClass getClass(InstrumentContext instrumentContext, ClassLoader try { if (classFileBuffer == null) { - ASMClassNodeAdapter classNode = ASMClassNodeAdapter.get(classLoader, JavaAssistUtils.javaNameToJvmName(classInternalName)); + ASMClassNodeAdapter classNode = ASMClassNodeAdapter.get(instrumentContext, classLoader, JavaAssistUtils.javaNameToJvmName(classInternalName)); if (classNode == null) { return null; } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriter.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriter.java index 733c9e7e810f..74bd15dcbf93 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriter.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriter.java @@ -15,6 +15,7 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentContext; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -34,12 +35,14 @@ public final class ASMClassWriter extends ClassWriter { private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final InstrumentContext pluginContext; private ClassLoader classLoader; private String className; private String superClassName; - public ASMClassWriter(final String className, final String superClassName, final int flags, final ClassLoader classLoader) { + public ASMClassWriter(final InstrumentContext pluginContext, final String className, final String superClassName, final int flags, final ClassLoader classLoader) { super(flags); + this.pluginContext = pluginContext; this.className = className; this.superClassName = superClassName; this.classLoader = classLoader; @@ -197,20 +200,9 @@ private String getSuperClassName(final String className) { } private ClassReader getClassReader(final String className) { - ClassLoader aClassLoader = classLoader; - if (aClassLoader == null) { - // bootstrap class loader. - aClassLoader = ClassLoader.getSystemClassLoader(); - } - - if (aClassLoader == null) { - // not initialized system classloader. - return null; - } - InputStream in = null; try { - in = aClassLoader.getResourceAsStream(className + ".class"); + in = pluginContext.getResourceAsStream(this.classLoader, className + ".class"); if (in != null) { return new ClassReader(in); } 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 index 5d0374d9f967..848ea3a81bb6 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/BootstrapClassLoaderHandler.java @@ -20,10 +20,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; /** * @author Woonduk Kang(emeroad) + * @author jaehong.kim */ public class BootstrapClassLoaderHandler implements ClassInjector { @@ -46,7 +48,7 @@ public BootstrapClassLoaderHandler(PluginConfig pluginConfig) { public Class injectClass(ClassLoader classLoader, String className) { try { if (classLoader == null) { - return (Class)injectClass0(className); + return (Class) injectClass0(className); } } catch (Exception e) { logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e); @@ -56,6 +58,11 @@ public Class injectClass(ClassLoader classLoader, String classN } private Class injectClass0(String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + appendToBootstrapClassLoaderSearch(); + return Class.forName(className, false, null); + } + + private void appendToBootstrapClassLoaderSearch() { synchronized (lock) { if (this.injectedToRoot == false) { this.injectedToRoot = true; @@ -63,8 +70,24 @@ private Class injectClass0(String className) throws IllegalArgumentException, pluginConfig.getClassPool().appendToBootstrapClassPath(pluginConfig.getPluginJarFile().getName()); } } - return Class.forName(className, false, null); } - -} + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + try { + if (targetClassLoader == null) { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + if (classLoader == null) { + return null; + } + appendToBootstrapClassLoaderSearch(); + return classLoader.getResourceAsStream(className); + } + } catch (Exception e) { + logger.warn("Failed to load plugin resource as stream {} with classLoader {}", className, targetClassLoader, e); + return null; + } + logger.warn("Invalid bootstrap class loader. cl={}", targetClassLoader); + return null; + } +} \ No newline at end of file diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassInjector.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassInjector.java index cbe5b8d19a79..47fa6a54a635 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassInjector.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassInjector.java @@ -14,12 +14,16 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import java.io.InputStream; + /** * @author Jongho Moon - * + * @author jaehong.kim */ -public interface ClassInjector { +public interface ClassInjector { Class injectClass(ClassLoader targetClassLoader, String className); + InputStream getResourceAsStream(ClassLoader targetClassLoader, String className); + } \ No newline at end of file diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassPoolBase_PlainClassLoaderHandler.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassPoolBase_PlainClassLoaderHandler.java index f3bdd6495e72..f901b7905c32 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassPoolBase_PlainClassLoaderHandler.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ClassPoolBase_PlainClassLoaderHandler.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -183,4 +184,9 @@ private boolean isSkipClass(final String className, final ClassLoadingChecker cl return false; } + + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + return null; + } } \ No newline at end of file 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 c3248fde7ab5..ec3bab2a00c3 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 @@ -14,6 +14,7 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import java.io.InputStream; import java.net.URLClassLoader; import com.navercorp.pinpoint.profiler.plugin.PluginConfig; @@ -25,7 +26,7 @@ /** * @author Jongho Moon - * + * @author jaehong.kim */ public class JarProfilerPluginClassInjector implements PluginClassInjector { private final Logger logger = LoggerFactory.getLogger(JarProfilerPluginClassInjector.class); @@ -69,4 +70,19 @@ public Class injectClass(ClassLoader classLoader, String classN } } -} + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + try { + if (targetClassLoader == null) { + return bootstrapClassLoaderHandler.getResourceAsStream(null, className); + } else if (targetClassLoader instanceof URLClassLoader) { + final URLClassLoader urlClassLoader = (URLClassLoader) targetClassLoader; + return urlClassLoaderHandler.getResourceAsStream(urlClassLoader, className); + } else { + return plainClassLoaderHandler.getResourceAsStream(targetClassLoader, className); + } + } catch (Throwable e) { + logger.warn("Failed to load plugin resource as stream {} with classLoader {}", className, targetClassLoader, e); + return null; + } + } +} \ No newline at end of file diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/LegacyProfilerPluginClassInjector.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/LegacyProfilerPluginClassInjector.java index 464a00537b3f..a20d53c22bd3 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/LegacyProfilerPluginClassInjector.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/LegacyProfilerPluginClassInjector.java @@ -15,6 +15,7 @@ package com.navercorp.pinpoint.profiler.instrument; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; @@ -124,4 +125,14 @@ private Class loadFromOtherClassLoader(ClassPool pool, ClassLoader classLoade byte[] bytes = ct.toBytecode(); return (Class)DEFINE_CLASS.invoke(classLoader, ct.getName(), bytes, 0, bytes.length); } + + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + ClassLoader classLoader = targetClassLoader; + if(classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + + return classLoader.getResourceAsStream(className); + } } 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 index e8086b5c174c..52f70ed1ed2d 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/PlainClassLoaderHandler.java @@ -24,12 +24,16 @@ import com.navercorp.pinpoint.profiler.util.ExtensionFilter; import com.navercorp.pinpoint.profiler.util.FileBinary; import com.navercorp.pinpoint.profiler.util.JarReader; +import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; import javassist.CannotCompileException; import javassist.NotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -56,7 +60,6 @@ public class PlainClassLoaderHandler implements ClassInjector { // TODO remove static field private static final ConcurrentMap classLoaderAttachment = new ConcurrentWeakHashMap(); - static { try { DEFINE_CLASS = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); @@ -76,7 +79,6 @@ public PlainClassLoaderHandler(PluginConfig pluginConfig) { this.pluginJarReader = new JarReader(pluginConfig.getPluginJarFile()); } - @Override @SuppressWarnings("unchecked") public Class injectClass(ClassLoader classLoader, String className) { @@ -88,8 +90,6 @@ public Class injectClass(ClassLoader classLoader, String classN if (!isPluginPackage(className)) { return loadClass(classLoader, className); } - logger.info("bootstrapJarPaths:{}", pluginConfig.getBootstrapJarPaths()); - return (Class) injectClass0(classLoader, className); } catch (Exception e) { logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e); @@ -97,6 +97,40 @@ public Class injectClass(ClassLoader classLoader, String classN } } + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + try { + String name = JavaAssistUtils.jvmNameToJavaName(className); + if (isBootstrapPackage(name)) { + ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + if(systemClassLoader != null) { + return systemClassLoader.getResourceAsStream(className); + } + return null; + } + if (!isPluginPackage(name)) { + return targetClassLoader.getResourceAsStream(className); + } + final int fileExtensionPosition = name.lastIndexOf(".class"); + if(fileExtensionPosition != -1) { + name = name.substring(0, fileExtensionPosition); + } + + final InputStream inputStream = getInputStream(targetClassLoader, name); + if (inputStream == null) { + if (logger.isInfoEnabled()) { + logger.info("can not find resource : {} {} ", className, pluginConfig.getPluginJarURLExternalForm()); + } + // fallback + return targetClassLoader.getResourceAsStream(className); + } + return inputStream; + } catch (Exception e) { + logger.warn("Failed to load plugin resource as stream {} with classLoader {}", className, targetClassLoader, e); + return null; + } + } + private boolean isPluginPackage(String className) { return pluginConfig.getPluginPackageFilter().accept(className); } @@ -112,11 +146,34 @@ private boolean isBootstrapPackage(String className) { private Class injectClass0(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, CannotCompileException, IllegalAccessException, InvocationTargetException { if (isDebug) { - logger.debug("injectClass0 className:{} cl:{}", className, classLoader); + logger.debug("Inject class className:{} cl:{}", className, classLoader); + } + final String pluginJarPath = pluginConfig.getPluginJarURLExternalForm(); + final ClassLoaderAttachment attachment = getClassLoaderAttachment(classLoader, pluginJarPath); + final Class findClazz = attachment.getClass(className); + if (findClazz == null) { + if (logger.isInfoEnabled()) { + logger.info("can not find class : {} {} ", className, pluginConfig.getPluginJarURLExternalForm()); + } + // fallback + return loadClass(classLoader, className); + } + return findClazz; + + } + + private InputStream getInputStream(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, CannotCompileException, IllegalAccessException, InvocationTargetException { + if (isDebug) { + logger.debug("Get input stream className:{} cl:{}", className, classLoader); } final String pluginJarPath = pluginConfig.getPluginJarURLExternalForm(); + final ClassLoaderAttachment attachment = getClassLoaderAttachment(classLoader, pluginJarPath); + final InputStream inputStream = attachment.getInputStream(className); + return inputStream; + } + private ClassLoaderAttachment getClassLoaderAttachment(ClassLoader classLoader, final String pluginJarPath) { final ClassLoaderAttachment attachment = getClassLoaderAttachment(classLoader); // this order is thread safe ? @@ -133,16 +190,7 @@ private Class injectClass0(ClassLoader classLoader, String className) throws } } - final Class findClazz = attachment.getClass(className); - if (findClazz == null) { - if (logger.isInfoEnabled()) { - logger.info("can not find class :{} {} {} ", className, pluginConfig.getPluginJarURLExternalForm()); - } - // fallback - return loadClass(classLoader, className); - } - return findClazz; - + return attachment; } private ClassLoaderAttachment getClassLoaderAttachment(ClassLoader classLoader) { @@ -251,8 +299,9 @@ private void define0(ClassLoader classLoader, ClassLoaderAttachment attachment, } } - Class clazz = defineClass(classLoader, currentClass); - attachment.putClass(currentClass.getClassName(), clazz); + final Class clazz = defineClass(classLoader, currentClass); + currentClass.setDefinedClass(clazz); + attachment.putClass(currentClass.getClassName(), currentClass); } @@ -305,7 +354,7 @@ private class ClassLoaderAttachment { private final ConcurrentMap pluginLock = new ConcurrentHashMap(); - private final ConcurrentMap> classCache = new ConcurrentHashMap>(); + private final ConcurrentMap classCache = new ConcurrentHashMap(); public PluginLock getPluginLock(String jarFile) { final PluginLock exist = this.pluginLock.get(jarFile); @@ -321,8 +370,8 @@ public PluginLock getPluginLock(String jarFile) { return newPluginLock; } - public void putClass(String className, Class clazz) { - final Class duplicatedClass = this.classCache.putIfAbsent(className, clazz); + public void putClass(String className, SimpleClassMetadata classMetadata) { + final SimpleClassMetadata duplicatedClass = this.classCache.putIfAbsent(className, classMetadata); if (duplicatedClass != null) { if (logger.isWarnEnabled()) { logger.warn("duplicated pluginClass {}", className); @@ -331,12 +380,27 @@ public void putClass(String className, Class clazz) { } public Class getClass(String className) { - return this.classCache.get(className); + final SimpleClassMetadata classMetadata = this.classCache.get(className); + if(classMetadata == null) { + return null; + } + + return classMetadata.getDefinedClass(); } public boolean containsClass(String className) { return this.classCache.containsKey(className); } + + public InputStream getInputStream(String className) { + final SimpleClassMetadata classMetadata = this.classCache.get(className); + if(classMetadata == null) { + return null; + } + + return new ByteArrayInputStream(classMetadata.getClassBinary()); + } + } private static class PluginLock { 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 index 88623eefc1a0..bcef58fc0d36 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/URLClassLoaderHandler.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; @@ -27,10 +28,12 @@ /** * @author Woonduk Kang(emeroad) + * @author jaehong.kim */ public class URLClassLoaderHandler implements ClassInjector { private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); private static final Method ADD_URL; @@ -60,7 +63,8 @@ public Class injectClass(ClassLoader classLoader, String classN try { if (classLoader instanceof URLClassLoader) { final URLClassLoader urlClassLoader = (URLClassLoader) classLoader; - return (Class)injectClass0(urlClassLoader, className); + addPluginURLIfAbsent(urlClassLoader); + return (Class) urlClassLoader.loadClass(className); } } catch (Exception e) { logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e); @@ -69,21 +73,32 @@ public Class injectClass(ClassLoader classLoader, String classN throw new PinpointException("invalid ClassLoader"); } + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + try { + if (targetClassLoader instanceof URLClassLoader) { + final URLClassLoader urlClassLoader = (URLClassLoader) targetClassLoader; + addPluginURLIfAbsent(urlClassLoader); + return targetClassLoader.getResourceAsStream(className); + } + } catch (Exception e) { + logger.warn("Failed to load plugin resource as stream {} with classLoader {}", className, targetClassLoader, e); + return null; + } + return null; + } - - private Class injectClass0(URLClassLoader classLoader, String className) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + private void addPluginURLIfAbsent(URLClassLoader classLoader) 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); + if (isDebug) { + logger.debug("add Jar:{}", pluginURLString); + } ADD_URL.invoke(classLoader, pluginURL); } } - - return classLoader.loadClass(className); } private boolean hasPluginJar(URL[] urls) { @@ -92,12 +107,9 @@ private boolean hasPluginJar(URL[] urls) { // 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/instrument/classreading/DefaultSimpleClassMetadata.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/DefaultSimpleClassMetadata.java index 466f1c7775cd..53a96d665601 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/DefaultSimpleClassMetadata.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/DefaultSimpleClassMetadata.java @@ -24,6 +24,7 @@ /** * @author Woonduk Kang(emeroad) + * @author jaehong.kim */ public class DefaultSimpleClassMetadata implements SimpleClassMetadata { @@ -39,6 +40,8 @@ public class DefaultSimpleClassMetadata implements SimpleClassMetadata { private final byte[] classBinary; + private Class definedClass; + public DefaultSimpleClassMetadata(int version, int accessFlag, String className, String superClassName, String[] interfaceNameList, byte[] classBinary) { this.version = version; this.accessFlag = accessFlag; @@ -48,7 +51,6 @@ public DefaultSimpleClassMetadata(int version, int accessFlag, String className, this.classBinary = classBinary; } - @Override public int getVersion() { return version; @@ -77,4 +79,13 @@ public List getInterfaceNames() { public byte[] getClassBinary() { return classBinary; } + + public void setDefinedClass(final Class definedClass) { + this.definedClass = definedClass; + } + + @Override + public Class getDefinedClass() { + return this.definedClass; + } } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/SimpleClassMetadata.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/SimpleClassMetadata.java index 99f5ef51fc10..e5bf6c1a63cb 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/SimpleClassMetadata.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/classreading/SimpleClassMetadata.java @@ -36,4 +36,7 @@ public interface SimpleClassMetadata { byte[] getClassBinary(); + void setDefinedClass(Class definedClass); + + Class getDefinedClass(); } 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 40beb4627013..9973bc14f071 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 @@ -16,6 +16,7 @@ package com.navercorp.pinpoint.profiler.plugin; +import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.util.ArrayList; import java.util.List; @@ -41,7 +42,9 @@ import com.navercorp.pinpoint.profiler.instrument.PluginClassInjector; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; - +/** + * @author jaehong.kim + */ public class DefaultProfilerPluginContext implements ProfilerPluginSetupContext, InstrumentContext { private final DefaultAgent agent; private final ClassInjector classInjector; @@ -176,6 +179,15 @@ public Class injectClass(ClassLoader targetClassLoader, String return classInjector.injectClass(targetClassLoader, className); } + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + if (className == null) { + return null; + } + + return classInjector.getResourceAsStream(targetClassLoader, className); + } + public List getClassEditors() { return classTransformers; } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java b/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java index d77b40ebfd7f..5085d96fbe15 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/test/TestProfilerPluginClassLoader.java @@ -17,9 +17,11 @@ import com.navercorp.pinpoint.exception.PinpointException; import com.navercorp.pinpoint.profiler.instrument.ClassInjector; +import java.io.InputStream; + /** * @author Jongho Moon - * + * @author jaehong.kim */ public class TestProfilerPluginClassLoader implements ClassInjector { @@ -32,4 +34,14 @@ public Class injectClass(ClassLoader targetClassLoader, String throw new PinpointException("Cannot find class: " + className, e); } } + + @Override + public InputStream getResourceAsStream(ClassLoader targetClassLoader, String className) { + ClassLoader classLoader = targetClassLoader; + if(classLoader == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } + + return classLoader.getResourceAsStream(className); + } } diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMAspectWeaverTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMAspectWeaverTest.java index 52079a7d6af1..278471e53f8c 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMAspectWeaverTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMAspectWeaverTest.java @@ -15,16 +15,27 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; +import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.util.CheckClassAdapter; +import java.io.InputStream; import java.io.PrintWriter; import java.lang.reflect.Method; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + /** * @author jaehong.kim */ @@ -42,6 +53,39 @@ public class ASMAspectWeaverTest { private final String ERROR_ASPECT_INVALID_EXTENTS = "com.navercorp.pinpoint.profiler.instrument.mock.AspectInterceptorInvalidExtendClass"; + private final DefaultProfilerPluginContext pluginContext = mock(DefaultProfilerPluginContext.class); + private final TraceContext traceContext = mock(TraceContext.class); + + @Before + public void setUp() { + reset(traceContext); + when(pluginContext.getTraceContext()).thenReturn(traceContext); + when(pluginContext.injectClass(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer>() { + + @Override + public Class answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + + return loader.loadClass(name); + } + + }); + when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer() { + + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + if(loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + + return loader.getResourceAsStream(name); + } + }); + } + @Test public void weaving() throws Exception { weaving(ORIGINAL, ASPECT); @@ -96,8 +140,8 @@ public Class loadClass(String name) throws ClassNotFoundException { final ClassNode classNode = new ClassNode(); cr.accept(classNode, 0); - final ASMClassNodeAdapter sourceClassNode = new ASMClassNodeAdapter(defaultClassLoader, classNode); - final ASMClassNodeAdapter adviceClassNode = ASMClassNodeAdapter.get(defaultClassLoader, JavaAssistUtils.javaNameToJvmName(aspectName)); + final ASMClassNodeAdapter sourceClassNode = new ASMClassNodeAdapter(pluginContext, defaultClassLoader, classNode); + final ASMClassNodeAdapter adviceClassNode = ASMClassNodeAdapter.get(pluginContext, defaultClassLoader, JavaAssistUtils.javaNameToJvmName(aspectName)); final ASMAspectWeaver aspectWeaver = new ASMAspectWeaver(); aspectWeaver.weaving(sourceClassNode, adviceClassNode); diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapterTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapterTest.java index faec3f9b4e98..b407fc3243ab 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapterTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeAdapterTest.java @@ -15,13 +15,19 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.instrument.aspect.Aspect; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; +import java.io.InputStream; import java.lang.reflect.Method; import java.util.List; @@ -29,20 +35,57 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; public class ASMClassNodeAdapterTest { + private final DefaultProfilerPluginContext pluginContext = mock(DefaultProfilerPluginContext.class); + private final TraceContext traceContext = mock(TraceContext.class); + + @Before + public void setUp() { + reset(traceContext); + when(pluginContext.getTraceContext()).thenReturn(traceContext); + when(pluginContext.injectClass(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer>() { + + @Override + public Class answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + + return loader.loadClass(name); + } + + }); + when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer() { + + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + if(loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + + return loader.getResourceAsStream(name); + } + }); + } + @Test public void get() { final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass"); + ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass"); assertNotNull(adapter); - adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/NotExistClass"); + adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/NotExistClass"); assertNull(adapter); // skip code - adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass", true); + adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseClass", true); try { adapter.getDeclaredMethods(); fail("can't throw IllegalStateException"); @@ -59,7 +102,7 @@ public void get() { @Test public void getter() { final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass"); + ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass"); // name assertEquals("com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass", adapter.getName()); assertEquals("com.navercorp.pinpoint.profiler.instrument.mock.ExtendedClass", adapter.getInternalName()); @@ -91,17 +134,17 @@ public void getter() { assertNull(field); // interface - adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseInterface"); + adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseInterface"); assertEquals(true, adapter.isInterface()); // implement - adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseImplementClass"); + adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/profiler/instrument/mock/BaseImplementClass"); String[] interfaceNames = adapter.getInterfaceInternalNames(); assertEquals(1, interfaceNames.length); assertEquals("com.navercorp.pinpoint.profiler.instrument.mock.BaseInterface", interfaceNames[0]); // annotation - adapter = ASMClassNodeAdapter.get(classLoader, "com/navercorp/pinpoint/bootstrap/instrument/aspect/Aspect"); + adapter = ASMClassNodeAdapter.get(pluginContext, classLoader, "com/navercorp/pinpoint/bootstrap/instrument/aspect/Aspect"); assertEquals(true, adapter.isAnnotation()); @@ -116,7 +159,7 @@ public void addGetter() throws Exception { classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() { @Override public void handle(ClassNode classNode) { - ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(null, classNode); + ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(pluginContext, null, classNode); ASMFieldNodeAdapter field = adapter.getField("i", null); adapter.addGetterMethod(getterMethodName, field); } @@ -136,7 +179,7 @@ public void addSetter() throws Exception { classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() { @Override public void handle(ClassNode classNode) { - ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(null, classNode); + ASMClassNodeAdapter adapter = new ASMClassNodeAdapter(pluginContext, null, classNode); ASMFieldNodeAdapter field = adapter.getField("i", null); adapter.addSetterMethod(setterMethodName, field); } @@ -155,7 +198,7 @@ public void addField() throws Exception { classLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() { @Override public void handle(ClassNode classNode) { - ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(null, classNode); + ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(pluginContext, null, classNode); classNodeAdapter.addField("_$PINPOINT$_" + JavaAssistUtils.javaClassNameToVariableName(accessorClassName), int.class); classNodeAdapter.addInterface(accessorClassName); ASMFieldNodeAdapter fieldNode = classNodeAdapter.getField("_$PINPOINT$_" + JavaAssistUtils.javaClassNameToVariableName(accessorClassName), null); @@ -176,7 +219,7 @@ public void handle(ClassNode classNode) { @Test public void hasAnnotation() throws Exception { - ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(null, "com/navercorp/pinpoint/profiler/instrument/mock/AnnotationClass"); + ASMClassNodeAdapter classNodeAdapter = ASMClassNodeAdapter.get(pluginContext, null, "com/navercorp/pinpoint/profiler/instrument/mock/AnnotationClass"); Assert.assertTrue(classNodeAdapter.hasAnnotation(Aspect.class)); Assert.assertFalse(classNodeAdapter.hasAnnotation(Override.class)); } @@ -192,7 +235,7 @@ public void addMethod() throws Exception { testClassLoader.setCallbackHandler(new ASMClassNodeLoader.CallbackHandler() { @Override public void handle(ClassNode classNode) { - ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(null, classNode); + ASMClassNodeAdapter classNodeAdapter = new ASMClassNodeAdapter(pluginContext, null, classNode); classNodeAdapter.copyMethod(adapter); } }); @@ -203,7 +246,7 @@ public void handle(ClassNode classNode) { @Test public void subclassOf() { - ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(null, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass"); + ASMClassNodeAdapter adapter = ASMClassNodeAdapter.get(pluginContext, null, "com/navercorp/pinpoint/profiler/instrument/mock/ExtendedClass"); // self assertEquals(true, adapter.subclassOf("com.navercorp.pinpoint.profiler.instrument.mock.ExtendedClass")); diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeLoader.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeLoader.java index b071940f5377..6acb74729755 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeLoader.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassNodeLoader.java @@ -15,7 +15,12 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; +import org.junit.Before; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; @@ -23,14 +28,53 @@ import org.objectweb.asm.util.CheckClassAdapter; import org.objectweb.asm.util.TraceClassVisitor; +import java.io.InputStream; import java.io.PrintWriter; import java.util.List; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + /** * @author jaehong.kim */ public class ASMClassNodeLoader { + private final static DefaultProfilerPluginContext pluginContext = mock(DefaultProfilerPluginContext.class); + private final static TraceContext traceContext = mock(TraceContext.class); + + static { + reset(traceContext); + when(pluginContext.getTraceContext()).thenReturn(traceContext); + when(pluginContext.injectClass(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer>() { + + @Override + public Class answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + + return loader.loadClass(name); + } + + }); + when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer() { + + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + if(loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + + return loader.getResourceAsStream(name); + } + }); + } + + // only use for test. public static ClassNode get(final String classInternalName) throws Exception { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); @@ -88,7 +132,7 @@ public Class loadClass(final String name) throws ClassNotFoundException { if (this.trace) { System.out.println("## original #############################################################"); - ASMClassWriter cw = new ASMClassWriter(classNode.name, classNode.superName, 0, null); + ASMClassWriter cw = new ASMClassWriter(pluginContext, classNode.name, classNode.superName, 0, null); TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out)); classNode.accept(tcv); } @@ -97,7 +141,7 @@ public Class loadClass(final String name) throws ClassNotFoundException { callbackHandler.handle(classNode); } - ASMClassWriter cw = new ASMClassWriter(classNode.name, classNode.superName, ClassWriter.COMPUTE_FRAMES, null); + ASMClassWriter cw = new ASMClassWriter(pluginContext, classNode.name, classNode.superName, ClassWriter.COMPUTE_FRAMES, null); if (this.trace) { System.out.println("## modified #############################################################"); TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out)); diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassTest.java index 73b26407ee39..1bd770c568e6 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassTest.java @@ -29,6 +29,7 @@ import org.mockito.stubbing.Answer; import org.objectweb.asm.tree.ClassNode; +import java.io.InputStream; import java.util.List; import static org.junit.Assert.assertEquals; @@ -66,6 +67,19 @@ public Class answer(InvocationOnMock invocation) throws Throwable { } }); + when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer() { + + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + if(loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + + return loader.getResourceAsStream(name); + } + }); } @Test diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriterTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriterTest.java index 4dc57b80a6d1..e7efc6d91c93 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriterTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/ASMClassWriterTest.java @@ -15,14 +15,24 @@ */ package com.navercorp.pinpoint.profiler.instrument; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.profiler.plugin.DefaultProfilerPluginContext; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; +import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.util.TraceClassVisitor; +import java.io.InputStream; import java.io.PrintWriter; import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; /** * @author jaehong.kim @@ -30,19 +40,53 @@ public class ASMClassWriterTest { + private final DefaultProfilerPluginContext pluginContext = mock(DefaultProfilerPluginContext.class); + private final TraceContext traceContext = mock(TraceContext.class); + + @Before + public void setUp() { + reset(traceContext); + when(pluginContext.getTraceContext()).thenReturn(traceContext); + when(pluginContext.injectClass(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer>() { + + @Override + public Class answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + + return loader.loadClass(name); + } + + }); + when(pluginContext.getResourceAsStream(any(ClassLoader.class), any(String.class))).thenAnswer(new Answer() { + + @Override + public InputStream answer(InvocationOnMock invocation) throws Throwable { + ClassLoader loader = (ClassLoader) invocation.getArguments()[0]; + String name = (String) invocation.getArguments()[1]; + if(loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + + return loader.getResourceAsStream(name); + } + }); + } + + @Test public void accept() throws Exception { final String className = "com.navercorp.pinpoint.profiler.instrument.mock.SampleClass"; ClassNode classNode = ASMClassNodeLoader.get(JavaAssistUtils.javaNameToJvmName(className)); - ASMClassWriter cw = new ASMClassWriter(classNode.name, classNode.superName, 0, null); + ASMClassWriter cw = new ASMClassWriter(pluginContext, classNode.name, classNode.superName, 0, null); TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out)); classNode.accept(tcv); } @Test public void getCommonSuperClass() throws Exception { - ASMClassWriter cw = new ASMClassWriter("", "", 0, null); + ASMClassWriter cw = new ASMClassWriter(pluginContext, "", "", 0, null); // java/lang/object. assertEquals("java/lang/Object", cw.getCommonSuperClass("java/util/Iterator", "java/lang/Object")); assertEquals("java/lang/Object", cw.getCommonSuperClass("java/lang/Object", "java/lang/String"));