2024-08-10 14:12:47 +08:00
|
|
|
|
package com.wflow.workflow.execute;
|
|
|
|
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2025-08-19 16:15:06 +08:00
|
|
|
|
import org.graalvm.polyglot.Context;
|
|
|
|
|
|
import org.graalvm.polyglot.HostAccess;
|
|
|
|
|
|
import org.graalvm.polyglot.Value;
|
2024-08-10 14:12:47 +08:00
|
|
|
|
|
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @author : willian fu
|
|
|
|
|
|
* @date : 2023/10/28
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class JsExecute {
|
|
|
|
|
|
|
|
|
|
|
|
private static final Class[] alowJsCallJavaClass = new Class[]{
|
2025-08-19 16:15:06 +08:00
|
|
|
|
//HttpRequest.class, System.class
|
|
|
|
|
|
// GraalVM的HostAccess默认允许访问一些常见的类,例如System
|
|
|
|
|
|
// 如果你需要访问自定义的类,你需要在这里配置
|
2024-08-10 14:12:47 +08:00
|
|
|
|
};
|
2025-08-19 16:15:06 +08:00
|
|
|
|
private static Context context;
|
2024-08-10 14:12:47 +08:00
|
|
|
|
|
2025-08-19 16:15:06 +08:00
|
|
|
|
// 使用 Context.Builder 来创建和配置沙箱环境
|
2024-08-10 14:12:47 +08:00
|
|
|
|
public JsExecute(ExecutorService executorService) {
|
2025-08-19 16:15:06 +08:00
|
|
|
|
if (Objects.isNull(context)) {
|
|
|
|
|
|
// 使用 Context.Builder 来创建和配置 GraalVM 沙箱
|
|
|
|
|
|
context = Context.newBuilder("js")
|
|
|
|
|
|
.allowHostAccess(HostAccess.ALL) // 允许访问所有Java对象
|
|
|
|
|
|
// 如果只想允许特定类,可以使用HostAccess.newBuilder()
|
|
|
|
|
|
.allowHostClassLookup(s -> true) // 允许查找所有Java类
|
|
|
|
|
|
// 如果只想允许特定类,可以配置如下:
|
|
|
|
|
|
// .allowHostClassLookup(className -> {
|
|
|
|
|
|
// return className.equals("com.yourpackage.HttpRequest") || className.equals("java.lang.System");
|
|
|
|
|
|
// })
|
|
|
|
|
|
.allowAllAccess(true) // 允许所有访问,包括I/O、线程等
|
|
|
|
|
|
.option("engine.WarnInterpreterOnly", "false") // 忽略解释器模式警告
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
// GraalVM的资源限制配置有所不同,通常是在创建context时设置,或者通过线程池等外部方式管理。
|
|
|
|
|
|
// 例如,CPU时间限制等需要通过更复杂的机制实现,Context.Builder不直接支持MaxCPUTime等。
|
|
|
|
|
|
// 内存限制通常通过JVM的-Xmx参数来控制。
|
|
|
|
|
|
// 你可以继续使用你传入的ExecutorService来控制并发。
|
|
|
|
|
|
|
2024-08-10 14:12:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public <T> T execute(String func, String script, Class<T> resultType, Object... args) {
|
|
|
|
|
|
try {
|
2025-08-19 16:15:06 +08:00
|
|
|
|
// 注意:GraalVM 的 Context.eval() 有两种重载,
|
|
|
|
|
|
// 一种是直接执行,另一种是带源文件名的,这里简化使用
|
|
|
|
|
|
context.eval("js", script);
|
|
|
|
|
|
|
|
|
|
|
|
// 获取 JavaScript 函数
|
|
|
|
|
|
Value function = context.getBindings("js").getMember(func);
|
|
|
|
|
|
if (function == null || !function.canExecute()) {
|
|
|
|
|
|
throw new NoSuchMethodException("Function " + func + " not found or not executable.");
|
2024-08-10 14:12:47 +08:00
|
|
|
|
}
|
2025-08-19 16:15:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 执行函数并获取结果
|
|
|
|
|
|
Value result = function.execute(args);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 Value.as() 方法进行类型转换,这是 GraalVM 推荐的方式
|
|
|
|
|
|
return result.as(resultType);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
2024-08-10 14:12:47 +08:00
|
|
|
|
log.error("js 函数[{}] [{}]执行异常: [{}]", func, script, e.getMessage());
|
2025-08-19 16:15:06 +08:00
|
|
|
|
throw new RuntimeException("js函数解析执行异常:" + e.getMessage(), e);
|
2024-08-10 14:12:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void executeVoid(String func, String script, Object... args) {
|
|
|
|
|
|
execute(func, script, Object.class, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|