From 82dc3b1922ffd6165039d91aa225268b866cb58a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 22 Oct 2018 15:46:58 +0800 Subject: [PATCH] add ognl command. #240 --- .../core/command/BuiltinCommandPack.java | 2 + .../express/ClassLoaderClassResolver.java | 42 +++++++ .../core/command/express/ExpressFactory.java | 3 + .../core/command/express/OgnlExpress.java | 12 +- .../command/klass100/GetStaticCommand.java | 2 +- .../core/command/klass100/OgnlCommand.java | 106 ++++++++++++++++++ 6 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index 5119c5c4e..2b9beef2a 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -15,6 +15,7 @@ import com.taobao.arthas.core.command.klass100.ClassLoaderCommand; import com.taobao.arthas.core.command.klass100.DumpClassCommand; import com.taobao.arthas.core.command.klass100.GetStaticCommand; import com.taobao.arthas.core.command.klass100.JadCommand; +import com.taobao.arthas.core.command.klass100.OgnlCommand; import com.taobao.arthas.core.command.klass100.RedefineCommand; import com.taobao.arthas.core.command.klass100.SearchClassCommand; import com.taobao.arthas.core.command.klass100.SearchMethodCommand; @@ -66,6 +67,7 @@ public class BuiltinCommandPack implements CommandResolver { commands.add(Command.create(TimeTunnelCommand.class)); commands.add(Command.create(JvmCommand.class)); // commands.add(Command.create(GroovyScriptCommand.class)); + commands.add(Command.create(OgnlCommand.class)); commands.add(Command.create(DashboardCommand.class)); commands.add(Command.create(DumpClassCommand.class)); commands.add(Command.create(JulyCommand.class)); diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java b/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java new file mode 100644 index 000000000..a1d3a4e84 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/express/ClassLoaderClassResolver.java @@ -0,0 +1,42 @@ +package com.taobao.arthas.core.command.express; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ognl.ClassResolver; + +/** + * + * @author hengyunabc 2018-10-18 + * @see ognl.DefaultClassResolver + */ +public class ClassLoaderClassResolver implements ClassResolver { + + private ClassLoader classLoader; + + private Map> classes = new ConcurrentHashMap>(101); + + public ClassLoaderClassResolver(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public Class classForName(String className, @SuppressWarnings("rawtypes") Map context) + throws ClassNotFoundException { + Class result = null; + + if ((result = classes.get(className)) == null) { + try { + result = classLoader.loadClass(className); + } catch (ClassNotFoundException ex) { + if (className.indexOf('.') == -1) { + result = Class.forName("java.lang." + className); + classes.put("java.lang." + className, result); + } + } + classes.put(className, result); + } + return result; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java b/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java index 34eaab0f2..5d6ffd63d 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java @@ -23,4 +23,7 @@ public class ExpressFactory { return expressRef.get().reset().bind(object); } + public static Express unpooledExpress(ClassLoader classloader) { + return new OgnlExpress(new ClassLoaderClassResolver(classloader)); + } } \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java b/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java index 9ff7042ef..524abae74 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/OgnlExpress.java @@ -2,12 +2,15 @@ package com.taobao.arthas.core.command.express; import com.taobao.arthas.core.util.LogUtil; import com.taobao.middleware.logger.Logger; + +import ognl.ClassResolver; import ognl.DefaultMemberAccess; import ognl.Ognl; import ognl.OgnlContext; /** * @author ralf0131 2017-01-04 14:41. + * @author hengyunabc 2018-10-18 */ public class OgnlExpress implements Express { @@ -17,14 +20,19 @@ public class OgnlExpress implements Express { private final OgnlContext context; public OgnlExpress() { + this(CustomClassResolver.customClassResolver); + } + + public OgnlExpress(ClassResolver classResolver) { context = new OgnlContext(); - context.setClassResolver(CustomClassResolver.customClassResolver); + context.setClassResolver(classResolver); + // allow private field access + context.setMemberAccess(new DefaultMemberAccess(true)); } @Override public Object get(String express) throws ExpressException { try { - context.setMemberAccess(new DefaultMemberAccess(true)); return Ognl.getValue(express, context, bindObject); } catch (Exception e) { logger.error(null, "Error during evaluating the expression:", e); diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java index ce16db01b..fad1d47fe 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java @@ -73,7 +73,7 @@ public class GetStaticCommand extends AnnotatedCommand { @Option(shortName = "c", longName = "classloader") @Description("The hash code of the special class's classLoader") - public void setCode(String hashCode) { + public void setHashCode(String hashCode) { this.hashCode = hashCode; } diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java new file mode 100644 index 000000000..f15768cfb --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java @@ -0,0 +1,106 @@ +package com.taobao.arthas.core.command.klass100; + +import java.lang.instrument.Instrumentation; + +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.express.Express; +import com.taobao.arthas.core.command.express.ExpressException; +import com.taobao.arthas.core.command.express.ExpressFactory; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.LogUtil; +import com.taobao.arthas.core.util.StringUtils; +import com.taobao.arthas.core.view.ObjectView; +import com.taobao.middleware.cli.annotations.Argument; +import com.taobao.middleware.cli.annotations.Description; +import com.taobao.middleware.cli.annotations.Name; +import com.taobao.middleware.cli.annotations.Option; +import com.taobao.middleware.cli.annotations.Summary; +import com.taobao.middleware.logger.Logger; + +/** + * + * @author hengyunabc 2018-10-18 + * + */ +@Name("ognl") +@Summary("Execute ognl expression.") +@Description(Constants.EXAMPLE + + " ognl '@java.lang.System@out.println(\"hello\")' \n" + + " ognl -x 2 '@Singleton@getInstance() \n" + + " ognl '@Demo@staticFiled' \n" + + " ognl '#value1=@System@getProperty(\"java.home\"), #value2=@System@getProperty(\"java.runtime.name\"), {#value1, #value2}'\n" + + " ognl -c 5d113a51 '@com.taobao.arthas.core.GlobalOptions@isDump' \n" + + Constants.WIKI + Constants.WIKI_HOME + "ognl\n" + + " https://commons.apache.org/proper/commons-ognl/language-guide.html") +public class OgnlCommand extends AnnotatedCommand { + private static final Logger logger = LogUtil.getArthasLogger(); + + private String express; + + private String hashCode; + private int expand = 1; + + @Argument(argName = "express", index = 0, required = true) + @Description("The ognl expression.") + public void setExpress(String express) { + this.express = express; + } + + @Option(shortName = "c", longName = "classLoader") + @Description("The hash code of the special class's classLoader, default classLoader is SystemClassLoader.") + public void setHashCode(String hashCode) { + this.hashCode = hashCode; + } + + @Option(shortName = "x", longName = "expand") + @Description("Expand level of object (1 by default).") + public void setExpand(Integer expand) { + this.expand = expand; + } + + @Override + public void process(CommandProcess process) { + int exitCode = 0; + try { + Instrumentation inst = process.session().getInstrumentation(); + ClassLoader classLoader = null; + if (hashCode == null) { + classLoader = ClassLoader.getSystemClassLoader(); + } else { + classLoader = findClassLoader(inst, hashCode); + } + + if (classLoader == null) { + process.write("Can not find classloader with hashCode: " + hashCode + ".\n"); + exitCode = -1; + return; + } + + Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); + try { + Object value = unpooledExpress.get(express); + String result = StringUtils.objectToString(expand >= 0 ? new ObjectView(value, expand).draw() : value); + process.write(result + "\n"); + } catch (ExpressException e) { + logger.warn("ognl: failed execute express: " + express, e); + process.write("Failed to get static, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details. \n"); + exitCode = -1; + } + } finally { + process.end(exitCode); + } + } + + private static ClassLoader findClassLoader(Instrumentation inst, String hashCode) { + for (Class clazz : inst.getAllLoadedClasses()) { + ClassLoader classLoader = clazz.getClassLoader(); + if (classLoader != null && hashCode.equals(Integer.toHexString(classLoader.hashCode()))) { + return classLoader; + } + } + return null; + } + +}