From 4280aa93ee2f32d964e7cbca91eff1d2af1b180b Mon Sep 17 00:00:00 2001 From: Woonduk Kang Date: Tue, 4 Sep 2012 07:02:01 +0000 Subject: [PATCH] =?UTF-8?q?[=EA=B0=95=EC=9A=B4=EB=8D=95]=20[LUCYSUS-1744]?= =?UTF-8?q?=20mysql=20interceptor=20=EC=B6=94=EA=B0=80,=20testcase?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EC=9D=B4=20=EA=B0=80=EB=8A=A5=ED=95=9C=20tes?= =?UTF-8?q?tclassloader=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-tomcat-profiler/trunk@569 84d0f5b1-2673-498c-a247-62c4ff18d310 --- .../interceptor/InterceptorRegistry.java | 4 +- .../interceptor/bci/InstrumentClass.java | 2 + .../bci/JavaAssistByteCodeInstrumentor.java | 3 + .../interceptor/bci/JavaAssistClass.java | 86 ++++++++++++++----- .../com/profiler/interceptor/bci/Type.java | 5 ++ .../profiler/modifier/AbstractModifier.java | 2 +- .../profiler/modifier/db/ConnectionTrace.java | 31 +++++++ .../db/mysql/MySQLConnectionImplModifier.java | 36 +++++--- .../CloseConnectionInterceptor.java | 27 ++++++ .../CreateConnectionInterceptor.java | 26 ++++++ .../CreateStatementInterceptor.java | 17 ++++ .../ExecuteQueryMethodInterceptor.java | 4 +- .../com/profiler/util/InterceptorUtils.java | 14 +++ .../MySQLConnectionImplModifierTest.java | 59 +++++++++++++ .../profiler/util/InstrumentTranslator.java | 43 ++++++++++ .../com/profiler/util/TestClassLoader.java | 73 ++++++++++++++++ 16 files changed, 397 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/profiler/interceptor/bci/Type.java create mode 100644 src/main/java/com/profiler/modifier/db/ConnectionTrace.java create mode 100644 src/main/java/com/profiler/modifier/db/mysql/interceptors/CloseConnectionInterceptor.java create mode 100644 src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateConnectionInterceptor.java create mode 100644 src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java create mode 100644 src/main/java/com/profiler/util/InterceptorUtils.java create mode 100644 src/test/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifierTest.java create mode 100644 src/test/java/com/profiler/util/InstrumentTranslator.java create mode 100644 src/test/java/com/profiler/util/TestClassLoader.java diff --git a/src/main/java/com/profiler/interceptor/InterceptorRegistry.java b/src/main/java/com/profiler/interceptor/InterceptorRegistry.java index df3c4d438..c25a89860 100644 --- a/src/main/java/com/profiler/interceptor/InterceptorRegistry.java +++ b/src/main/java/com/profiler/interceptor/InterceptorRegistry.java @@ -9,8 +9,6 @@ public class InterceptorRegistry { private final AtomicInteger id = new AtomicInteger(0); private final Interceptor[] index; - // private final ConcurrentMap> nameToIndex = new - // ConcurrentHashMap>(); public static final InterceptorRegistry REGISTRY = new InterceptorRegistry(); @@ -23,6 +21,7 @@ public class InterceptorRegistry { this.index = new Interceptor[max]; } + int addInterceptor0(Interceptor interceptor) { if (interceptor == null) { return -1; @@ -47,4 +46,5 @@ public class InterceptorRegistry { public static Interceptor getInterceptor(int key) { return REGISTRY.getInterceptor0(key); } + } diff --git a/src/main/java/com/profiler/interceptor/bci/InstrumentClass.java b/src/main/java/com/profiler/interceptor/bci/InstrumentClass.java index a6c71d5a8..4e494fcc0 100644 --- a/src/main/java/com/profiler/interceptor/bci/InstrumentClass.java +++ b/src/main/java/com/profiler/interceptor/bci/InstrumentClass.java @@ -5,6 +5,8 @@ import com.profiler.interceptor.Interceptor; public interface InstrumentClass { boolean addInterceptor(String methodName, String[] args, Interceptor interceptor); + boolean addInterceptor(String methodName, String[] args, Interceptor interceptor, Type type); + boolean addDebugLogBeforeAfterMethod(); boolean addDebugLogBeforeAfterConstructor(); diff --git a/src/main/java/com/profiler/interceptor/bci/JavaAssistByteCodeInstrumentor.java b/src/main/java/com/profiler/interceptor/bci/JavaAssistByteCodeInstrumentor.java index 078debf05..97e2d0a28 100644 --- a/src/main/java/com/profiler/interceptor/bci/JavaAssistByteCodeInstrumentor.java +++ b/src/main/java/com/profiler/interceptor/bci/JavaAssistByteCodeInstrumentor.java @@ -118,6 +118,9 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor { logger.info("Loaded " + filePath + " library."); } } catch (NotFoundException e) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, "lib not fail. " + e.getMessage(), e); + } } } } diff --git a/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java b/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java index 7ee813503..72c9804f2 100644 --- a/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java +++ b/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java @@ -30,21 +30,42 @@ public class JavaAssistClass implements InstrumentClass { this.ctClass = ctClass; } - @Override + @Override public boolean addInterceptor(String methodName, String[] args, Interceptor interceptor) { + return addInterceptor(methodName, args, interceptor, Type.auto); + } + + @Override + public boolean addInterceptor(String methodName, String[] args, Interceptor interceptor, Type type) { if (interceptor == null) return false; - + + CtMethod method = getMethod(methodName, args); + if(method == null) { + return false; + } + int id = InterceptorRegistry.addInterceptor(interceptor); try { - CtMethod method = getMethod(methodName, args); - if (interceptor instanceof StaticAroundInterceptor) { - addAroundInterceptor(methodName, id, method); - } else if (interceptor instanceof StaticBeforeInterceptor) { - addStaticBeforeInterceptor(methodName, id, method); - } else if (interceptor instanceof StaticAfterInterceptor) { - addStaticAfterInterceptor(methodName, id, method); - } + if(type == Type.auto) { + if (interceptor instanceof StaticAroundInterceptor) { + addStaticAroundInterceptor(methodName, id, method); + } else if (interceptor instanceof StaticBeforeInterceptor) { + addStaticBeforeInterceptor(methodName, id, method); + } else if (interceptor instanceof StaticAfterInterceptor) { + addStaticAfterInterceptor(methodName, id, method); + } else { + return false; + } + } else if(type == Type.around && interceptor instanceof StaticAroundInterceptor) { + addStaticAroundInterceptor(methodName, id, method); + } else if(type == Type.before && interceptor instanceof StaticBeforeInterceptor) { + addStaticBeforeInterceptor(methodName, id, method); + } else if(type == Type.after && interceptor instanceof StaticAfterInterceptor) { + addStaticAfterInterceptor(methodName, id, method); + } else { + return false; + } return true; } catch (NotFoundException e) { if (logger.isLoggable(Level.WARNING)) { @@ -58,7 +79,7 @@ public class JavaAssistClass implements InstrumentClass { return false; } - private void addAroundInterceptor(String methodName, int id, CtBehavior method) throws NotFoundException, CannotCompileException { + private void addStaticAroundInterceptor(String methodName, int id, CtBehavior method) throws NotFoundException, CannotCompileException { addStaticBeforeInterceptor(methodName, id, method); addStaticAfterInterceptor(methodName, id, method); } @@ -67,7 +88,8 @@ public class JavaAssistClass implements InstrumentClass { StringBuilder after = new StringBuilder(1024); after.append("{"); addGetStaticAfterInterceptor(after, id); - after.append(" interceptor.after(this, \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, ($w)$_);"); + String target = getTarget(behavior); + after.append(" interceptor.after(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, ($w)$_);"); after.append("}"); String buildAfter = after.toString(); if (logger.isLoggable(Level.INFO)) { @@ -78,7 +100,7 @@ public class JavaAssistClass implements InstrumentClass { StringBuilder catchCode = new StringBuilder(1024); catchCode.append("{"); addGetStaticAfterInterceptor(catchCode, id); - catchCode.append(" interceptor.after(this, \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, $e);"); + catchCode.append(" interceptor.after(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, $e);"); catchCode.append(" throw $e;"); catchCode.append("}"); String buildCatch = catchCode.toString(); @@ -90,7 +112,21 @@ public class JavaAssistClass implements InstrumentClass { } - private void addGetStaticAfterInterceptor(StringBuilder after, int id) { + private String getTarget(CtBehavior behavior) { + boolean staticMethod = isStatic(behavior); + if(staticMethod) { + return "null"; + } else { + return "this"; + } + } + + private boolean isStatic(CtBehavior behavior) { + int modifiers = behavior.getModifiers(); + return java.lang.reflect.Modifier.isStatic(modifiers); + } + + private void addGetStaticAfterInterceptor(StringBuilder after, int id) { after.append(" com.profiler.interceptor.StaticAfterInterceptor interceptor = " + "(com.profiler.interceptor.StaticAfterInterceptor) com.profiler.interceptor.InterceptorRegistry.getInterceptor("); after.append(id); after.append(");"); @@ -100,7 +136,8 @@ public class JavaAssistClass implements InstrumentClass { StringBuilder code = new StringBuilder(1024); code.append("{"); addGetBeforeInterceptor(id, code); - code.append(" interceptor.before(this, \"" + ctClass.getName() + "\", \"" + methodName + "\", $args);"); + String target = getTarget(behavior); + code.append(" interceptor.before(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args);"); code.append("}"); String buildBefore = code.toString(); if (logger.isLoggable(Level.INFO)) { @@ -139,7 +176,7 @@ public class JavaAssistClass implements InstrumentClass { // TODO method의 prameter type을 interceptor에 별도 추가해야 될것으로 보임. String params = getParamsToString(method.getParameterTypes()); - addAroundInterceptor(methodName, id, method); + addStaticAroundInterceptor(methodName, id, method); } return true; } catch (Exception e) { @@ -181,7 +218,7 @@ public class JavaAssistClass implements InstrumentClass { // constructorName + " Constructor:Param=(" + params + // ") is finished.\"); throw $e; }" // , instrumentor.getClassPool().get("java.lang.Throwable")); - addAroundInterceptor(constructorName, id, constructor); + addStaticAroundInterceptor(constructorName, id, constructor); } return true; } catch (Exception e) { @@ -207,10 +244,17 @@ public class JavaAssistClass implements InstrumentClass { return paramsStr; } - private CtMethod getMethod(String methodName, String[] args) throws NotFoundException { - CtClass[] params = getCtParameter(args); - return ctClass.getDeclaredMethod(methodName, params); - } + private CtMethod getMethod(String methodName, String[] args) { + try { + CtClass[] params = getCtParameter(args); + return ctClass.getDeclaredMethod(methodName, params); + } catch (NotFoundException e) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, e.getMessage(), e); + } + } + return null; + } private CtClass[] getCtParameter(String[] args) throws NotFoundException { if (args == null) { diff --git a/src/main/java/com/profiler/interceptor/bci/Type.java b/src/main/java/com/profiler/interceptor/bci/Type.java new file mode 100644 index 000000000..e8f1c00ad --- /dev/null +++ b/src/main/java/com/profiler/interceptor/bci/Type.java @@ -0,0 +1,5 @@ +package com.profiler.interceptor.bci; + +public enum Type { + around, before, after, auto +} diff --git a/src/main/java/com/profiler/modifier/AbstractModifier.java b/src/main/java/com/profiler/modifier/AbstractModifier.java index 4431aa777..b7470dc46 100644 --- a/src/main/java/com/profiler/modifier/AbstractModifier.java +++ b/src/main/java/com/profiler/modifier/AbstractModifier.java @@ -34,7 +34,7 @@ public abstract class AbstractModifier implements Modifier { protected Interceptor newInterceptor(ClassLoader classLoader, ProtectionDomain protectedDomain, String interceptorFQCN) { Class aClass = this.byteCodeInstrumentor.defineClass(classLoader, interceptorFQCN, protectedDomain); try { - return (Interceptor) aClass.newInstance(); + return (Interceptor) aClass.newInstance(); } catch (InstantiationException e) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, e.getMessage(), e); diff --git a/src/main/java/com/profiler/modifier/db/ConnectionTrace.java b/src/main/java/com/profiler/modifier/db/ConnectionTrace.java new file mode 100644 index 000000000..644d59751 --- /dev/null +++ b/src/main/java/com/profiler/modifier/db/ConnectionTrace.java @@ -0,0 +1,31 @@ +package com.profiler.modifier.db; + +import java.sql.Connection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ConnectionTrace { + + public ConcurrentMap connectionMap = new ConcurrentHashMap(); + + private static ConnectionTrace CONNECTION_TRACE = new ConnectionTrace(); + + public static ConnectionTrace getConnectionTrace() { + return CONNECTION_TRACE; + } + + public void createConnection(Connection connection, String url) { + this.connectionMap.put(connection, url); + } + + + public void closeConnection(Connection connection) { + this.connectionMap.remove(connection); + } + + public Set getConnectionList() { + return connectionMap.keySet(); + } +} diff --git a/src/main/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifier.java b/src/main/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifier.java index 9e2e02094..90552a9f2 100644 --- a/src/main/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifier.java +++ b/src/main/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifier.java @@ -1,6 +1,8 @@ package com.profiler.modifier.db.mysql; +import com.profiler.interceptor.Interceptor; import com.profiler.interceptor.bci.ByteCodeInstrumentor; +import com.profiler.interceptor.bci.InstrumentClass; import javassist.CtClass; import javassist.CtMethod; @@ -29,20 +31,33 @@ public class MySQLConnectionImplModifier extends AbstractModifier { logger.info("Modifing. " + javassistClassName); } checkLibrary(classLoader, javassistClassName); - return changeMethods(javassistClassName, classFileBuffer); - } - - private byte[] changeMethods(String javassistClassName, byte[] classfileBuffer) { try { - CtClass cc = classPool.get(javassistClassName); - - updateGetInstanceMethod(cc); - updateCreateStatementMethod(cc); - updateCloseMethod(cc); + InstrumentClass mysqlConnection = byteCodeInstrumentor.getClass(javassistClassName); + if (mysqlConnection == null) { + return null; + } + String[] params = new String[] { + "java.lang.String", "int", "java.util.Properties", "java.lang.String", "java.lang.String" + }; + Interceptor createConnection = newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.mysql.interceptors.CreateConnectionInterceptor"); + if (createConnection == null) { + return null; + } + Interceptor closeConnection = newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.mysql.interceptors.CloseConnectionInterceptor"); + if (closeConnection == null) { + return null; + } + Interceptor createStatement = newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.mysql.interceptors.CreateStatementInterceptor"); + if (createStatement == null) { + return null; + } + mysqlConnection.addInterceptor("getInstance", params, createConnection); + mysqlConnection.addInterceptor("close", null, closeConnection); + mysqlConnection.addInterceptor("createStatement", null, createStatement); printClassConvertComplete(javassistClassName); - return cc.toBytecode(); + return mysqlConnection.toBytecode(); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, e.getMessage(), e); @@ -51,6 +66,7 @@ public class MySQLConnectionImplModifier extends AbstractModifier { return null; } + private void updateCreateStatementMethod(CtClass cc) throws Exception { CtMethod method = cc.getDeclaredMethod("createStatement", null); method.insertAfter("{" + DatabaseRequestTracer.FQCN + ".put(" + TomcatProfilerConstant.REQ_DATA_TYPE_DB_CREATE_STATEMENT + "); }"); diff --git a/src/main/java/com/profiler/modifier/db/mysql/interceptors/CloseConnectionInterceptor.java b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CloseConnectionInterceptor.java new file mode 100644 index 000000000..344ec47fc --- /dev/null +++ b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CloseConnectionInterceptor.java @@ -0,0 +1,27 @@ +package com.profiler.modifier.db.mysql.interceptors; + +import com.profiler.interceptor.StaticAfterInterceptor; +import com.profiler.interceptor.StaticAroundInterceptor; +import com.profiler.interceptor.StaticBeforeInterceptor; +import com.profiler.modifier.db.ConnectionTrace; +import com.profiler.util.InterceptorUtils; + +import java.sql.Connection; + + +public class CloseConnectionInterceptor implements StaticBeforeInterceptor { + + public void before(Object target, String className, String methodName, Object[] args) { + if(!(target instanceof Connection)) { + return; + } + + ConnectionTrace connectionTrace = ConnectionTrace.getConnectionTrace(); + connectionTrace.closeConnection((Connection) target); + + } + + + + +} diff --git a/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateConnectionInterceptor.java b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateConnectionInterceptor.java new file mode 100644 index 000000000..c03abd7c4 --- /dev/null +++ b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateConnectionInterceptor.java @@ -0,0 +1,26 @@ +package com.profiler.modifier.db.mysql.interceptors; + +import com.profiler.interceptor.StaticAfterInterceptor; +import com.profiler.modifier.db.ConnectionTrace; +import com.profiler.util.InterceptorUtils; + +import java.sql.Connection; + +public class CreateConnectionInterceptor implements StaticAfterInterceptor { + + @Override + public void after(Object target, String className, String methodName, Object[] args, Object result) { + if (InterceptorUtils.isThrowable(result)) { + return; + } + + if (!(result instanceof Connection)) { + return; + } + String url = (String) args[4]; + if (url instanceof String) { + ConnectionTrace connectionTrace = ConnectionTrace.getConnectionTrace(); + connectionTrace.createConnection((Connection)result, url); + } + } +} diff --git a/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java new file mode 100644 index 000000000..f5b1ac336 --- /dev/null +++ b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java @@ -0,0 +1,17 @@ +package com.profiler.modifier.db.mysql.interceptors; + +import com.profiler.context.Trace; +import com.profiler.interceptor.StaticAfterInterceptor; +import com.profiler.interceptor.StaticBeforeInterceptor; + +import java.sql.Connection; + +public class CreateStatementInterceptor implements StaticAfterInterceptor { + + @Override + public void after(Object target, String className, String methodName, Object[] args, Object result) { + if (Trace.getCurrentTraceId() == null) { + return; + } + } +} diff --git a/src/main/java/com/profiler/modifier/db/mysql/interceptors/ExecuteQueryMethodInterceptor.java b/src/main/java/com/profiler/modifier/db/mysql/interceptors/ExecuteQueryMethodInterceptor.java index cad45e8c3..62bc7db13 100644 --- a/src/main/java/com/profiler/modifier/db/mysql/interceptors/ExecuteQueryMethodInterceptor.java +++ b/src/main/java/com/profiler/modifier/db/mysql/interceptors/ExecuteQueryMethodInterceptor.java @@ -5,6 +5,8 @@ import com.profiler.context.Annotation; import com.profiler.context.Trace; import com.profiler.interceptor.StaticAroundInterceptor; +import java.net.URL; + /** * * @author netspider @@ -22,7 +24,7 @@ public class ExecuteQueryMethodInterceptor implements StaticAroundInterceptor { return; } - Trace.recordRpcName("mysql", ""); + Trace.recordRpcName("mysql", "url*****"); // // TODO: add destination address diff --git a/src/main/java/com/profiler/util/InterceptorUtils.java b/src/main/java/com/profiler/util/InterceptorUtils.java new file mode 100644 index 000000000..65a7e0f7d --- /dev/null +++ b/src/main/java/com/profiler/util/InterceptorUtils.java @@ -0,0 +1,14 @@ +package com.profiler.util; + +public class InterceptorUtils { + public static boolean isThrowable(Object result) { + if (result instanceof Throwable) { + return true; + } + return false; + } + + public static boolean isSuccess(Object result) { + return !isThrowable(result); + } +} diff --git a/src/test/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifierTest.java b/src/test/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifierTest.java new file mode 100644 index 000000000..4642cda36 --- /dev/null +++ b/src/test/java/com/profiler/modifier/db/mysql/MySQLConnectionImplModifierTest.java @@ -0,0 +1,59 @@ +package com.profiler.modifier.db.mysql; + +import com.profiler.modifier.db.ConnectionTrace; +import com.profiler.util.TestClassLoader; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.Driver; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Logger; + +public class MySQLConnectionImplModifierTest { + + private final Logger logger = Logger.getLogger(MySQLConnectionImplModifierTest.class.getName()); + + private TestClassLoader loader; + @Before + public void setUp() throws Exception { + loader = new TestClassLoader(); + + MySQLConnectionImplModifier modifier = new MySQLConnectionImplModifier(loader.getInstrumentor()); + loader.addModifier(modifier); + + +// loader.delegateLoadingOf(ConnectionTrace.class.getName()); + + loader.initialize(); + } + + @Test + public void testModify() throws Exception { + + Class driverClazz = (Class) loader.loadClass("com.mysql.jdbc.NonRegisteringDriver"); + Driver driver = driverClazz.newInstance(); + logger.info("Driver class name:" + driverClazz.getName()); + logger.info("Driver class cl:" + driverClazz.getClassLoader()); + + Properties properties = new Properties(); + properties.setProperty("user", "lucytest"); + properties.setProperty("password", "testlucy"); + Connection connect = driver.connect("jdbc:mysql://10.98.133.22:3306/hippo", properties); + + logger.info("Connection class name:" + connect.getClass().getName()); + logger.info("Connection class cl:" + connect.getClass().getClassLoader()); + + Set connectionList = ConnectionTrace.getConnectionTrace().getConnectionList(); + Assert.assertEquals(connectionList.size(), 1); + logger.info("connection size:" + connectionList.size()); + + connect.close(); + Assert.assertEquals(connectionList.size(), 0); + logger.info("connection size:" + connectionList.size()); + + } + +} diff --git a/src/test/java/com/profiler/util/InstrumentTranslator.java b/src/test/java/com/profiler/util/InstrumentTranslator.java new file mode 100644 index 000000000..1652c8a92 --- /dev/null +++ b/src/test/java/com/profiler/util/InstrumentTranslator.java @@ -0,0 +1,43 @@ +package com.profiler.util; + +import com.profiler.modifier.Modifier; +import javassist.*; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InstrumentTranslator implements Translator { + private final Logger logger = Logger.getLogger(InstrumentTranslator.class.getName()); + + private ConcurrentMap modifierMap = new ConcurrentHashMap(); + + private ClassLoader loader; + + public InstrumentTranslator(ClassLoader loader) { + this.loader = loader; + } + + public Modifier addModifier(Modifier modifier) { + return modifierMap.put(modifier.getTargetClass().replace('/', '.'), modifier); + } + + @Override + public void start(ClassPool pool) throws NotFoundException, CannotCompileException { +// this.pool = pool; + } + + @Override + public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException { + if(logger.isLoggable(Level.FINE)) { + logger.fine("loading className:" + classname); + } + + Modifier modifier = modifierMap.get(classname); + if(modifier == null) { + return; + } + modifier.modify(this.loader, classname, null, null); + } +} diff --git a/src/test/java/com/profiler/util/TestClassLoader.java b/src/test/java/com/profiler/util/TestClassLoader.java new file mode 100644 index 000000000..6c25f7c00 --- /dev/null +++ b/src/test/java/com/profiler/util/TestClassLoader.java @@ -0,0 +1,73 @@ +package com.profiler.util; + +import com.profiler.interceptor.*; +import com.profiler.interceptor.bci.ByteCodeInstrumentor; +import com.profiler.interceptor.bci.JavaAssistByteCodeInstrumentor; +import com.profiler.modifier.Modifier; +import com.profiler.modifier.db.ConnectionTrace; +import javassist.CannotCompileException; +import javassist.Loader; +import javassist.NotFoundException; + +import java.util.logging.Logger; + +public class TestClassLoader extends Loader { + private final Logger logger = Logger.getLogger(TestClassLoader.class.getName()); + + private ByteCodeInstrumentor instrumentor; + private InstrumentTranslator instrumentTranslator; + + + public TestClassLoader() { + this.instrumentor = new JavaAssistByteCodeInstrumentor(); + this.instrumentTranslator = new InstrumentTranslator(this); + } + + public void initialize() { + addDefaultDelegateLoadingOf(); + addTranslator(); + } + + public ByteCodeInstrumentor getInstrumentor() { + return instrumentor; + } + + public Modifier addModifier(Modifier modifier){ + return this.instrumentTranslator.addModifier(modifier); + } + + private void addDefaultDelegateLoadingOf() { + this.delegateLoadingOf(Interceptor.class.getName()); + this.delegateLoadingOf(StaticAroundInterceptor.class.getName()); + this.delegateLoadingOf(StaticBeforeInterceptor.class.getName()); + this.delegateLoadingOf(StaticAfterInterceptor.class.getName()); + this.delegateLoadingOf(InterceptorRegistry.class.getName()); + this.delegateLoadingOf(ConnectionTrace.class.getName()); + } + + @Override + protected Class loadClassByDelegation(String name) throws ClassNotFoundException { + return super.loadClassByDelegation(name); + } + + private void addTranslator() { + try { + addTranslator(instrumentor.getClassPool(), instrumentTranslator); + } catch (NotFoundException e) { + throw new RuntimeException(e.getMessage(), e); + } catch (CannotCompileException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public void runTest(String className, String methodName) throws Throwable { + Class c = loadClass(className); + Object o = c.newInstance(); + try { + c.getDeclaredMethod(methodName, null).invoke(o, null); + } + catch (java.lang.reflect.InvocationTargetException e) { + throw e.getTargetException(); + } + } +}