diff --git a/src/main/java/com/alibaba/ttl/classloader/TtlAgentJarUtil.java b/src/main/java/com/alibaba/ttl/classloader/TtlAgentJarUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..d33650474f953130d892fb5c8b3b8e59245bffde --- /dev/null +++ b/src/main/java/com/alibaba/ttl/classloader/TtlAgentJarUtil.java @@ -0,0 +1,159 @@ +package com.alibaba.ttl.classloader; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import com.alibaba.ttl.threadpool.agent.TtlAgent; + +public class TtlAgentJarUtil { + + private static final Logger logger = Logger.getLogger(TtlAgentJarUtil.class.getName()); + + private static final Pattern CLASS_NAME = Pattern.compile(TtlAgent.class.getName().replace('.', '/') + ".class"); + + private static URL cachedURL = null; + + @SuppressWarnings({ "rawtypes" }) + public static URL getAgentJarFilePath() { + + if (cachedURL != null) { + return cachedURL; + } + ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader(); + if ((sysClassLoader instanceof URLClassLoader)) { + URL[] jarUrls = ((URLClassLoader) sysClassLoader).getURLs(); + for (URL jarFullPath : jarUrls) { + if ((jarFullPath.getFile().endsWith(".jar")) + && (jarFullPath.getFile().indexOf("transmittable-thread-local") != -1)) { + Collection jarClassCollection = getClassCollectionsByJarUrl(jarFullPath, CLASS_NAME); + if (!jarClassCollection.isEmpty()) { + cachedURL = jarFullPath; + return cachedURL; + } + } + } + } + // JDK 10 and later needs this code + Class loadClasssysClassLoader = sysClassLoader.getClass(); + if (loadClasssysClassLoader.getName().equals("jdk.internal.loader.BuiltinClassLoader") + || loadClasssysClassLoader.getSuperclass().getName().equals("jdk.internal.loader.BuiltinClassLoader")) { + try { + + //ucp field has class jar file system location information that we need + Field classPathField = loadClasssysClassLoader.getDeclaredField("ucp"); + Object classLoaderModule = java.lang.Class.class.getDeclaredMethod("getModule") + .invoke(loadClasssysClassLoader); + //get system classloader's module + Field moduleField = java.lang.Class.class.getDeclaredField("module"); + moduleField.setAccessible(true); + + // set agent class module the same as classloader's module + moduleField.set(TtlAgentJarUtil.class, classLoaderModule); + + // in jdk10 after caller( in this case TtlAgentJarUtil.class) has the same module with target class (systemClassLoad of jdk 10) + // then we can call setAccessible(true) to access private field + classPathField.setAccessible(true); + Object urlclasspath = classPathField.get(sysClassLoader); + + // jdk.internal.loader.URLClassPath is JDK 10 special class, we can only reflect invoke + Method getUrlMethod = Class.forName("jdk.internal.loader.URLClassPath").getDeclaredMethod("getURLs"); + URL[] jarUrls = (URL[]) getUrlMethod.invoke(urlclasspath); + for (URL jarFullPath : jarUrls) { + if ((jarFullPath.getFile().endsWith(".jar")) + && (jarFullPath.getFile().indexOf("transmittable-thread-local") != -1)) { + Collection jarClassCollection = getClassCollectionsByJarUrl(jarFullPath, CLASS_NAME); + if (!jarClassCollection.isEmpty()) { + cachedURL = jarFullPath; + return cachedURL; + } + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + cachedURL = TtlAgent.class.getProtectionDomain().getCodeSource().getLocation(); + return cachedURL; + } + + private static Collection getClassCollectionsByJarUrl(URL paramURL, Pattern classNamePattern) { + JarFile localJarFile = null; + try { + localJarFile = getJarFileByPath(paramURL); + List destinationClassList = new ArrayList(); + Enumeration jarEntry = localJarFile.entries(); + while (jarEntry.hasMoreElements()) { + JarEntry jarEntryObject = jarEntry.nextElement(); + if (classNamePattern.matcher(jarEntryObject.getName()).matches()) { + destinationClassList.add(jarEntryObject.getName()); + } + } + return destinationClassList; + } catch (Exception t) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, "Unable to search the agent jar for " + classNamePattern.pattern(), t); + } + return Collections.emptyList(); + } finally { + if (localJarFile != null) { + try { + localJarFile.close(); + } catch (IOException localIOException3) { + } + } + } + } + + public static Collection getClassCollectionsByClassPattern(Pattern classPattern) { + URL jarUrl = getAgentJarFilePath(); + return getClassCollectionsByJarUrl(jarUrl, classPattern); + } + + public static File getJarFile() { + URL argentUrl = getAgentJarFilePath(); + if (argentUrl != null) { + File jarFile = new File(replaceJarFullPath(argentUrl)); + if (jarFile.exists()) { + return jarFile.getParentFile(); + } + } + return null; + } + + public static JarFile getJarFileByPath(URL jarFullPath) { + if (jarFullPath != null) { + try { + return new JarFile(replaceJarFullPath(jarFullPath)); + } catch (IOException localIOException) { + } + } + return null; + } + + private static String replaceJarFullPath(URL jarFullPath) { + if (jarFullPath == null) { + return null; + } + try { + return URLDecoder.decode(jarFullPath.getFile().replace("+", "%2B"), "UTF-8"); + } catch (IOException localIOException) { + } + return null; + } + +} diff --git a/src/main/java/com/alibaba/ttl/classloader/TtlClassCache.java b/src/main/java/com/alibaba/ttl/classloader/TtlClassCache.java new file mode 100644 index 0000000000000000000000000000000000000000..adcc1fe7b5bbe8f153bd0cce1cb6e306008291da --- /dev/null +++ b/src/main/java/com/alibaba/ttl/classloader/TtlClassCache.java @@ -0,0 +1,39 @@ +package com.alibaba.ttl.classloader; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +//c +public class TtlClassCache { + + private Map> classCacheMap = new ConcurrentHashMap>(); + + public T loadClassByName(ClassLoader paramClassLoader, String className) { + Map classLoaderKeyAndClassValueMap = (Map) this.classCacheMap.get(className); + if (classLoaderKeyAndClassValueMap != null) { + if (classLoaderKeyAndClassValueMap.containsKey(paramClassLoader)) { + return (T) classLoaderKeyAndClassValueMap.get(paramClassLoader); + } + ClassLoader parentClassLoader = paramClassLoader == null ? null : paramClassLoader.getParent(); + while ((parentClassLoader != null) && (!parentClassLoader.getClass().equals(ClassLoader.class))) { + if (classLoaderKeyAndClassValueMap.containsKey(parentClassLoader)) { + return (T) classLoaderKeyAndClassValueMap.get(parentClassLoader); + } + if (parentClassLoader.equals(paramClassLoader.getParent())) { + break; + } + parentClassLoader = paramClassLoader.getParent(); + } + } + return null; + } + + public void setClazzLoaderMap(ClassLoader paramClassLoader, String className, T clazz) { + if (!this.classCacheMap.containsKey(className)) { + this.classCacheMap.put(className, new HashMap()); + } + ((Map) this.classCacheMap.get(className)).put(paramClassLoader, clazz); + } + +} diff --git a/src/main/java/com/alibaba/ttl/classloader/TtlExtendLoader.java b/src/main/java/com/alibaba/ttl/classloader/TtlExtendLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..7780218bf622e39400b7b40f1de20e2d7dc2cd96 --- /dev/null +++ b/src/main/java/com/alibaba/ttl/classloader/TtlExtendLoader.java @@ -0,0 +1,111 @@ +package com.alibaba.ttl.classloader; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TtlExtendLoader { + + private static final TtlClassCache> LOAD_CLASS_TREE = new TtlClassCache>(); + + private static final Map CLASS_BYTE_MAP = new HashMap(30); + + private static final Logger logger = Logger.getLogger(TtlExtendLoader.class.getName()); + + public static boolean isExtendLoadClass(String className) { + if (CLASS_BYTE_MAP.isEmpty()) { + init(); + } + return CLASS_BYTE_MAP.containsKey(className); + } + + public static boolean isAgentLoadClass(String className) { + return className.startsWith("com.alibaba.ttl."); + } + + public static Class getClazz(ClassLoader paramClassLoader, String className) { + return (Class) LOAD_CLASS_TREE.loadClassByName(paramClassLoader, className); + } + + public static void setClazzLoaderMap(ClassLoader paramClassLoader, String className, Class paramClass) { + LOAD_CLASS_TREE.setClazzLoaderMap(paramClassLoader, className, paramClass); + } + + public static byte[] getClazzBytes(String paramString) { + if (CLASS_BYTE_MAP.isEmpty()) { + init(); + } + return (byte[]) CLASS_BYTE_MAP.get(paramString); + } + + private static final int EOF = -1; + + public static void init() { + synchronized (TtlExtendLoader.class) { + if (CLASS_BYTE_MAP.isEmpty()) { + JarInputStream jarInputStream = null; + try { + URL agentJarUrl = TtlAgentJarUtil.getAgentJarFilePath(); + JarFile jarfile = TtlAgentJarUtil.getJarFileByPath(agentJarUrl); + jarInputStream = new JarInputStream(agentJarUrl.openStream()); + JarEntry agentJarEntry = null; + while ((agentJarEntry = jarInputStream.getNextJarEntry()) != null) { + if (agentJarEntry.getName().endsWith(".class")) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(jarfile.getInputStream(agentJarEntry), output); + byte[] classBytes = output.toByteArray(); + final String className = agentJarEntry.getName() + .substring(0, agentJarEntry.getName().length() - 6).replace('/', '.'); + CLASS_BYTE_MAP.put(className, classBytes); + } + } + } catch (Exception t) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, "TTL exception when load class, cause: " + t.toString(), t); + } + } finally { + try { + if (jarInputStream != null) { + jarInputStream.close(); + } + } catch (IOException e) { + } + } + } + } + } + + public static int copy(InputStream input, OutputStream output) throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + public static long copyLarge(InputStream input, OutputStream output) throws IOException { + return copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]); + } + + public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException { + long count = 0; + int n = 0; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + +} diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistDynamicMutipleClassTransformlet.java b/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistDynamicMutipleClassTransformlet.java new file mode 100644 index 0000000000000000000000000000000000000000..878a7895c59b30012e7c66aee2b968d7d905b580 --- /dev/null +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistDynamicMutipleClassTransformlet.java @@ -0,0 +1,14 @@ +package com.alibaba.ttl.threadpool.agent; + +import java.io.IOException; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.NotFoundException; + +public interface JavassistDynamicMutipleClassTransformlet extends Transformlet { + + boolean needTransform(CtClass clazz, String className); + + void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException; +} diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistTransformlet.java b/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistTransformlet.java index 1eaeb0169a7e95e05174e2621e100c0c40bf3f68..0e22a55a14fd761ce823d93309da57d5a8024e46 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistTransformlet.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/JavassistTransformlet.java @@ -12,7 +12,8 @@ import java.io.IOException; * @author Jerry Lee (oldratlee at gmail dot com) * @since 2.5.1 */ -public interface JavassistTransformlet { +public interface JavassistTransformlet extends Transformlet { + boolean needTransform(String className); void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException; diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/Transformlet.java b/src/main/java/com/alibaba/ttl/threadpool/agent/Transformlet.java new file mode 100644 index 0000000000000000000000000000000000000000..e999361090e4eebe7f6a59496558e9fbd0ebbd08 --- /dev/null +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/Transformlet.java @@ -0,0 +1,5 @@ +package com.alibaba.ttl.threadpool.agent; + +public interface Transformlet { + +} diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java index dede118280089da4d0ee72a4df1276958da081cc..7d7a48490681c39e32026f5ed306e3f530a43598 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java @@ -1,38 +1,41 @@ package com.alibaba.ttl.threadpool.agent; - -import com.alibaba.ttl.threadpool.agent.transformlet.TtlExecutorTransformlet; -import com.alibaba.ttl.threadpool.agent.transformlet.TtlForkJoinTransformlet; - import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; +import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; +import com.alibaba.ttl.classloader.TtlAgentJarUtil; +import com.alibaba.ttl.threadpool.agent.transformlet.TtlClassloaderTransformlet; +import com.alibaba.ttl.threadpool.agent.transformlet.TtlExecutorTransformlet; +import com.alibaba.ttl.threadpool.agent.transformlet.TtlForkJoinTransformlet; + +public class TtlAgent { -/** - * TTL Java Agent. - * - * @author Jerry Lee (oldratlee at gmail dot com) - * @see The mechanism for instrumentation - * @since 0.9.0 - */ -public final class TtlAgent { private static final Logger logger = Logger.getLogger(TtlAgent.class.getName()); private TtlAgent() { throw new InstantiationError("Must not instantiate this class"); } - public static void premain(String agentArgs, Instrumentation inst) { + public static void premain(String agentArgs, Instrumentation paramInstrumentation) { try { - - logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst); - - @SuppressWarnings("unchecked") ClassFileTransformer transformer = new TtlTransformer(TtlExecutorTransformlet.class, TtlForkJoinTransformlet.class); - inst.addTransformer(transformer, true); + //even api can append agent jar to xbootclasspath,we still need enhance subclassloaders, + //because in tomcat's WebAppBaseClassLoader, our business class such as filter serlvet still can't load TransmittableThreadLocal + JarFile localJarFile = TtlAgentJarUtil.getJarFileByPath(TtlAgentJarUtil.getAgentJarFilePath()); + paramInstrumentation.appendToBootstrapClassLoaderSearch(localJarFile); + paramInstrumentation.appendToSystemClassLoaderSearch(localJarFile); + logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + paramInstrumentation); + + @SuppressWarnings("unchecked") + ClassFileTransformer transformer = new TtlTransformer( + TtlClassloaderTransformlet.class, + TtlExecutorTransformlet.class, + TtlForkJoinTransformlet.class); + + paramInstrumentation.addTransformer(transformer, true); logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success"); - logger.info("[TtlAgent.premain] end"); } catch (Exception e) { @@ -41,6 +44,8 @@ public final class TtlAgent { logger.log(Level.SEVERE, msg, e); } throw new IllegalStateException(msg, e); + } + } } diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java index 423766ddcfe9aa5f33e84197bfe49938788f4105..ae654ebe5b42227ab37c1c223f375b841234672f 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java @@ -25,15 +25,24 @@ public class TtlTransformer implements ClassFileTransformer { private static final byte[] EMPTY_BYTE_ARRAY = {}; - @SuppressWarnings("unchecked") - final List transformletList = new ArrayList(); + final List transformletList = new ArrayList(); - @SuppressWarnings("unchecked") - public TtlTransformer(Class... transformletClasses) throws Exception { - for (Class transformletClass : transformletClasses) { - final JavassistTransformlet transformlet = transformletClass.getConstructor().newInstance(); - transformletList.add(transformlet); + final List dynamicTransformletList + = new ArrayList(); + public TtlTransformer(Class... transformletClasses ) throws Exception { + for (Class transformletClass : transformletClasses) { + + final Transformlet transformlet = transformletClass.getConstructor().newInstance(); + + if(transformlet instanceof JavassistTransformlet){ + JavassistTransformlet jtransformlet =(JavassistTransformlet) transformlet; + transformletList.add(jtransformlet); + }else if(transformlet instanceof JavassistDynamicMutipleClassTransformlet){ + JavassistDynamicMutipleClassTransformlet dynamictransformlet =(JavassistDynamicMutipleClassTransformlet) transformlet; + dynamicTransformletList.add(dynamictransformlet); + } + logger.info("[TtlTransformer] add Transformlet " + transformletClass + " success"); } } @@ -48,6 +57,16 @@ public class TtlTransformer implements ClassFileTransformer { } final String className = toClassName(classFile); + + for (JavassistDynamicMutipleClassTransformlet dynamicTransformlet : dynamicTransformletList) { + final CtClass clazz = getCtClass(classFileBuffer, loader); + if (dynamicTransformlet.needTransform(clazz, className )) { + logger.info("Dynamic Transforming class " + className); + dynamicTransformlet.doTransform(clazz); + return clazz.toBytecode(); + } + } + for (JavassistTransformlet transformlet : transformletList) { if (transformlet.needTransform(className)) { logger.info("Transforming class " + className); @@ -56,6 +75,7 @@ public class TtlTransformer implements ClassFileTransformer { return clazz.toBytecode(); } } + } catch (Throwable t) { String msg = "Fail to transform class " + classFile + ", cause: " + t.toString(); if (logger.isLoggable(Level.SEVERE)) { diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/transformlet/TtlClassloaderTransformlet.java b/src/main/java/com/alibaba/ttl/threadpool/agent/transformlet/TtlClassloaderTransformlet.java new file mode 100644 index 0000000000000000000000000000000000000000..0d4febb8bddcd4db9cd2d81c9e895ea9a274b869 --- /dev/null +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/transformlet/TtlClassloaderTransformlet.java @@ -0,0 +1,102 @@ +package com.alibaba.ttl.threadpool.agent.transformlet; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + +import com.alibaba.ttl.threadpool.agent.JavassistDynamicMutipleClassTransformlet; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewMethod; +import javassist.NotFoundException; + +public class TtlClassloaderTransformlet implements JavassistDynamicMutipleClassTransformlet { + + private static final Logger logger = Logger.getLogger(TtlClassloaderTransformlet.class.getName()); + + @Override + public boolean needTransform(CtClass clazz, String className) { + List superClassList = new ArrayList(); + addAllSuperClass(clazz, superClassList); + + //All the sub class of java.lang.ClassLoader will be enhanced + //Here we enhance ClassLoader's sub classes, + //But in fact, only 3rd part sub classes such as Tomcat WebAppBaseClassLoader can be enhanced + //In jdk, bootstrap ext app, this 3 classloaders can't be enhanced + //I found this hint by system.out.prinln all the class in premain + //This hints that jdk's own 3 classloader can't be enhanced and it is already loader, + //JDK's own classloaders is loaded before agent started + for (Iterator iterator = superClassList.iterator(); iterator.hasNext();) { + CtClass ctClass = (CtClass) iterator.next(); + if ("java.lang.ClassLoader".equals(ctClass.getName())) { + return true; + } + } + return false; + } + + @Override + public void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException { + final String className = clazz.getName(); + // enhance two loadClass method + // if subclass doesn't have any load class method override, then ignore it + // as method does not search the superclasses, so we only modify subclass + String loadClass_methodName = "loadClass"; + final CtMethod[] loadClassMethods = clazz.getDeclaredMethods(loadClass_methodName); + if (loadClassMethods != null) { + for (CtMethod loadClassMethod : loadClassMethods) { + final CtMethod new_loadClassMethod = CtNewMethod.copy(loadClassMethod, loadClass_methodName, clazz, + null); + final String original_load_method_rename = "original$loadClass$method$renamed$by$ttl"; + loadClassMethod.setName(original_load_method_rename);// rename loadClass + + CtClass[] parameterTypes = loadClassMethod.getParameterTypes(); + String codeWeaveParameter = ((parameterTypes.length == 1) ? ("$1") : ("$1,$2")); + + final String code = "{\n" + + "boolean isAgentClass = com.alibaba.ttl.classloader.TtlExtendLoader.isAgentLoadClass($1);\n"+ + "if(isAgentClass){\n"+ + " boolean isExtendloadedClass = com.alibaba.ttl.classloader.TtlExtendLoader.isExtendLoadClass($1);\n"+ + " if(isExtendloadedClass){\n " + + " Class clazzTtlInner = com.alibaba.ttl.classloader.TtlExtendLoader.getClazz(this, $1);\n "+ + " if(clazzTtlInner==null){\n "+ + " byte[] clazzBytes = com.alibaba.ttl.classloader.TtlExtendLoader.getClazzBytes($1);\n"+ + " if(clazzBytes==null){ return " + original_load_method_rename + "(" + codeWeaveParameter + "); }\n "+ + " Class resultClass = this.defineClass($1, clazzBytes, 0, clazzBytes.length);\n "+ + " com.alibaba.ttl.classloader.TtlExtendLoader.setClazzLoaderMap(this, $1, resultClass ); \n "+ + " return resultClass;\n "+ + " }else{\n"+ + " return clazzTtlInner;\n"+ + " }\n "+ + " }else{\n"+ + " return " + original_load_method_rename + "(" + codeWeaveParameter + "); \n "+ + " }\n"+ + " }else{ return " + original_load_method_rename + "(" + codeWeaveParameter + "); }\n" + + "}\n"; + + new_loadClassMethod.setBody(code); + clazz.addMethod(new_loadClassMethod); + logger.info( + "insert code around method " + new_loadClassMethod + " of class " + className + ": " + code); + } + + } + } + + private void addAllSuperClass(CtClass clazz, List superClassList) { + CtClass supserClazz = null; + try { + supserClazz = clazz.getSuperclass(); + } catch (NotFoundException e) { + } + + if (supserClazz != null) { + superClassList.add(supserClazz); + addAllSuperClass(supserClazz, superClassList); + } + } +}