diff --git a/common/common-code/pom.xml b/common/common-code/pom.xml
index 9dea91f..6daff0a 100644
--- a/common/common-code/pom.xml
+++ b/common/common-code/pom.xml
@@ -21,6 +21,16 @@
cn.fateverse
common-core
+
+ org.graalvm.js
+ js-scriptengine
+ 22.1.0
+
+
+ org.graalvm.js
+ js
+ 22.1.0
+
\ No newline at end of file
diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/console/MultiThreadedCapture.java b/common/common-code/src/main/java/cn/fateverse/common/code/console/MultiThreadedCapture.java
new file mode 100644
index 0000000..a0f4ddf
--- /dev/null
+++ b/common/common-code/src/main/java/cn/fateverse/common/code/console/MultiThreadedCapture.java
@@ -0,0 +1,45 @@
+package cn.fateverse.common.code.console;
+
+import cn.fateverse.common.code.model.EngineResult;
+import cn.fateverse.common.core.exception.CustomException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.*;
+
+/**
+ * @author Clay
+ * @date 2024/4/22 17:08
+ */
+public class MultiThreadedCapture {
+
+
+ private final static ExecutorService executor = Executors.newFixedThreadPool(2);
+
+
+ public static EngineResult capture(Task task) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream oldOut = System.out;
+ System.setOut(new PrintStream(baos));
+ Object result;
+ String capturedOutput;
+ try {
+ result = task.execute();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ System.setOut(oldOut);
+ // 从捕获的字节数组输出流中获取打印的文本
+ capturedOutput = baos.toString();
+ }
+ return new EngineResult(result, capturedOutput);
+ }
+
+
+ public interface Task {
+ Object execute() throws Exception;
+ }
+
+
+}
diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaCodeEngine.java b/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaCodeEngine.java
index 180e34e..b15fa60 100644
--- a/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaCodeEngine.java
+++ b/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaCodeEngine.java
@@ -2,18 +2,21 @@ package cn.fateverse.common.code.engine;
import cn.fateverse.common.code.config.JavaCodeProperties;
+import cn.fateverse.common.code.console.MultiThreadedCapture;
import cn.fateverse.common.code.exception.SandboxClassNotFoundException;
import cn.fateverse.common.code.lock.SegmentLock;
+import cn.fateverse.common.code.model.EngineResult;
import cn.fateverse.common.code.sandbox.SandboxClassLoader;
import cn.fateverse.common.code.sandbox.SandboxSecurityManager;
+import cn.fateverse.common.core.exception.CustomException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.springframework.util.ObjectUtils;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
+import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
@@ -73,7 +76,7 @@ public class JavaCodeEngine {
* @param development 是否为开发环境 开发环境下会将生成的类在执行完成后删除,不是生产环境则会缓存提高运行效率
* @return 执行结果
*/
- public T execute(String code, String className, String methodName, Class>[] paramClass, Object[] args, boolean development) {
+ public EngineResult execute(String code, String className, String methodName, Class>[] paramClass, Object[] args, boolean development) {
if (development) {
return developmentExecute(code, className, methodName, paramClass, args);
} else {
@@ -90,11 +93,10 @@ public class JavaCodeEngine {
* @param methodName 方法名
* @param paramClass 参数类型数组
* @param args 参数数组
- * @param 接收泛型
* @return 执行结构
*/
@SneakyThrows
- private T developmentExecute(String code, String className, String methodName, Class>[] paramClass, Object[] args) {
+ private EngineResult developmentExecute(String code, String className, String methodName, Class>[] paramClass, Object[] args) {
Class> loadClass = null;
try {
// 加锁,确保类只加载一次
@@ -123,7 +125,8 @@ public class JavaCodeEngine {
// 设置安全检查器
System.setSecurityManager(securityManager);
// 执行方法并返回结果
- return (T) method.invoke(null, args);
+ return MultiThreadedCapture.capture(() -> method.invoke(null, args));
+
} finally {
// 从缓存中移除编译好的类
classCache.remove(className);
@@ -155,10 +158,9 @@ public class JavaCodeEngine {
* @param methodName 方法名
* @param paramClass 参数类型数组
* @param args 参数数组
- * @param 接收泛型
* @return 执行结构
*/
- private T onlineExecute(String code, String className, String methodName, Class>[] paramClass, Object[] args) {
+ private EngineResult onlineExecute(String code, String className, String methodName, Class>[] paramClass, Object[] args) {
try {
Class> loadClass = null;
loadClass = classCache.get(className);
@@ -166,7 +168,8 @@ public class JavaCodeEngine {
loadClass = getLoadClass(code, className);
Method method = loadClass.getMethod(methodName, paramClass);
System.setSecurityManager(securityManager);
- return (T) method.invoke(null, args);
+ Object result = (Object) method.invoke(null, args);
+ return new EngineResult(result);
}
} catch (Exception e) {
e.printStackTrace();
@@ -215,22 +218,31 @@ public class JavaCodeEngine {
* @return 编译完成的类对象
*/
private Class> compilerClass(String className, String code, URLClassLoader classLoader) {
- log.info(code);
File tempFile = new File(CLASS_PATH + className + JAVA_SUFFIX);
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(code);
writer.close();
+ ByteArrayOutputStream errorStream = new ByteArrayOutputStream(10240);
// 编译.java文件
- compiler.run(null, null, null, tempFile.getPath());
+ compiler.run(null, null, errorStream, tempFile.getPath());
+ String trace = errorStream.toString();//存放控制台输出的字符串
+ if (!ObjectUtils.isEmpty(trace)) {
+ trace = trace.replace(CLASS_PATH + className + ".", "");
+ throw new CustomException("编译错误: " + trace);
+ }
return classLoader.loadClass(className);
} catch (Exception e) {
e.printStackTrace();
- throw new RuntimeException(e);
+ if (e instanceof CustomException) {
+ throw (CustomException) e;
+ }
+ throw new CustomException("执行或者编辑错误!");
}
}
/**
* 删除类
+ *
* @param className 删除类
* @return 删除结果
*/
diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaScriptEngine.java b/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaScriptEngine.java
index 085390d..c54dc58 100644
--- a/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaScriptEngine.java
+++ b/common/common-code/src/main/java/cn/fateverse/common/code/engine/JavaScriptEngine.java
@@ -1,9 +1,17 @@
package cn.fateverse.common.code.engine;
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
+import cn.fateverse.common.code.console.MultiThreadedCapture;
+import cn.fateverse.common.code.lock.SegmentLock;
+import cn.fateverse.common.code.model.EngineResult;
+import cn.fateverse.common.core.exception.CustomException;
+import com.alibaba.fastjson2.JSON;
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.springframework.security.core.parameters.P;
+
+import javax.script.*;
+import java.util.HashMap;
+import java.util.Map;
/**
* js 工具类
@@ -13,25 +21,60 @@ import javax.script.ScriptException;
*/
public class JavaScriptEngine {
+
+ // 创建 GraalVM 上下文
+ private static final Context context = Context.newBuilder()
+ .allowAllAccess(true)
+// .allowHostClassLoading(true)
+// .allowIO(true)
+// .allowNativeAccess(true)
+ .build();
+
+
+ private static final Map functionMap = new HashMap<>();
+
+
/**
* 执行js代码
- * @param script js脚本
- * @param function js函数名
- * @param args 参数
+ *
+ * @param script js脚本
+ * @param functionName js函数名
+ * @param args 参数
* @return 返回结构
- * @param 泛型类型
*/
- public static T executeScript(String script, String function, Object... args) {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("JavaScript");
- try {
- engine.eval(script);
- Invocable inv = (Invocable) engine;
- return (T) inv.invokeFunction(function, args);
- } catch (ScriptException | NoSuchMethodException e) {
- throw new RuntimeException(e);
+ public static EngineResult execute(String script, String functionName, boolean development, Object args) {
+ if (development) {
+ return MultiThreadedCapture.capture(() -> {
+ Context context = Context.newBuilder()
+ .allowAllAccess(true)
+ .allowHostClassLoading(true)
+ .allowIO(true)
+ .allowNativeAccess(true).build();
+ context.eval("js", script);
+ Value executeFunction = context.getBindings("js").getMember(functionName);
+ Value javaObjectAsValue = Value.asValue(args);
+ Value result = executeFunction.execute(javaObjectAsValue);
+ return result.as(Object.class);
+ });
+ } else {
+ Value executeFunction = getFunction(functionName, script);
+ Value result = executeFunction.execute(JSON.toJSONString(args));
+ return new EngineResult(result.as(Object.class));
}
}
+ private static Value getFunction(String functionName, String script) {
+ return SegmentLock.lock(functionName, () -> {
+ if (functionMap.containsKey(functionName)) {
+ return functionMap.get(functionName);
+ }
+ context.eval("js", script);
+ Value executeFunction = context.getBindings("js").getMember(functionName);
+ functionMap.put(functionName, executeFunction);
+ return executeFunction;
+ });
+ }
+
+
}
diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/model/EngineResult.java b/common/common-code/src/main/java/cn/fateverse/common/code/model/EngineResult.java
new file mode 100644
index 0000000..c3580af
--- /dev/null
+++ b/common/common-code/src/main/java/cn/fateverse/common/code/model/EngineResult.java
@@ -0,0 +1,25 @@
+package cn.fateverse.common.code.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author Clay
+ * @date 2024/4/22 17:10
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EngineResult {
+
+ private Object result;
+
+
+ private String console;
+
+
+ public EngineResult(Object result) {
+ this.result = result;
+ }
+}
diff --git a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/Encrypt.java b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/Encrypt.java
index a378b65..9f2daf5 100644
--- a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/Encrypt.java
+++ b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/Encrypt.java
@@ -12,4 +12,23 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
+
+ Position value() default Position.ALL;
+
+
+ EncryptType type() default EncryptType.SM4;
+
+
+ enum EncryptType {
+
+ SM4,
+
+ }
+
+ enum Position {
+ ALL,
+ OUT,
+ IN
+ }
+
}
diff --git a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/EncryptField.java b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/EncryptField.java
index d3d5582..f361306 100644
--- a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/EncryptField.java
+++ b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/annotation/EncryptField.java
@@ -11,4 +11,12 @@ import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
+
+ Position value() default Position.ALL;
+
+ enum Position {
+ ALL,
+ OUT,
+ IN
+ }
}
\ No newline at end of file
diff --git a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/aspect/EncryptAspect.java b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/aspect/EncryptAspect.java
index e72f998..73152dc 100644
--- a/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/aspect/EncryptAspect.java
+++ b/common/common-decrypt/src/main/java/cn/fateverse/common/decrypt/aspect/EncryptAspect.java
@@ -2,6 +2,7 @@ package cn.fateverse.common.decrypt.aspect;
import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.common.core.result.Result;
+import cn.fateverse.common.decrypt.annotation.Encrypt;
import cn.fateverse.common.decrypt.annotation.EncryptField;
import cn.fateverse.common.decrypt.service.EncryptService;
import lombok.extern.slf4j.Slf4j;
@@ -11,12 +12,10 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;
-import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
@Slf4j
@Aspect
@@ -41,36 +40,35 @@ public class EncryptAspect {
@Around("@annotation(cn.fateverse.common.decrypt.annotation.Encrypt)")
public Object decryptField(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
- //获取请求参数
- Object[] args = point.getArgs();
//获取方法
Method method = signature.getMethod();
- //获取方法参数 Parameter对象集 参数修饰符、参数名、注解及注解类型
- Parameter[] parameters = method.getParameters();
- for (int i = 0; i < parameters.length; i++) {
- Parameter parameter = parameters[i];
- //获取参数注解
- EncryptField encryptField = parameter.getAnnotation(EncryptField.class);
- Object arg = args[i];
- if (null != encryptField) {
- if (arg instanceof String) {
- String decrypt = encryptService.decrypt((String) arg);
- args[i] = decrypt;
- } else if (arg instanceof List) {
- try {
- List list = (List) arg;
- list.replaceAll(encryptService::decrypt);
- args[i] = list;
- } catch (Exception e) {
- throw new CustomException("接受参数类型错误,请使用String类型的泛型参数");
- }
- }
- } else if (parameter.getType().getName().startsWith(BASE_PACKAGE)) { //返回一个类对象,该类对象标识此参数对象表示的参数的声明类型
- decrypt(arg);
- }
+ Encrypt encrypt = method.getAnnotation(Encrypt.class);
+ if (encrypt == null) {
+ return point.proceed();
+ }
+ //获取请求参数
+ Object[] args = point.getArgs();
+ if (Encrypt.Position.ALL.equals(encrypt.value()) || Encrypt.Position.IN.equals(encrypt.value())) {
+ decryptParams(args, method);
}
//正常执行业务,最后返回的返回值为Result
Object proceed = point.proceed(args);
+ if (Encrypt.Position.ALL.equals(encrypt.value()) || Encrypt.Position.OUT.equals(encrypt.value())) {
+ Result