-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Qlexpress #2978
base: master
Are you sure you want to change the base?
Qlexpress #2978
Changes from 1 commit
b126872
2b88739
30fe689
efebecd
b1a0379
a878dc6
c780218
a54ac65
cbdf590
ec0f951
2dab795
44fde5c
aebe302
7d73a17
49d9397
87b71d0
35899a8
5a77e93
adb0257
4f3a518
564c11a
23c3df1
74a84f8
92cff37
63c648c
dac03f5
99f4850
0de1a3f
ded1ca5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,57 @@ | ||
package com.taobao.arthas.core.command.express; | ||
|
||
import com.alibaba.fastjson.JSON; | ||
import com.taobao.arthas.core.GlobalOptions; | ||
import com.taobao.arthas.core.command.model.ExpressTypeEnum; | ||
import com.taobao.arthas.core.command.model.QLExpressConfigModel; | ||
|
||
/** | ||
* ExpressFactory | ||
* @author ralf0131 2017-01-04 14:40. | ||
* @author hengyunabc 2018-10-08 | ||
*/ | ||
public class ExpressFactory { | ||
|
||
private static final ThreadLocal<Express> expressRef = new ThreadLocal<Express>() { | ||
@Override | ||
protected Express initialValue() { | ||
return new OgnlExpress(); | ||
} | ||
}; | ||
private static final ThreadLocal<Express> expressRef = ThreadLocal.withInitial(() -> new OgnlExpress()); | ||
private static final ThreadLocal<Express> expressRefQLExpress = ThreadLocal.withInitial(() -> new QLExpress()); | ||
|
||
/** | ||
* get ThreadLocal Express Object | ||
* @param object | ||
* @return | ||
*/ | ||
public static Express threadLocalExpress(Object object) { | ||
if (GlobalOptions.ExpressType == ExpressTypeEnum.QLEXPRESS.getExpressType()) { | ||
return expressRefQLExpress.get().reset().bind(object); | ||
} | ||
return expressRef.get().reset().bind(object); | ||
} | ||
|
||
public static Express unpooledExpress(ClassLoader classloader) { | ||
if (classloader == null) { | ||
classloader = ClassLoader.getSystemClassLoader(); | ||
} | ||
if (GlobalOptions.ExpressType == ExpressTypeEnum.QLEXPRESS.getExpressType()) { | ||
return new QLExpress(classloader); | ||
} | ||
return new OgnlExpress(new ClassLoaderClassResolver(classloader)); | ||
} | ||
|
||
public static Express unpooledExpressByOGNL(ClassLoader classloader) { | ||
if (classloader == null) { | ||
classloader = ClassLoader.getSystemClassLoader(); | ||
} | ||
return new OgnlExpress(new ClassLoaderClassResolver(classloader)); | ||
} | ||
|
||
public static boolean checkQLExpressConfig(String configValue) { | ||
try { | ||
if ("".equals(configValue)) { | ||
return true; | ||
} | ||
JSON.parseObject(configValue, QLExpressConfigModel.class); | ||
return true; | ||
}catch (Throwable t){ | ||
return false; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package com.taobao.arthas.core.command.express; | ||
|
||
import com.alibaba.arthas.deps.org.slf4j.Logger; | ||
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; | ||
import com.alibaba.fastjson.JSON; | ||
import com.alibaba.qlexpress4.Express4Runner; | ||
import com.alibaba.qlexpress4.InitOptions; | ||
import com.alibaba.qlexpress4.QLOptions; | ||
import com.taobao.arthas.core.GlobalOptions; | ||
import com.taobao.arthas.core.command.model.QLExpressConfigModel; | ||
|
||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/9/17 6:01 PM | ||
*/ | ||
public class QLExpress implements Express { | ||
private static final Logger logger = LoggerFactory.getLogger(QLExpress.class); | ||
private Express4Runner expressRunner; | ||
private QLGlobalContext qlGlobalContext; | ||
private Object runResult; | ||
|
||
private QLExpressConfigModel qlExpressConfigModel; | ||
|
||
private QLOptions qlOptions; | ||
|
||
private InitOptions initOptions; | ||
|
||
public QLExpress() { | ||
initQLExpress(); | ||
initConfig(null); | ||
initContext(); | ||
} | ||
|
||
public QLExpress(ClassLoader classloader) { | ||
initQLExpress(); | ||
initConfig(classloader); | ||
initContext(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 其实这三个函数就是分别初始化三个成员变量,下面这种写法可能更加清晰: this.expressRunner = initQLExpress(classResolver);
this.qlOptions = initConfig();
this.qlGlobalContext = initContext(); 尽量不要使用有副作用的函数 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fix |
||
} | ||
|
||
private void initConfig(ClassLoader classloader) { | ||
try { | ||
QLExpressConfigModel qlExpressConfigModel = JSON.parseObject(GlobalOptions.QLExpressConfig, QLExpressConfigModel.class); | ||
this.qlExpressConfigModel = qlExpressConfigModel; | ||
QLOptions.Builder qlOptionsBuilder = QLOptions.builder(); | ||
qlOptionsBuilder.cache(qlExpressConfigModel.isCache()); | ||
qlOptionsBuilder.avoidNullPointer(qlExpressConfigModel.isAvoidNullPointer()); | ||
qlOptionsBuilder.maxArrLength(qlExpressConfigModel.getMaxArrLength()); | ||
qlOptionsBuilder.polluteUserContext(qlExpressConfigModel.isPolluteUserContext()); | ||
qlOptionsBuilder.precise(qlExpressConfigModel.isPrecise()); | ||
qlOptionsBuilder.timeoutMillis(qlExpressConfigModel.getTimeoutMillis()); | ||
qlOptions = qlOptionsBuilder.build(); | ||
|
||
InitOptions.Builder initOptionsBuilder = InitOptions.builder(); | ||
initOptionsBuilder.allowPrivateAccess(qlExpressConfigModel.isAllowPrivateAccess()); | ||
initOptionsBuilder.debug(qlExpressConfigModel.isDebug()); | ||
initOptionsBuilder.useCacheClear(qlExpressConfigModel.isUseCacheClear()); | ||
initOptions = initOptionsBuilder.build(); | ||
//4.0设置InitOptions | ||
}catch (Throwable t){ | ||
//异常不设置options | ||
logger.error("Error Init Options For QLExpress:", t); | ||
} | ||
} | ||
|
||
private void initQLExpress() { | ||
expressRunner = QLExpressRunner.getInstance(); | ||
} | ||
|
||
private void initContext() { | ||
qlGlobalContext = new QLGlobalContext(); | ||
} | ||
|
||
@Override | ||
public Object get(String express) throws ExpressException { | ||
try { | ||
Object result = expressRunner.execute(express, qlGlobalContext, qlOptions); | ||
return result; | ||
} catch (Exception e) { | ||
logger.error("Error during evaluating the expression with QLExpress:", e); | ||
throw new ExpressException(express, e); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean is(String express) throws ExpressException { | ||
final Object ret = get(express); | ||
return ret instanceof Boolean && (Boolean) ret; | ||
} | ||
|
||
@Override | ||
public Express bind(Object object) { | ||
qlGlobalContext.bindObj(object); | ||
return this; | ||
} | ||
|
||
@Override | ||
public Express bind(String name, Object value) { | ||
qlGlobalContext.put(name, value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 个人觉得在 put 的是很加上 "#" 前缀,要比 get 的时候 replace 要好。这样 context 的逻辑更加简单纯粹。replace 可能会导致改变不该改变的字符。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
return this; | ||
} | ||
|
||
@Override | ||
public Express reset() { | ||
qlGlobalContext.clear(); | ||
return this; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.taobao.arthas.core.command.express; | ||
|
||
|
||
import com.alibaba.qlexpress4.Express4Runner; | ||
import com.alibaba.qlexpress4.InitOptions; | ||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/9/22 12:20 PM | ||
*/ | ||
public class QLExpressRunner { | ||
private volatile static QLExpressRunner instance = null; | ||
private Express4Runner expressRunner; | ||
|
||
private QLExpressRunner(){ | ||
expressRunner = new Express4Runner(InitOptions.DEFAULT_OPTIONS); | ||
} | ||
|
||
//对外提供静态方法获取对象 | ||
public static Express4Runner getInstance(){ | ||
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例 | ||
if(instance == null){ | ||
synchronized (QLExpressRunner.class){ | ||
//抢到锁之后再次进行判断是否为null | ||
if(instance == null){ | ||
instance = new QLExpressRunner(); | ||
} | ||
} | ||
} | ||
return instance.expressRunner; | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.taobao.arthas.core.command.express; | ||
|
||
import com.alibaba.qlexpress4.exception.PureErrReporter; | ||
import com.alibaba.qlexpress4.runtime.ReflectLoader; | ||
import com.alibaba.qlexpress4.runtime.Value; | ||
import com.alibaba.qlexpress4.runtime.context.ExpressContext; | ||
import com.alibaba.qlexpress4.runtime.data.MapItemValue; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/9/22 12:39 PM | ||
*/ | ||
public class QLGlobalContext implements ExpressContext { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 考虑继承 ObjectFieldExpressContext 添加额外功能 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
private Map<String, Object> context; | ||
private Object object; | ||
private ReflectLoader reflectLoader; | ||
|
||
public QLGlobalContext(Map<String, Object> context, Object bindObject, ReflectLoader reflectLoader) { | ||
this.context = context; | ||
this.object = bindObject; | ||
this.reflectLoader = reflectLoader; | ||
} | ||
|
||
public QLGlobalContext() { | ||
this.context = new ConcurrentHashMap<>(); | ||
} | ||
|
||
public void put(String name, Object value){ | ||
context.put(name, value); | ||
} | ||
|
||
public void clear() { | ||
context.clear(); | ||
} | ||
|
||
public void bindObj(Object object) { | ||
this.object = object; | ||
} | ||
@Override | ||
public Value get(Map<String, Object> attachments, String variableName) { | ||
if ((this.reflectLoader != null) && (this.object != null) && !variableName.startsWith("#")) { | ||
return this.reflectLoader.loadField(this.object, variableName, true, PureErrReporter.INSTANCE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个 api 有点底层,可以考虑继承 ObjectFieldExpressContext,然后额外加一个 map 功能,而不是直接调用 reflectLoader。或者我直接在 Express4Runner 上开个 api There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 待讨论点 |
||
} | ||
return new MapItemValue(this.context, variableName); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,13 +31,13 @@ | |
@Name("ognl") | ||
@Summary("Execute ognl expression.") | ||
@Description(Constants.EXAMPLE | ||
+ " ognl '@[email protected](\"hello \\u4e2d\\u6587\")' \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") | ||
+ " ognl '@[email protected](\"hello \\u4e2d\\u6587\")' \n" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 是不是也得有一个类似 ognl 这样的纯执行 qlexpress 表达式的命令 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 待讨论点 |
||
+ " 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 = LoggerFactory.getLogger(OgnlCommand.class); | ||
|
||
|
@@ -100,7 +100,7 @@ public void process(CommandProcess process) { | |
classLoader = ClassLoader.getSystemClassLoader(); | ||
} | ||
|
||
Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); | ||
Express unpooledExpress = ExpressFactory.unpooledExpressByOGNL(classLoader); | ||
try { | ||
Object value = unpooledExpress.get(express); | ||
OgnlModel ognlModel = new OgnlModel() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.taobao.arthas.core.command.model; | ||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/9/22 7:32 AM | ||
*/ | ||
public enum ExpressTypeEnum | ||
{ | ||
OGNL("ognl"), | ||
QLEXPRESS("QLExpress"); | ||
|
||
private String expressType; | ||
|
||
ExpressTypeEnum(String expressType) { | ||
this.expressType = expressType; | ||
} | ||
|
||
public String getExpressType() { | ||
return expressType; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是不是可以考虑短一点,比如就缩写成 el(expression language)