-
Notifications
You must be signed in to change notification settings - Fork 7.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'qlexpress' into qlexpress20240112
# Conflicts: # core/src/main/java/com/taobao/arthas/core/GlobalOptions.java # core/src/test/java/com/taobao/arthas/core/command/express/OgnlTest.java
- Loading branch information
Showing
13 changed files
with
525 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 19 additions & 6 deletions
25
core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
core/src/main/java/com/taobao/arthas/core/command/express/QLExpress.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
return this; | ||
} | ||
|
||
@Override | ||
public Express reset() { | ||
qlGlobalContext.clear(); | ||
return this; | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...c/main/java/com/taobao/arthas/core/command/express/QLExpressClassLoaderClassResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
core/src/main/java/com/taobao/arthas/core/command/express/QLExpressCustomClassResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
core/src/main/java/com/taobao/arthas/core/command/express/QLExpressRunner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
|
||
} |
70 changes: 70 additions & 0 deletions
70
core/src/main/java/com/taobao/arthas/core/command/express/QLGlobalContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
Oops, something went wrong.