/* * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package scouter.agent.plugin; import scouter.agent.Configure; import scouter.agent.Logger; import scouter.agent.trace.HookArgs; import scouter.agent.trace.HookReturn; import scouter.agent.trace.TraceSQL; import scouter.javassist.*; import scouter.util.FileUtil; import scouter.util.Hexa32; import scouter.util.StringUtil; import scouter.util.ThreadUtil; import java.io.BufferedReader; import java.io.File; import java.io.StringReader; import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; public class PluginLoader extends Thread { private static PluginLoader instance; public synchronized static PluginLoader getInstance() { if (instance == null) { instance = new PluginLoader(); instance.setDaemon(true); instance.setName(ThreadUtil.getName(PluginLoader.class)); instance.start(); } return instance; } public void run() { while (true) { try { File root = Configure.getInstance().plugin_dir; reloadIfModified(root); } catch (Throwable t) { Logger.println("A160", t.toString()); } ThreadUtil.sleep(5000); } } private void reloadIfModified(File root) { File script = new File(root, "service.plug"); if (script.canRead() == false) { PluginAppServiceTrace.plugIn = null; } else { if (PluginAppServiceTrace.plugIn == null || PluginAppServiceTrace.plugIn.lastModified != script.lastModified()) { PluginAppServiceTrace.plugIn = createAppService(script); } } script = new File(root, "httpservice.plug"); if (script.canRead() == false) { PluginHttpServiceTrace.plugIn = null; } else { if (PluginHttpServiceTrace.plugIn == null || PluginHttpServiceTrace.plugIn.lastModified != script.lastModified()) { PluginHttpServiceTrace.plugIn = createHttpService(script); } } script = new File(root, "capture.plug"); if (script.canRead() == false) { PluginCaptureTrace.plugIn = null; } else { if (PluginCaptureTrace.plugIn == null || PluginCaptureTrace.plugIn.lastModified != script.lastModified()) { PluginCaptureTrace.plugIn = createICaptureTrace(script); } } script = new File(root, "springControllerCapture.plug"); if (script.canRead() == false) { PluginSpringControllerCaptureTrace.plugIn = null; } else { if (PluginSpringControllerCaptureTrace.plugIn == null || PluginSpringControllerCaptureTrace.plugIn.lastModified != script.lastModified()) { PluginSpringControllerCaptureTrace.plugIn = createICaptureTrace(script); } } script = new File(root, "jdbcpool.plug"); if (script.canRead() == false) { PluginJdbcPoolTrace.plugIn = null; } else { if (PluginJdbcPoolTrace.plugIn == null || PluginJdbcPoolTrace.plugIn.lastModified != script.lastModified()) { PluginJdbcPoolTrace.plugIn = createIJdbcPool(script); if (PluginJdbcPoolTrace.plugIn != null) { TraceSQL.clearUrlMap(); } } } script = new File(root, "httpcall.plug"); if (script.canRead() == false) { PluginHttpCallTrace.plugIn = null; } else { if (PluginHttpCallTrace.plugIn == null || PluginHttpCallTrace.plugIn.lastModified != script.lastModified()) { PluginHttpCallTrace.plugIn = createIHttpCall(script); } } } private long IHttpServiceCompile; private AbstractHttpService createHttpService(File script) { if (IHttpServiceCompile == script.lastModified()) return null; IHttpServiceCompile = script.lastModified(); try { HashMap<String, StringBuffer> bodyTable = loadFileText(script); String superName = AbstractHttpService.class.getName(); String className = "scouter.agent.plugin.impl.HttpServiceImpl"; String METHOD_START = "start"; String METHOD_END = "end"; String METHOD_REJECT = "reject"; String SIGNATURE = nativeName(WrContext.class) + nativeName(WrRequest.class) + nativeName(WrResponse.class); String METHOD_P1 = WrContext.class.getName(); String METHOD_P2 = WrRequest.class.getName(); String METHOD_P3 = WrResponse.class.getName(); if (bodyTable.containsKey(METHOD_START) == false) throw new CannotCompileException("no method body: " + METHOD_START); if (bodyTable.containsKey(METHOD_END) == false) throw new CannotCompileException("no method body: " + METHOD_END); if (bodyTable.containsKey(METHOD_REJECT) == false) throw new CannotCompileException("no method body: " + METHOD_REJECT); ClassPool cp = ClassPool.getDefault(); String jar = FileUtil.getJarFileName(PluginLoader.class); if (jar != null) { cp.appendClassPath(jar); } CtClass cc = cp.get(superName); CtClass impl = null; CtMethod method_start = null; CtMethod method_end = null; CtMethod method_reject = null; StringBuffer sb = null; try { impl = cp.get(className); impl.defrost(); // START METHOD method_start = impl.getMethod(METHOD_START, "(" + SIGNATURE + ")V"); // END METHOD method_end = impl.getMethod(METHOD_END, "(" + SIGNATURE + ")V"); // REJECT METHOD method_reject = impl.getMethod(METHOD_REJECT, "(" + SIGNATURE + ")Z"); } catch (scouter.javassist.NotFoundException e) { impl = cp.makeClass(className, cc); StringBuffer sb1 = new StringBuffer(); sb1.append(METHOD_P1).append(" p1").append(","); sb1.append(METHOD_P2).append(" p2").append(","); sb1.append(METHOD_P3).append(" p3"); // START METHOD sb = new StringBuffer(); sb.append("public void ").append(METHOD_START).append("(").append(sb1).append("){}"); method_start = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_start); // END METHOD sb = new StringBuffer(); sb.append("public void ").append(METHOD_END).append("(").append(sb1).append("){}"); method_end = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_end); // REJECT METHOD sb = new StringBuffer(); sb.append("public boolean ").append(METHOD_REJECT).append("(").append(sb1).append("){return false;}"); method_reject = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_reject); } StringBuffer bodyPrefix = new StringBuffer(); bodyPrefix.append("{"); bodyPrefix.append(METHOD_P1).append(" $ctx=$1;"); bodyPrefix.append(METHOD_P2).append(" $req=$2;"); bodyPrefix.append(METHOD_P3).append(" $res=$3;"); method_start.setBody( new StringBuffer().append(bodyPrefix).append(bodyTable.get(METHOD_START)).append("\n}").toString()); method_end.setBody( new StringBuffer().append(bodyPrefix).append(bodyTable.get(METHOD_END)).append("\n}").toString()); method_reject.setBody( new StringBuffer().append(bodyPrefix).append(bodyTable.get(METHOD_REJECT)).append("\n}").toString()); Class c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null); AbstractHttpService plugin = (AbstractHttpService) c.newInstance(); plugin.lastModified = script.lastModified(); Logger.println("PLUG-IN : " + AbstractHttpService.class.getName() + " loaded #" + Hexa32.toString32(plugin.hashCode())); return plugin; } catch (scouter.javassist.CannotCompileException ee) { Logger.println(ee.getMessage()); } catch (Throwable e) { Logger.println("A161", e); } return null; } private HashMap<String, StringBuffer> loadFileText(File script) { StringBuffer sb = new StringBuffer(); HashMap<String, StringBuffer> result = new HashMap<String, StringBuffer>(); String txt = new String(FileUtil.readAll(script)); try { BufferedReader r = new BufferedReader(new StringReader(txt)); while (true) { String line = StringUtil.trim(r.readLine()); if (line == null) break; if (line.startsWith("[") && line.endsWith("]")) { sb = new StringBuffer(); result.put(line.substring(1, line.length() - 1), sb); } else { sb.append(line).append("\n"); } } } catch (Exception e) { e.printStackTrace(); } return result; } private long IServiceTraceCompile; private AbstractAppService createAppService(File script) { if (IServiceTraceCompile == script.lastModified()) return null; IServiceTraceCompile = script.lastModified(); try { HashMap<String, StringBuffer> bodyTable = loadFileText(script); String superName = AbstractAppService.class.getName(); String className = "scouter.agent.plugin.impl.ServiceTraceImpl"; String START = "start"; String START_SIG = "(" + nativeName(WrContext.class) + nativeName(HookArgs.class) + ")V"; String START_P1 = WrContext.class.getName(); String START_P2 = HookArgs.class.getName(); StringBuffer START_BODY = bodyTable.get(START); if (START_BODY == null) throw new CannotCompileException("no method body: " + START); String END = "end"; String END_SIG = "(" + nativeName(WrContext.class) + ")V"; String END_P1 = WrContext.class.getName(); StringBuffer END_BODY = bodyTable.get(END); if (END_BODY == null) throw new CannotCompileException("no method body: " + END); ClassPool cp = ClassPool.getDefault(); String jar = FileUtil.getJarFileName(PluginLoader.class); if (jar != null) { cp.appendClassPath(jar); } Class c = null; CtClass cc = cp.get(superName); CtClass impl = null; StringBuffer sb; CtMethod method_start = null; CtMethod method_end = null; try { impl = cp.get(className); impl.defrost(); // START METHOD method_start = impl.getMethod(START, START_SIG); // END METHOD method_end = impl.getMethod(END, END_SIG); } catch (scouter.javassist.NotFoundException e) { impl = cp.makeClass(className, cc); // START METHOD sb = new StringBuffer(); sb.append("public void ").append(START).append("("); sb.append(START_P1).append(" p1 ").append(","); sb.append(START_P2).append(" p2"); sb.append("){}"); method_start = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_start); // END METHOD sb = new StringBuffer(); sb.append("public void ").append(END).append("("); sb.append(END_P1).append(" p1 "); sb.append("){}"); method_end = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_end); } sb = new StringBuffer(); sb.append("{"); sb.append(START_P1).append(" $ctx=$1;"); sb.append(START_P2).append(" $hook=$2;"); sb.append(START_BODY); sb.append("\n}"); method_start.setBody(sb.toString()); sb = new StringBuffer(); sb.append("{"); sb.append(END_P1).append(" $ctx=$1;"); sb.append(END_BODY); sb.append("\n}"); method_end.setBody(sb.toString()); c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null); AbstractAppService plugin = (AbstractAppService) c.newInstance(); plugin.lastModified = script.lastModified(); Logger.println("PLUG-IN : " + AbstractAppService.class.getName() + " loaded #" + Hexa32.toString32(plugin.hashCode())); return plugin; } catch (scouter.javassist.CannotCompileException ee) { Logger.println("PLUG-IN : " + ee.getMessage()); } catch (Exception e) { Logger.println("A162", e); } return null; } private long ICaptureCompile; private AbstractCapture createICaptureTrace(File script) { if (ICaptureCompile == script.lastModified()) return null; ICaptureCompile = script.lastModified(); try { HashMap<String, StringBuffer> bodyTable = loadFileText(script); String superName = AbstractCapture.class.getName(); String className = "scouter.agent.plugin.impl.CaptureImpl"; String ARG = "capArgs"; String ARG_SIG = "(" + nativeName(WrContext.class) + nativeName(HookArgs.class) + ")V"; String ARG_P1 = WrContext.class.getName(); String ARG_P2 = HookArgs.class.getName(); StringBuffer ARG_BODY = bodyTable.get("args"); String RTN = "capReturn"; String RTN_SIG = "(" + nativeName(WrContext.class) + nativeName(HookReturn.class) + ")V"; String RTN_P1 = WrContext.class.getName(); String RTN_P2 = HookReturn.class.getName(); StringBuffer RTN_BODY = bodyTable.get("return"); String THIS = "capThis"; String THIS_SIG = "(" + nativeName(WrContext.class) + nativeName(String.class) + nativeName(String.class) + nativeName(Object.class) + ")V"; String THIS_P1 = WrContext.class.getName(); String THIS_P2 = String.class.getName(); String THIS_P3 = String.class.getName(); String THIS_P4 = "Object"; StringBuffer THIS_BODY = bodyTable.get("this"); ClassPool cp = ClassPool.getDefault(); String jar = FileUtil.getJarFileName(PluginLoader.class); if (jar != null) { cp.appendClassPath(jar); } Class c = null; CtClass cc = cp.get(superName); CtClass impl = null; CtMethod method_args; CtMethod method_return; CtMethod method_this; StringBuffer sb; try { impl = cp.get(className); impl.defrost(); // ARG METHOD method_args = impl.getMethod(ARG, ARG_SIG); // RETURN METHOD method_return = impl.getMethod(RTN, RTN_SIG); // THIS METHOD method_this = impl.getMethod(THIS, THIS_SIG); } catch (scouter.javassist.NotFoundException e) { impl = cp.makeClass(className, cc); // ARG METHOD sb = new StringBuffer(); sb.append("public void ").append(ARG).append("("); sb.append(ARG_P1).append(" p1 ").append(","); sb.append(ARG_P2).append(" p2 "); sb.append("){}"); method_args = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_args); // RTN METHOD sb = new StringBuffer(); sb.append("public void ").append(RTN).append("("); sb.append(RTN_P1).append(" p1 ").append(","); sb.append(RTN_P2).append(" p2 "); sb.append("){}"); method_return = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_return); // THIS METHOD sb = new StringBuffer(); sb.append("public void ").append(THIS).append("("); sb.append(THIS_P1).append(" p1 ").append(","); sb.append(THIS_P2).append(" p2 ").append(","); sb.append(THIS_P3).append(" p3 ").append(","); sb.append(THIS_P4).append(" p4 "); sb.append("){}"); method_this = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method_this); } sb = new StringBuffer(); sb.append("{"); sb.append(ARG_P1).append(" $ctx=$1;"); sb.append(ARG_P2).append(" $hook=$2;"); sb.append(ARG_BODY); sb.append("\n}"); method_args.setBody(sb.toString()); sb = new StringBuffer(); sb.append("{"); sb.append(RTN_P1).append(" $ctx=$1;"); sb.append(RTN_P2).append(" $hook=$2;"); sb.append(RTN_BODY); sb.append("\n}"); method_return.setBody(sb.toString()); sb = new StringBuffer(); sb.append("{"); sb.append(THIS_P1).append(" $ctx=$1;"); sb.append(THIS_P2).append(" $class=$2;"); sb.append(THIS_P3).append(" $desc=$3;"); sb.append(THIS_P4).append(" $this=$4;"); sb.append(THIS_BODY); sb.append("\n}"); method_this.setBody(sb.toString()); c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null); AbstractCapture plugin = (AbstractCapture) c.newInstance(); plugin.lastModified = script.lastModified(); Logger.println("PLUG-IN : " + AbstractCapture.class.getName() + " " + script.getName() + " loaded #" + Hexa32.toString32(plugin.hashCode())); return plugin; } catch (scouter.javassist.CannotCompileException ee) { Logger.println("PLUG-IN : " + ee.getMessage()); } catch (Exception e) { Logger.println("A905", e); } return null; } private long IJdbcPoolCompile; private AbstractJdbcPool createIJdbcPool(File script) { if (IJdbcPoolCompile == script.lastModified()) return null; IJdbcPoolCompile = script.lastModified(); try { HashMap<String, StringBuffer> bodyTable = loadFileText(script); String superName = AbstractJdbcPool.class.getName(); String className = "scouter.agent.plugin.impl.JdbcPoolImpl"; String URL = "url"; String URL_SIG = "(" + nativeName(WrContext.class) + nativeName(String.class) + nativeName(Object.class) + ")" + nativeName(String.class); String URL_P1 = WrContext.class.getName(); String URL_P2 = String.class.getName(); String URL_P3 = "Object"; StringBuffer URL_BODY = bodyTable.get("url"); ClassPool cp = ClassPool.getDefault(); String jar = FileUtil.getJarFileName(PluginLoader.class); if (jar != null) { cp.appendClassPath(jar); } Class c = null; CtClass cc = cp.get(superName); CtClass impl = null; CtMethod method = null; try { impl = cp.get(className); impl.defrost(); method = impl.getMethod(URL, URL_SIG); } catch (scouter.javassist.NotFoundException e) { impl = cp.makeClass(className, cc); StringBuffer sb = new StringBuffer(); sb.append("public String ").append(URL).append("("); sb.append(URL_P1).append(" p1 ").append(","); sb.append(URL_P2).append(" p2 ").append(","); sb.append(URL_P3).append(" p3 "); sb.append("){return null;}"); method = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method); } StringBuffer sb = new StringBuffer(); sb.append("{"); sb.append(URL_P1).append(" $ctx=$1;"); sb.append(URL_P2).append(" $msg=$2;"); sb.append(URL_P3).append(" $pool=$3;"); sb.append(URL_BODY); sb.append("\n}"); method.setBody(sb.toString()); c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null); AbstractJdbcPool plugin = (AbstractJdbcPool) c.newInstance(); plugin.lastModified = script.lastModified(); Logger.println("PLUG-IN : " + AbstractJdbcPool.class.getName() + " loaded #" + Hexa32.toString32(plugin.hashCode())); return plugin; } catch (scouter.javassist.CannotCompileException ee) { Logger.println("PLUG-IN : " + ee.getMessage()); } catch (Exception e) { Logger.println("A906", e); } return null; } private long IHttpCallCompile; private AbstractHttpCall createIHttpCall(File script) { if (IHttpCallCompile == script.lastModified()) return null; IHttpCallCompile = script.lastModified(); try { HashMap<String, StringBuffer> bodyTable = loadFileText(script); String superName = AbstractHttpCall.class.getName(); String className = "scouter.agent.plugin.impl.IHttCallTraceImpl"; String CALL = "call"; String CALL_SIG = "(" + nativeName(WrContext.class) + nativeName(WrHttpCallRequest.class) + ")V"; String CALL_P1 = WrContext.class.getName(); String CALL_P2 = WrHttpCallRequest.class.getName(); StringBuffer CALL_BODY = bodyTable.get(CALL); if (CALL_BODY == null) throw new CannotCompileException("no method body: " + CALL); ClassPool cp = ClassPool.getDefault(); String jar = FileUtil.getJarFileName(PluginLoader.class); if (jar != null) { cp.appendClassPath(jar); } Class c = null; CtClass cc = cp.get(superName); CtClass impl = null; StringBuffer sb; CtMethod method = null; try { impl = cp.get(className); impl.defrost(); method = impl.getMethod(CALL, CALL_SIG); } catch (scouter.javassist.NotFoundException e) { impl = cp.makeClass(className, cc); sb = new StringBuffer(); sb.append("public void ").append(CALL).append("("); sb.append(CALL_P1).append(" p1 ").append(","); sb.append(CALL_P2).append(" p2"); sb.append("){}"); method = CtNewMethod.make(sb.toString(), impl); impl.addMethod(method); } sb = new StringBuffer(); sb.append("{"); sb.append(CALL_P1).append(" $ctx=$1;"); sb.append(CALL_P2).append(" $req=$2;"); sb.append(CALL_BODY); sb.append("\n}"); method.setBody(sb.toString()); c = impl.toClass(new URLClassLoader(new URL[0], this.getClass().getClassLoader()), null); AbstractHttpCall plugin = (AbstractHttpCall) c.newInstance(); plugin.lastModified = script.lastModified(); Logger.println("PLUG-IN : " + AbstractHttpCall.class.getName() + " loaded #" + Hexa32.toString32(plugin.hashCode())); return plugin; } catch (scouter.javassist.CannotCompileException ee) { Logger.println("PLUG-IN : " + ee.getMessage()); } catch (Exception e) { Logger.println("A907", e); } return null; } private String nativeName(Class class1) { return "L" + class1.getName().replace('.', '/') + ";"; } }