[강운덕] [LUCYSUS-1744] mysql interceptor 추가, testcase작성이 가능한 testclassloader 추가.

git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-tomcat-profiler/trunk@569 84d0f5b1-2673-498c-a247-62c4ff18d310
This commit is contained in:
Woonduk Kang
2012-09-04 07:02:01 +00:00
parent 19f6a9dca7
commit 4280aa93ee
16 changed files with 397 additions and 35 deletions
@@ -9,8 +9,6 @@ public class InterceptorRegistry {
private final AtomicInteger id = new AtomicInteger(0);
private final Interceptor[] index;
// private final ConcurrentMap<String, List<Integer>> nameToIndex = new
// ConcurrentHashMap<String, List<Integer>>();
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);
}
}
@@ -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();
@@ -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);
}
}
}
}
@@ -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) {
@@ -0,0 +1,5 @@
package com.profiler.interceptor.bci;
public enum Type {
around, before, after, auto
}
@@ -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);
@@ -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<Connection, String> connectionMap = new ConcurrentHashMap<Connection, String>();
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<Connection> getConnectionList() {
return connectionMap.keySet();
}
}
@@ -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 + "); }");
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
@@ -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
@@ -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);
}
}