Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b126872
[support qlexpress]
taokan Oct 7, 2024
2b88739
Merge branch 'master' of https://github.com/taokan/arthas
taokan Oct 7, 2024
30fe689
【支持qlexpress表达式】
taokan Oct 8, 2024
efebecd
【支持qlexpress表达式】
taokan Oct 8, 2024
b1a0379
【支持qlexpress表达式】
taokan Oct 8, 2024
a878dc6
【支持qlexpress表达式】
taokan Oct 27, 2024
c780218
【支持qlexpress表达式】
taokan Nov 3, 2024
a54ac65
【支持qlexpress表达式】
taokan Nov 3, 2024
cbdf590
【支持qlexpress表达式】
taokan Nov 3, 2024
ec0f951
【支持qlexpress表达式】
taokan Nov 10, 2024
2dab795
【支持qlexpress表达式】
taokan Nov 10, 2024
44fde5c
【支持qlexpress表达式】
taokan Nov 10, 2024
aebe302
【支持qlexpress表达式】
taokan Nov 10, 2024
7d73a17
【支持qlexpress表达式】
taokan Nov 10, 2024
49d9397
【支持qlexpress表达式】
taokan Dec 1, 2024
87b71d0
【支持qlexpress表达式】
taokan Dec 22, 2024
35899a8
【支持qlexpress表达式】
taokan Dec 22, 2024
5a77e93
【支持qlexpress表达式】yarn revert
taokan Dec 22, 2024
adb0257
【支持qlexpress表达式】pom版本升级
taokan Jan 4, 2025
4f3a518
【支持qlexpress表达式】冲突
taokan Jan 4, 2025
564c11a
【支持qlexpress表达式】冲突
taokan Jan 4, 2025
23c3df1
【支持qlexpress表达式】冲突
taokan Jan 4, 2025
74a84f8
【支持qlexpress表达式】冲突
taokan Jan 4, 2025
92cff37
【支持qlexpress表达式】冲突
taokan Jan 12, 2025
63c648c
【支持qlexpress表达式】冲突
taokan Jan 12, 2025
dac03f5
【支持qlexpress表达式】冲突
taokan Jan 12, 2025
99f4850
【支持qlexpress表达式】冲突
taokan Jan 12, 2025
0de1a3f
Merge branch 'qlexpress' into qlexpress20240112
taokan Jan 12, 2025
ded1ca5
【支持qlexpress表达式】文档
taokan Feb 12, 2025
6dfe5fa
【支持qlexpress表达式】文档
taokan Mar 9, 2025
4bb7b1e
【支持qlexpress表达式】刪除未引用代码
taokan Mar 18, 2025
cd4ce45
【支持qlexpress表达式】刪除未引用代码
taokan Mar 18, 2025
df33bba
【支持qlexpress表达式】刪除未引用代码
taokan Mar 23, 2025
cf8a6ee
【增加gl命令】command命令
taokan Apr 27, 2025
4bc09fb
【增加qlexpress命令】command命令
taokan May 5, 2025
28d99d4
Merge branch 'master' of https://github.com/taokan/arthas
taokan Aug 4, 2025
5a8a935
Merge branch 'master' into qlexpress
taokan Aug 4, 2025
b9f8f56
【qlexpress版本】4.0.0
taokan Aug 9, 2025
8001cf1
【qlexpress版本】4.0.0
taokan Aug 18, 2025
15e0ffd
【qlexpress版本】4.0.0
taokan Aug 18, 2025
993e4e9
qlexpress version 4.0.3
DQinYuan Sep 22, 2025
7c390e6
Update site/docs/doc/options.md
DQinYuan Sep 22, 2025
17ceb67
【qlexpress版本】4.0.0
taokan Sep 26, 2025
e11628f
【qlexpress版本】4.0.0
taokan Sep 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>qlexpress4</artifactId>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
Expand Down Expand Up @@ -263,6 +267,10 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
</dependency>
Copy link

Copilot AI Aug 10, 2025

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.

Suggested change
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
</dependency>

Copilot uses AI. Check for mistakes.

</dependencies>

</project>
9 changes: 9 additions & 0 deletions core/src/main/java/com/taobao/arthas/core/GlobalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ public class GlobalOptions {
description = STRICT_MESSAGE
)
public static volatile boolean strict = true;
/**
* 是否切换使用表达式ognl/qlexpress开关
*/
@Option(level = 1,
name = "el",
summary = "expression language",
description = "Option to use ognl/qlexpress in commands, default ognl, can change to qlexpress"
)
public static volatile String ExpressType = "ognl";

public static void updateOnglStrict(boolean strict) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ private void initCommands(List<String> disabledCommands) {
commandClassList.add(PerfCounterCommand.class);
// commandClassList.add(GroovyScriptCommand.class);
commandClassList.add(OgnlCommand.class);
commandClassList.add(QLExpressCommand.class);
commandClassList.add(MemoryCompilerCommand.class);
commandClassList.add(RedefineCommand.class);
commandClassList.add(RetransformCommand.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.taobao.arthas.core.command.basic1000;

import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
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.command.model.ClassLoaderVO;
import com.taobao.arthas.core.command.model.ObjectVO;
import com.taobao.arthas.core.command.model.OgnlModel;
Copy link

Copilot AI Aug 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import for OgnlModel is unused in this class. Since this class uses QLExpressModel instead, this import should be removed.

Suggested change
import com.taobao.arthas.core.command.model.OgnlModel;

Copilot uses AI. Check for mistakes.

import com.taobao.arthas.core.command.model.QLExpressModel;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.middleware.cli.annotations.*;

import java.lang.instrument.Instrumentation;
import java.util.Collection;
import java.util.List;

/**
* @Author TaoKan
* @Date 2025/3/23 8:59 PM
*/
@Name("qlexpress")
@Summary("Execute qlexpress expression.")
@Description(Constants.EXAMPLE
+ " qlexpress 'java.lang.System.out.println(\"hello~\")' \n"
+ " qlexpress -x 2 'java.lang.Math.abs(1)' \n"
+ " qlexpress -c 5d113a51 'com.taobao.arthas.core.GlobalOptions.isDump' \n"
+ Constants.WIKI + Constants.WIKI_HOME + "qlexpress\n"
+ " https://github.com/alibaba/QLExpress/tree/v4.0.0-beta.1")
public class QLExpressCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(QLExpressCommand.class);

private String express;
private String hashCode;
private String classLoaderClass;
private int expand = 1;

@Argument(argName = "express", index = 0, required = true)
@Description("The qlexpress 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(longName = "classLoaderClass")
@Description("The class name of the special class's classLoader.")
public void setClassLoaderClass(String classLoaderClass) {
this.classLoaderClass = classLoaderClass;
}


@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) {
Instrumentation inst = process.session().getInstrumentation();
ClassLoader classLoader = null;
if (hashCode != null) {
classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode);
if (classLoader == null) {
process.end(-1, "Can not find classloader with hashCode: " + hashCode + ".");
return;
}
} else if (classLoaderClass != null) {
List<ClassLoader> matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass);
if (matchedClassLoaders.size() == 1) {
classLoader = matchedClassLoaders.get(0);
} else if (matchedClassLoaders.size() > 1) {
Collection<ClassLoaderVO> classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders);
QLExpressModel qlModel = new QLExpressModel()
.setClassLoaderClass(classLoaderClass)
.setMatchedClassLoaders(classLoaderVOList);
process.appendResult(qlModel);
process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'");
return;
} else {
process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
return;
}
} else {
classLoader = ClassLoader.getSystemClassLoader();
}

Express unpooledExpress = ExpressFactory.unpooledExpressByQL(classLoader);
try {
Object value = unpooledExpress.bind(new Object()).get(express);
QLExpressModel qlModel = new QLExpressModel()
.setValue(new ObjectVO(value, expand));
process.appendResult(qlModel);
process.end();
} catch (ExpressException e) {
logger.warn("qlexpress: failed execute express: " + express, e);
process.end(-1, "Failed to execute qlexpress, exception message: " + e.getMessage()
+ ", please check $HOME/logs/arthas/arthas.log for more details. ");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
package com.taobao.arthas.core.command.express;

import com.taobao.arthas.core.GlobalOptions;
import com.taobao.arthas.core.command.model.ExpressTypeEnum;


/**
* 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.equals(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.equals(ExpressTypeEnum.QLEXPRESS.getExpressType())) {
return new QLExpress(new QLExpressClassLoaderClassResolver(classloader));
}
return new OgnlExpress(new ClassLoaderClassResolver(classloader));
}


public static Express unpooledExpressByQL(ClassLoader classloader) {
if (classloader == null) {
classloader = ClassLoader.getSystemClassLoader();
}
return new QLExpress(new QLExpressClassLoaderClassResolver(classloader));
}

public static Express unpooledExpressByOGNL(ClassLoader classloader) {
if (classloader == null) {
classloader = ClassLoader.getSystemClassLoader();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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.*;
import com.alibaba.qlexpress4.security.QLSecurityStrategy;


/**
* @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) {
this.initOptions = initQLExpress(classResolver);
this.expressRunner = QLExpressRunner.getInstance(initOptions);
this.qlOptions = initConfig();
this.qlGlobalContext = new QLGlobalContext(expressRunner);
}

private QLOptions initConfig() {
return QLOptions.DEFAULT_OPTIONS;
}

private InitOptions initQLExpress(ClassSupplier classResolver) {
InitOptions.Builder initOptionsBuilder = InitOptions.builder();
initOptionsBuilder.securityStrategy(QLSecurityStrategy.open());
initOptionsBuilder.allowPrivateAccess(true);
initOptionsBuilder.classSupplier(classResolver);
return initOptionsBuilder.build();
}

@Override
public Object get(String express) throws ExpressException {
try {
return expressRunner.execute(express, qlGlobalContext, qlOptions).getResult();
} 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);
Copy link

@DQinYuan DQinYuan Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

个人觉得在 put 的时候加上 "#" 前缀,要比 get 的时候 replace 要好。这样 context 的逻辑更加简单纯粹。replace 可能会导致改变不该改变的字符。

Copy link
Author

Choose a reason for hiding this comment

The 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);
}

}
Loading
Loading