-
Notifications
You must be signed in to change notification settings - Fork 7.6k
表达式支持使用qlexpress4.0 #2978
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
base: master
Are you sure you want to change the base?
表达式支持使用qlexpress4.0 #2978
Changes from 28 commits
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
6dfe5fa
4bb7b1e
cd4ce45
df33bba
cf8a6ee
4bc09fb
28d99d4
5a8a935
b9f8f56
8001cf1
15e0ffd
993e4e9
7c390e6
17ceb67
e11628f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -142,6 +142,26 @@ public class GlobalOptions { | |
description = STRICT_MESSAGE | ||
) | ||
public static volatile boolean strict = true; | ||
/** | ||
* 是否切换使用表达式ognl/qlexpress开关 | ||
*/ | ||
@Option(level = 1, | ||
name = "express-type", | ||
|
||
summary = "Option to use ognl/qlexpress", | ||
description = "Option to use ognl/qlexpress in commands, default ognl, can change to qlexpress" | ||
) | ||
public static volatile String ExpressType = "ognl"; | ||
|
||
|
||
/** | ||
* qlexpress使用参数 | ||
*/ | ||
@Option(level = 1, | ||
name = "qlexpress-config", | ||
|
||
summary = "config init when use qlexpress, with json-simple, for example: {\"precise\": true }", | ||
description = "" | ||
) | ||
public static volatile String QLExpressConfig = ""; | ||
|
||
public static void updateOnglStrict(boolean strict) { | ||
try { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
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.fastjson2.JSON; | ||
import com.alibaba.qlexpress4.ClassSupplier; | ||
import com.alibaba.qlexpress4.Express4Runner; | ||
import com.alibaba.qlexpress4.InitOptions; | ||
import com.alibaba.qlexpress4.QLOptions; | ||
import com.alibaba.qlexpress4.runtime.ReflectLoader; | ||
import com.alibaba.qlexpress4.security.QLSecurityStrategy; | ||
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 QLOptions qlOptions; | ||
|
||
private InitOptions initOptions; | ||
|
||
public QLExpress() { | ||
this(QLExpressCustomClassResolver.customClassResolver); | ||
} | ||
|
||
public QLExpress(ClassSupplier classResolver) { | ||
initQLExpress(classResolver); | ||
initConfig(); | ||
initContext(); | ||
|
||
} | ||
|
||
private void initConfig() { | ||
try { | ||
if (GlobalOptions.QLExpressConfig.length() > 0) { | ||
QLOptions.Builder qlOptionsBuilder = QLOptions.builder(); | ||
QLExpressConfigModel qlExpressConfigModel = JSON.parseObject(GlobalOptions.QLExpressConfig, QLExpressConfigModel.class); | ||
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(); | ||
}else { | ||
qlOptions = QLOptions.DEFAULT_OPTIONS; | ||
} | ||
//4.0设置InitOptions | ||
}catch (Throwable t){ | ||
//异常不设置options | ||
logger.error("Error Init Options For QLExpress:", t); | ||
} | ||
} | ||
|
||
private void initQLExpress(ClassSupplier classResolver) { | ||
InitOptions.Builder initOptionsBuilder = InitOptions.builder(); | ||
initOptionsBuilder.securityStrategy(QLSecurityStrategy.open()); | ||
initOptionsBuilder.allowPrivateAccess(true); | ||
initOptionsBuilder.classSupplier(classResolver); | ||
initOptions = initOptionsBuilder.build(); | ||
expressRunner = QLExpressRunner.getInstance(initOptions); | ||
} | ||
|
||
private void initContext() { | ||
ReflectLoader reflectLoader = new ReflectLoader(initOptions.getSecurityStrategy(), initOptions.getExtensionFunctions(), initOptions.isAllowPrivateAccess()); | ||
qlGlobalContext = new QLGlobalContext(reflectLoader); | ||
} | ||
|
||
@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,40 @@ | ||
package com.taobao.arthas.core.command.express; | ||
|
||
import com.alibaba.qlexpress4.ClassSupplier; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/12/1 7:07 PM | ||
*/ | ||
public class QLExpressClassLoaderClassResolver implements ClassSupplier { | ||
|
||
private ClassLoader classLoader; | ||
|
||
private final Map<String, Optional<Class<?>>> cache = new ConcurrentHashMap<>(); | ||
|
||
public QLExpressClassLoaderClassResolver(ClassLoader classLoader) { | ||
this.classLoader = classLoader; | ||
} | ||
|
||
private Optional<Class<?>> loadClsInner(String clsQualifiedName) { | ||
try { | ||
Class<?> aClass = null; | ||
if (classLoader != null) { | ||
aClass = classLoader.loadClass(clsQualifiedName); | ||
}else { | ||
aClass = Class.forName(clsQualifiedName); | ||
} | ||
return Optional.of(aClass); | ||
} catch (ClassNotFoundException | NoClassDefFoundError e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
@Override | ||
public Class<?> loadCls(String className) { | ||
Optional<Class<?>> clsOp = cache.computeIfAbsent(className, this::loadClsInner); | ||
return clsOp.orElse(null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
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.qlexpress4.ClassSupplier; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* @Author TaoKan | ||
* @Date 2024/12/1 7:06 PM | ||
*/ | ||
public class QLExpressCustomClassResolver implements ClassSupplier { | ||
|
||
public static final QLExpressCustomClassResolver customClassResolver = new QLExpressCustomClassResolver(); | ||
|
||
private final Map<String, Optional<Class<?>>> cache = new ConcurrentHashMap<>(); | ||
|
||
private QLExpressCustomClassResolver() { | ||
|
||
} | ||
|
||
private Optional<Class<?>> loadClsInner(String clsQualifiedName) { | ||
try { | ||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); | ||
Class<?> aClass = null; | ||
if (classLoader != null) { | ||
aClass = classLoader.loadClass(clsQualifiedName); | ||
} else { | ||
aClass = Class.forName(clsQualifiedName); | ||
} | ||
return Optional.of(aClass); | ||
} catch (ClassNotFoundException | NoClassDefFoundError e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
@Override | ||
public Class<?> loadCls(String clsQualifiedName) { | ||
Optional<Class<?>> clsOp = cache.computeIfAbsent(clsQualifiedName, this::loadClsInner); | ||
return clsOp.orElse(null); | ||
} | ||
|
||
} |
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(InitOptions initOptions){ | ||
expressRunner = new Express4Runner(initOptions); | ||
} | ||
|
||
//对外提供静态方法获取对象 | ||
public static Express4Runner getInstance(InitOptions initOptions){ | ||
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例 | ||
if(instance == null){ | ||
synchronized (QLExpressRunner.class){ | ||
//抢到锁之后再次进行判断是否为null | ||
if(instance == null){ | ||
instance = new QLExpressRunner(initOptions); | ||
} | ||
} | ||
} | ||
return instance.expressRunner; | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
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.fastjson2.JSON; | ||
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 { | ||
private static final Logger logger = LoggerFactory.getLogger(QLGlobalContext.class); | ||
|
||
private Map<String, Object> context; | ||
private Object object; | ||
private ReflectLoader reflectLoader; | ||
|
||
public QLGlobalContext(ReflectLoader reflectLoader) { | ||
this.context = new ConcurrentHashMap<>(); | ||
this.reflectLoader = reflectLoader; | ||
} | ||
|
||
public void put(String name, Object value){ | ||
context.put(name, value); | ||
} | ||
|
||
public void clear() { | ||
context.clear(); | ||
this.context.put("reflectLoader",reflectLoader); | ||
|
||
} | ||
|
||
public void bindObj(Object object) { | ||
this.object = object; | ||
context.put("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); | ||
|
||
} | ||
String newVariableName = variableName.replace("#",""); | ||
|
||
return new MapItemValue(this.context, newVariableName); | ||
} | ||
|
||
|
||
public Map<String, Object> getContext() { | ||
return context; | ||
} | ||
|
||
public void setContext(Map<String, Object> context) { | ||
this.context = context; | ||
} | ||
|
||
public Object getObject() { | ||
return object; | ||
} | ||
|
||
public void setObject(Object object) { | ||
this.object = object; | ||
} | ||
|
||
} |
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.
There appears to be a duplicate OGNL dependency declaration. The OGNL dependency is already declared at line 211-213, making this additional declaration redundant.
Copilot uses AI. Check for mistakes.