From c2581d2d805719640f632188f36c92b8c496cf2c Mon Sep 17 00:00:00 2001 From: clay <20932067@zju.edu.cn> Date: Mon, 29 Apr 2024 17:43:32 +0800 Subject: [PATCH] =?UTF-8?q?perf=20:=20=E5=BC=95=E6=93=8E=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code/JavaCodeAutoConfiguration.java | 4 +- ...readedCapture.java => ConsoleCapture.java} | 17 +- .../common/code/engine/JavaCodeEngine.java | 146 ++++++++++-------- .../common/code/engine/JavaScriptEngine.java | 26 ++-- .../common/code/lock/SegmentLock.java | 7 +- .../common/code/model/EngineResult.java | 5 +- .../code/sandbox/SandboxSecurityManager.java | 8 +- .../filter/AuthenticationTokenFilter.java | 2 +- .../fateverse/query/entity/DataAdapter.java | 3 +- .../fateverse/query/entity/bo/PortalBo.java | 1 - .../fateverse/query/entity/dto/MockParam.java | 2 + .../adapter/AbstractDataAdapterHandler.java | 12 +- .../impl/ExternalDataAdapterHandler.java | 75 +++++++-- .../adapter/impl/LocalDataAdapterHandler.java | 4 +- .../handler/engine/EngineExecuteHandler.java | 13 +- .../engine/impl/JavaEngineExecuteHandler.java | 14 +- .../impl/JavaScriptEngineExecuteHandler.java | 13 +- .../reader/EngineExecuteHandlerReader.java | 20 ++- .../query/portal/PortalDispatchServlet.java | 4 - .../query/service/impl/PortalServiceImpl.java | 15 +- .../mapper/PortalInterfaceMapper.xml | 7 +- .../workflow/process/TriggerService.java | 8 +- 22 files changed, 270 insertions(+), 136 deletions(-) rename common/common-code/src/main/java/cn/fateverse/common/code/console/{MultiThreadedCapture.java => ConsoleCapture.java} (80%) diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/JavaCodeAutoConfiguration.java b/common/common-code/src/main/java/cn/fateverse/common/code/JavaCodeAutoConfiguration.java index 993a65e..ceaa52f 100644 --- a/common/common-code/src/main/java/cn/fateverse/common/code/JavaCodeAutoConfiguration.java +++ b/common/common-code/src/main/java/cn/fateverse/common/code/JavaCodeAutoConfiguration.java @@ -13,8 +13,8 @@ import org.springframework.context.annotation.Bean; public class JavaCodeAutoConfiguration { @Bean - public JavaCodeEngine javaCodeEngine(JavaCodeProperties javaCodeProperties){ - return new JavaCodeEngine(javaCodeProperties); + public JavaCodeEngine javaCodeEngine(JavaCodeProperties javaCodeProperties) { + return new JavaCodeEngine(javaCodeProperties.getClassPath()); } 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/ConsoleCapture.java similarity index 80% rename from common/common-code/src/main/java/cn/fateverse/common/code/console/MultiThreadedCapture.java rename to common/common-code/src/main/java/cn/fateverse/common/code/console/ConsoleCapture.java index 491b792..c5e8fc7 100644 --- 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/ConsoleCapture.java @@ -5,19 +5,22 @@ 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 class ConsoleCapture { + /** + * 捕获方法 + * + * @param task 任務 + * @return 返回结果 + */ public static EngineResult capture(Task task) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream oldOut = System.out; @@ -27,7 +30,7 @@ public class MultiThreadedCapture { try { result = task.execute(); } catch (Exception e) { - if (e instanceof CustomException){ + if (e instanceof CustomException) { throw (CustomException) e; } throw new RuntimeException(e); 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 b15fa60..abeff1c 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 @@ -1,17 +1,16 @@ 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.console.ConsoleCapture; 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.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.util.ObjectUtils; import javax.tools.JavaCompiler; @@ -39,9 +38,7 @@ public class JavaCodeEngine { private final URL url; - private final URLClassLoader classLoader; - - private final Map> classCache = new ConcurrentHashMap<>(); + private final Map classCache = new ConcurrentHashMap<>(); private final SandboxSecurityManager securityManager = new SandboxSecurityManager(classCache); @@ -50,53 +47,31 @@ public class JavaCodeEngine { private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - public JavaCodeEngine(JavaCodeProperties javaCodeProperties) { + public JavaCodeEngine(String classPath) { try { - CLASS_PATH = javaCodeProperties.getClassPath(); + CLASS_PATH = classPath; File file = new File(CLASS_PATH); if (!file.exists()) { file.mkdirs(); } url = file.toURI().toURL(); - classLoader = new SandboxClassLoader(new URL[]{url}); } catch (MalformedURLException e) { throw new RuntimeException(e); } } - /** - * 执行方法 - * - * @param code 代码字符串 - * @param className 类名 - * @param methodName 方法名 - * @param paramClass 参数类型数组 - * @param args 参数数组 - * @param development 是否为开发环境 开发环境下会将生成的类在执行完成后删除,不是生产环境则会缓存提高运行效率 - * @return 执行结果 - */ - 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 { - return onlineExecute(code, className, methodName, paramClass, args); - } - } - - /** * 用于在开发环境中执行代码的私有方法。 * * @param code 需要执行的代码字符串 * @param className 类名 * @param methodName 方法名 - * @param paramClass 参数类型数组 * @param args 参数数组 * @return 执行结构 */ @SneakyThrows - private EngineResult developmentExecute(String code, String className, String methodName, Class[] paramClass, Object[] args) { + public EngineResult mockExecute(String code, String className, String methodName, Object[] args) { Class loadClass = null; try { // 加锁,确保类只加载一次 @@ -106,12 +81,12 @@ public class JavaCodeEngine { // 创建一个URLClassLoader,用于加载代码字符串 tempClassLoader = new URLClassLoader(new URL[]{url}); // 编译代码字符串为类 - Class tempClass = compilerClass(className, code, tempClassLoader); - // 将编译好的类放入缓存 - classCache.put(className, tempClass); - return tempClass; + return compilerClass(className, code, tempClassLoader); } catch (Exception e) { e.printStackTrace(); + if (e instanceof CustomException) { + throw (CustomException) e; + } // 异常处理,并抛出自定义的SandboxClassNotFoundException异常 throw new SandboxClassNotFoundException(e.getMessage()); } finally { @@ -121,15 +96,17 @@ public class JavaCodeEngine { } }); // 获取需要执行的方法 - Method method = loadClass.getMethod(methodName, paramClass); + Method method = getMethod(methodName, loadClass); // 设置安全检查器 System.setSecurityManager(securityManager); // 执行方法并返回结果 - return MultiThreadedCapture.capture(() -> method.invoke(null, args)); - + return ConsoleCapture.capture(() -> method.invoke(null, args)); + } catch (CustomException e) { + EngineResult result = new EngineResult(); + result.setSuccess(Boolean.FALSE); + result.setConsole(e.getMessage()); + return result; } finally { - // 从缓存中移除编译好的类 - classCache.remove(className); // 清空安全检查器 System.setSecurityManager(null); if (loadClass != null) { @@ -156,37 +133,53 @@ public class JavaCodeEngine { * @param code 需要执行的代码字符串 * @param className 类名 * @param methodName 方法名 - * @param paramClass 参数类型数组 * @param args 参数数组 * @return 执行结构 */ - private EngineResult onlineExecute(String code, String className, String methodName, Class[] paramClass, Object[] args) { + public Object execute(String code, String className, String methodName, Object[] args) { try { Class loadClass = null; - loadClass = classCache.get(className); - if (loadClass == null) { - loadClass = getLoadClass(code, className); - Method method = loadClass.getMethod(methodName, paramClass); - System.setSecurityManager(securityManager); - Object result = (Object) method.invoke(null, args); - return new EngineResult(result); + //从缓存中获取 + CacheWrapper wrapper = classCache.get(className); + //缓存中不存在 + if (wrapper == null) { + //加载 + wrapper = getLoadClass(code, className); } + //获取到类信息 + loadClass = wrapper.getClazz(); + //获取方法 + Method method = getMethod(methodName, loadClass); + //开启安全模式 + System.setSecurityManager(securityManager); + //执行方法 + return method.invoke(null, args); } catch (Exception e) { + remove(className); e.printStackTrace(); } finally { System.setSecurityManager(null); - File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX); - if (javaFile.exists()) { - javaFile.delete(); - } - File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX); - if (classFile.exists()) { - classFile.delete(); - } } return null; } + /** + * 获取到方法 + * + * @param methodName 方法名称 + * @param loadClass 类信息 + * @return 方法对象 + */ + private Method getMethod(String methodName, Class loadClass) { + Method method = null; + for (Method declaredMethod : loadClass.getDeclaredMethods()) { + if (declaredMethod.getName().equals(methodName)) { + method = declaredMethod; + } + } + return method; + } + /** * 获取到编译完成的Class对象 * @@ -194,15 +187,18 @@ public class JavaCodeEngine { * @param className 类名 * @return 编译后的Java对象 */ - private Class getLoadClass(String code, String className) { + private CacheWrapper getLoadClass(String code, String className) { //使用分段锁,提高效率,放多并发情况下多次对同一个类进行编译 return SegmentLock.lock(className, () -> { try { + URLClassLoader classLoader = new SandboxClassLoader(new URL[]{url}); //执行编译 Class tempClass = compilerClass(className, code, classLoader); + //创建缓存包装对象 + CacheWrapper wrapper = new CacheWrapper(tempClass, classLoader); //将编译之后的类对象放在缓存中,提高线上环境的运行效率 - classCache.put(className, tempClass); - return tempClass; + classCache.put(className, wrapper); + return wrapper; } catch (Exception e) { throw new RuntimeException(e); } @@ -248,11 +244,19 @@ public class JavaCodeEngine { */ public Boolean remove(String className) { return SegmentLock.lock(className, () -> { - classCache.remove(className); + CacheWrapper wrapper = classCache.get(className); + if (wrapper != null) { + classCache.remove(className); + wrapper.remove(); + } + //进行gc 垃圾挥手 + System.gc(); + //删除Java文件 File javaFile = new File(CLASS_PATH + className + JAVA_SUFFIX); if (javaFile.exists()) { javaFile.delete(); } + //删除class文件 File classFile = new File(CLASS_PATH + className + CLASS_SUFFIX); if (classFile.exists()) { classFile.delete(); @@ -260,4 +264,24 @@ public class JavaCodeEngine { return true; }); } + + + @Getter + public static class CacheWrapper { + + private Class clazz; + + private URLClassLoader classLoader; + + public CacheWrapper(Class clazz, URLClassLoader classLoader) { + this.clazz = clazz; + this.classLoader = classLoader; + } + + + public void remove(){ + clazz = null; + classLoader = null; + } + } } 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 e75db28..7809420 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,15 +1,13 @@ package cn.fateverse.common.code.engine; -import cn.fateverse.common.code.console.MultiThreadedCapture; +import cn.fateverse.common.code.console.ConsoleCapture; 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; @@ -42,9 +40,16 @@ public class JavaScriptEngine { * @param args 参数 * @return 返回结构 */ - public static EngineResult execute(String script, String functionName, boolean development, Object args) { - if (development) { - return MultiThreadedCapture.capture(() -> { + public static Object execute(String script, String functionName, Object args) { + Value executeFunction = getFunction(functionName, script); + Value result = executeFunction.execute(JSON.toJSONString(args)); + return result.as(Object.class); + } + + + public static EngineResult mockExecute(String script, String functionName, Object args) { + try { + return ConsoleCapture.capture(() -> { Context context = Context.newBuilder() .allowAllAccess(true) // .allowHostClassLoading(true) @@ -63,10 +68,11 @@ public class JavaScriptEngine { 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)); + }catch (CustomException e){ + EngineResult result = new EngineResult(); + result.setSuccess(Boolean.FALSE); + result.setConsole(e.getMessage()); + return result; } } diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/lock/SegmentLock.java b/common/common-code/src/main/java/cn/fateverse/common/code/lock/SegmentLock.java index 48933de..c21c459 100644 --- a/common/common-code/src/main/java/cn/fateverse/common/code/lock/SegmentLock.java +++ b/common/common-code/src/main/java/cn/fateverse/common/code/lock/SegmentLock.java @@ -6,6 +6,8 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; /** + * 分段锁对象 + * * @author Clay * @date 2023-10-25 */ @@ -15,10 +17,11 @@ public class SegmentLock { /** * 分段锁 - * @param key 锁名称 + * + * @param key 锁名称 * @param supplier 需要执行的函数 + * @param 接收泛型 * @return 执行后的结果 - * @param 接收泛型 */ public static T lock(String key, Supplier supplier) { ReentrantLock lock = lockMap.get(key); 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 index c3580af..2f6b8a1 100644 --- 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 @@ -18,8 +18,11 @@ public class EngineResult { private String console; + private Boolean success; - public EngineResult(Object result) { + public EngineResult(Object result, String console) { + success = true; this.result = result; + this.console = console; } } diff --git a/common/common-code/src/main/java/cn/fateverse/common/code/sandbox/SandboxSecurityManager.java b/common/common-code/src/main/java/cn/fateverse/common/code/sandbox/SandboxSecurityManager.java index 72c1d12..894bfc2 100644 --- a/common/common-code/src/main/java/cn/fateverse/common/code/sandbox/SandboxSecurityManager.java +++ b/common/common-code/src/main/java/cn/fateverse/common/code/sandbox/SandboxSecurityManager.java @@ -1,5 +1,7 @@ package cn.fateverse.common.code.sandbox; +import cn.fateverse.common.code.engine.JavaCodeEngine; + import java.io.FilePermission; import java.lang.reflect.ReflectPermission; import java.security.Permission; @@ -9,9 +11,9 @@ import java.util.Set; public class SandboxSecurityManager extends SecurityManager { - private final Map> classCache; + private final Map classCache; - public SandboxSecurityManager(Map> classCache) { + public SandboxSecurityManager(Map classCache) { this.classCache = classCache; } @@ -38,7 +40,7 @@ public class SandboxSecurityManager extends SecurityManager { private boolean isAllowedPermission(Permission permission) { //权限:用于校验文件系统访问权限,包括读取、写入、删除文件,以及目录操作。权限名称可能包括文件路径和操作,如 "read", "write", "delete", "execute" 等。 - if (permission instanceof FilePermission){ + if (permission instanceof FilePermission) { System.out.println("触发文件读写权限"); return false; } diff --git a/common/common-security/src/main/java/cn/fateverse/common/security/filter/AuthenticationTokenFilter.java b/common/common-security/src/main/java/cn/fateverse/common/security/filter/AuthenticationTokenFilter.java index 7c594c7..8310eea 100644 --- a/common/common-security/src/main/java/cn/fateverse/common/security/filter/AuthenticationTokenFilter.java +++ b/common/common-security/src/main/java/cn/fateverse/common/security/filter/AuthenticationTokenFilter.java @@ -1,6 +1,5 @@ package cn.fateverse.common.security.filter; -import cn.fateverse.common.core.utils.ObjectUtils; import cn.fateverse.common.security.service.TokenService; import cn.fateverse.common.security.entity.LoginUser; import cn.fateverse.common.security.utils.SecurityUtils; @@ -8,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.ObjectUtils; import org.springframework.web.filter.OncePerRequestFilter; import javax.annotation.Resource; diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/DataAdapter.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/DataAdapter.java index a4419b4..3e4b3f9 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/DataAdapter.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/DataAdapter.java @@ -69,10 +69,11 @@ public class DataAdapter extends BaseEntity { this.code = "\n" + "import java.util.*;\n" + "import java.util.stream.*;\n" + + "import com.alibaba.fastjson2.*;\n" + "\n" + "public class DataAdapter {\n" + "\n" + - " public static Object execute(List> data) {\n" + + " public static Object execute(Object data) {\n" + " return data;\n" + " }\n" + "}\n"; diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java index 32b1b01..e83de0d 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/bo/PortalBo.java @@ -29,7 +29,6 @@ public class PortalBo implements Serializable { private Boolean createDataAdapter; private Boolean page; private String path; - private String url; private Integer state; private List mappings; diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/MockParam.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/MockParam.java index 0cadc5b..d8fc541 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/MockParam.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/entity/dto/MockParam.java @@ -29,5 +29,7 @@ public class MockParam { private String key; private Object value; + + private String type; } } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/AbstractDataAdapterHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/AbstractDataAdapterHandler.java index 269d7f8..7dff1bb 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/AbstractDataAdapterHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/AbstractDataAdapterHandler.java @@ -23,7 +23,7 @@ public abstract class AbstractDataAdapterHandler implements DataAdapterHandler { this.handlerReader = handlerReader; } - protected Object execute(Long adapterId, Object data, boolean development) { + protected Object execute(Long adapterId, Object data) { if (ObjectUtils.isEmpty(adapterId)) { return data; } @@ -33,15 +33,15 @@ public abstract class AbstractDataAdapterHandler implements DataAdapterHandler { throw new RuntimeException("dataAdapter is null"); } handlerReader.preconditioning(dataAdapter); - EngineResult execute = handlerReader.execute(dataAdapter, data, development); - if (ObjectUtils.isEmpty(execute)) { + Object result = handlerReader.execute(dataAdapter, data); + if (ObjectUtils.isEmpty(result)) { throw new RuntimeException("执行结果错误"); } - return execute.getResult(); + return result; } - protected EngineResult mockExecute(Long adapterId, String code, Object data, boolean development) { + protected EngineResult mockExecute(Long adapterId, String code, Object data) { //获取当当前接口对应的数据适配器 DataAdapter dataAdapter = dataAdapterMapper.selectById(adapterId); if (ObjectUtils.isEmpty(dataAdapter)) { @@ -49,7 +49,7 @@ public abstract class AbstractDataAdapterHandler implements DataAdapterHandler { } dataAdapter.setCode(code); handlerReader.preconditioning(dataAdapter); - EngineResult execute = handlerReader.execute(dataAdapter, data, development); + EngineResult execute = handlerReader.mockExecute(dataAdapter, data); if (ObjectUtils.isEmpty(execute)) { throw new RuntimeException("执行结果错误"); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/ExternalDataAdapterHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/ExternalDataAdapterHandler.java index 0c0c857..ecd1750 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/ExternalDataAdapterHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/ExternalDataAdapterHandler.java @@ -1,12 +1,15 @@ package cn.fateverse.query.handler.adapter.impl; import cn.fateverse.common.core.exception.CustomException; +import cn.fateverse.query.entity.PortalInterface; +import cn.fateverse.query.entity.PortalMapping; import cn.fateverse.query.entity.dto.MockParam; import cn.fateverse.query.entity.bo.PortalBo; import cn.fateverse.query.enums.PortalEnum; import cn.fateverse.query.handler.adapter.AbstractDataAdapterHandler; import cn.fateverse.query.handler.reader.EngineExecuteHandlerReader; import cn.fateverse.query.mapper.DataAdapterMapper; +import cn.fateverse.query.mapper.PortalInterfaceMapper; import com.alibaba.fastjson2.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -17,6 +20,7 @@ import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; +import java.util.Map; /** * @author Clay @@ -31,11 +35,15 @@ public class ExternalDataAdapterHandler extends AbstractDataAdapterHandler { */ private final RestTemplate restTemplate; - public ExternalDataAdapterHandler(DataAdapterMapper dataAdapterMapper, + private final PortalInterfaceMapper portalInterfaceMapper; + + public ExternalDataAdapterHandler(RestTemplate restTemplate, + DataAdapterMapper dataAdapterMapper, EngineExecuteHandlerReader handlerReader, - RestTemplate restTemplate) { + PortalInterfaceMapper portalInterfaceMapper) { super(dataAdapterMapper, handlerReader); this.restTemplate = restTemplate; + this.portalInterfaceMapper = portalInterfaceMapper; } @@ -44,18 +52,28 @@ public class ExternalDataAdapterHandler extends AbstractDataAdapterHandler { if (!PortalEnum.EXTERNAL.equals(portal.getType())) { return null; } + PortalInterface portalInterface = portalInterfaceMapper.selectById(portal.getInterfaceId()); + if (ObjectUtils.isEmpty(portalInterface)) { + throw new CustomException("接口不存在"); + } HttpHeaders headers = new HttpHeaders(); - MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8"); + MediaType type = MediaType.parseMediaType(portalInterface.getContentType()); headers.setContentType(type); - headers.add("Accept", MediaType.APPLICATION_JSON.toString()); - HashMap map = new HashMap<>(); + Map requestParams = new HashMap<>(); + if (!ObjectUtils.isEmpty(mockParam.getParams())) { + for (MockParam.Param param : mockParam.getParams()) { + if (!ObjectUtils.isEmpty(param.getKey()) && !ObjectUtils.isEmpty(param.getValue())) { + requestParams.put(param.getKey(), param.getValue()); + } + } + } JSONObject response = null; - switch (portal.getRequestMethod()) { + switch (portalInterface.getRequestMethod()) { case "GET": - response = restTemplate.getForObject(portal.getUrl(), JSONObject.class, map); + response = restTemplate.getForObject(portalInterface.getUrl(), JSONObject.class, requestParams); break; case "POST": - response = restTemplate.postForObject(portal.getUrl(), map, JSONObject.class); + response = restTemplate.postForObject(portalInterface.getUrl(), requestParams, JSONObject.class); break; default: throw new CustomException("请求方式错误"); @@ -63,12 +81,49 @@ public class ExternalDataAdapterHandler extends AbstractDataAdapterHandler { if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) { return response; } else { - return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), response, true); + return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), response); } } @Override public Object execute(PortalBo portal, HttpServletRequest request) { - return null; + if (!PortalEnum.EXTERNAL.equals(portal.getType())) { + return null; + } + PortalInterface portalInterface = portalInterfaceMapper.selectById(portal.getInterfaceId()); + if (ObjectUtils.isEmpty(portalInterface)) { + throw new CustomException("接口不存在"); + } + HttpHeaders headers = new HttpHeaders(); + MediaType type = MediaType.parseMediaType(portalInterface.getContentType()); + headers.setContentType(type); + Map requestParams = new HashMap<>(); + for (PortalMapping mapping : portal.getMappings()) { + String mappingValue = mapping.getMappingValue(); + String mappingKey = mapping.getMappingKey(); + if (mapping.getMappingType() == 0) { + requestParams.put(mappingKey, request.getParameter(mappingValue)); + } else if (mapping.getMappingType() == 1) { + headers.add(mappingKey, request.getHeader(mappingValue)); + } else { + requestParams.put(mappingKey, request.getParameter(mappingValue)); + } + } + JSONObject response = null; + switch (portalInterface.getRequestMethod()) { + case "GET": + response = restTemplate.getForObject(portalInterface.getUrl(), JSONObject.class, requestParams); + break; + case "POST": + response = restTemplate.postForObject(portalInterface.getUrl(), requestParams, JSONObject.class); + break; + default: + throw new CustomException("请求方式错误"); + } + if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) { + return response; + } else { + return super.execute(portal.getAdapterId(), response); + } } } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java index eca8562..7cfcc7a 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/adapter/impl/LocalDataAdapterHandler.java @@ -69,7 +69,7 @@ public class LocalDataAdapterHandler extends AbstractDataAdapterHandler { if (ObjectUtils.isEmpty(portal.getAdapterId()) || !portal.getCreateDataAdapter()) { return tableDataInfo.getRows(); } else { - return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), tableDataInfo.getRows(), true); + return super.mockExecute(portal.getAdapterId(), mockParam.getCode(), tableDataInfo.getRows()); } } @@ -103,7 +103,7 @@ public class LocalDataAdapterHandler extends AbstractDataAdapterHandler { //根据设置的参数动态调整当前是否需要分页操作 TableDataInfo> tableDataInfo = dynamicDataSearchService.searchData(uniConList, query, null, Boolean.TRUE); if (portal.getCreateDataAdapter()) { - return super.execute(portal.getAdapterId(), tableDataInfo.getRows(), false); + return super.execute(portal.getAdapterId(), tableDataInfo.getRows()); } else { return tableDataInfo.getRows(); } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/EngineExecuteHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/EngineExecuteHandler.java index 5db4504..1eb1b42 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/EngineExecuteHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/EngineExecuteHandler.java @@ -11,10 +11,18 @@ public interface EngineExecuteHandler { * * @param dataAdapter 数据适配器 * @param data 数据列表 - * @param development * @return JSONObject对象 */ - EngineResult execute(DataAdapter dataAdapter, Object data, boolean development); + Object execute(DataAdapter dataAdapter, Object data); + + /** + * 模拟执行方法 + * + * @param dataAdapter 数据适配器 + * @param data 数据列表 + * @return JSONObject对象 + */ + EngineResult mockExecute(DataAdapter dataAdapter, Object data); /** * 预处理数据适配器 @@ -31,4 +39,5 @@ public interface EngineExecuteHandler { * @return 删除结果 */ Boolean remove(DataAdapter dataAdapter); + } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaEngineExecuteHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaEngineExecuteHandler.java index 431f03d..1d09ef7 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaEngineExecuteHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaEngineExecuteHandler.java @@ -9,7 +9,6 @@ import cn.fateverse.query.handler.engine.EngineExecuteHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,12 +31,18 @@ public class JavaEngineExecuteHandler implements EngineExecuteHandler { @Override - public EngineResult execute(DataAdapter dataAdapter, Object data, boolean development) { + public Object execute(DataAdapter dataAdapter, Object data) { if (!DataAdapterType.JAVA.equals(dataAdapter.getType())) { return null; } return javaCodeEngine.execute(dataAdapter.getExecuteCode(), getClassName(dataAdapter), - "execute", new Class[]{List.class}, new Object[]{data}, development); + "execute", new Object[]{data}); + } + + @Override + public EngineResult mockExecute(DataAdapter dataAdapter, Object data) { + return javaCodeEngine.mockExecute(dataAdapter.getExecuteCode(), getClassName(dataAdapter), + "execute", new Object[]{data}); } private static String getClassName(DataAdapter dataAdapter) { @@ -53,7 +58,8 @@ public class JavaEngineExecuteHandler implements EngineExecuteHandler { String modifiedCode = modifiedCode(dataAdapter.getCode()); String replacedCode = replacedClass(modifiedCode, getClassName(dataAdapter)); String IMPORT_CODE = "import java.util.*;\n" + - "import java.util.stream.*;\n"; + "import java.util.stream.*;\n" + + "import com.alibaba.fastjson2.*;"; dataAdapter.setExecuteCode(IMPORT_CODE + replacedCode); return Boolean.TRUE; } diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaScriptEngineExecuteHandler.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaScriptEngineExecuteHandler.java index 9f08e1a..9a512f0 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaScriptEngineExecuteHandler.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/engine/impl/JavaScriptEngineExecuteHandler.java @@ -23,11 +23,20 @@ import java.util.regex.Pattern; public class JavaScriptEngineExecuteHandler implements EngineExecuteHandler { @Override - public EngineResult execute(DataAdapter dataAdapter, Object data, boolean development) { + public Object execute(DataAdapter dataAdapter, Object data) { if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) { return null; } - return JavaScriptEngine.execute(dataAdapter.getExecuteCode(), "execute" + dataAdapter.getAdapterId(), development, data); + return JavaScriptEngine.execute(dataAdapter.getExecuteCode(), "execute" + dataAdapter.getAdapterId(), data); + } + + @Override + public EngineResult mockExecute(DataAdapter dataAdapter, Object data) { + if (!DataAdapterType.JAVA_SCRIPT.equals(dataAdapter.getType())) { + return null; + } + return JavaScriptEngine.mockExecute(dataAdapter.getExecuteCode(), "execute" + dataAdapter.getAdapterId(), data); + } @Override diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/EngineExecuteHandlerReader.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/EngineExecuteHandlerReader.java index 60a3e93..06b98d2 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/EngineExecuteHandlerReader.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/handler/reader/EngineExecuteHandlerReader.java @@ -48,11 +48,11 @@ public class EngineExecuteHandlerReader { * @param data 数据列表 * @return 执行结果 */ - public EngineResult execute(DataAdapter dataAdapter, Object data, boolean development) { + public Object execute(DataAdapter dataAdapter, Object data) { // 遍历引擎执行处理器列表 for (EngineExecuteHandler engineExecuteHandler : handlerList) { // 执行数据适配器的处理方法 - EngineResult result = engineExecuteHandler.execute(dataAdapter, data, development); + Object result = engineExecuteHandler.execute(dataAdapter, data); if (result != null) { return result; } @@ -60,6 +60,22 @@ public class EngineExecuteHandlerReader { // 若未找到匹配的数据适配器处理器,则返回null return null; } + public EngineResult mockExecute(DataAdapter dataAdapter, Object data) { + // 遍历引擎执行处理器列表 + for (EngineExecuteHandler engineExecuteHandler : handlerList) { + // 执行数据适配器的处理方法 + EngineResult result = engineExecuteHandler.mockExecute(dataAdapter, data); + if (result != null) { + return result; + } + } + // 若未找到匹配的数据适配器处理器,则返回null + return null; + } + + + + public Boolean remove(DataAdapter dataAdapter) { diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java index cf443cf..281755e 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/portal/PortalDispatchServlet.java @@ -3,12 +3,9 @@ package cn.fateverse.query.portal; import cn.fateverse.common.core.result.Result; import cn.fateverse.common.security.utils.ResponseRender; import cn.fateverse.query.constant.QueryConstant; -import cn.fateverse.query.entity.DataAdapter; import cn.fateverse.query.entity.Portal; import cn.fateverse.query.entity.PortalMapping; import cn.fateverse.query.entity.bo.PortalBo; -import cn.fateverse.query.entity.dto.SearchInfo; -import cn.fateverse.query.entity.dto.UniConDto; import cn.fateverse.query.handler.reader.DataAdapterHandlerReader; import cn.fateverse.query.mapper.DataAdapterMapper; import cn.fateverse.query.mapper.PortalMapper; @@ -20,7 +17,6 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; import java.util.List; /** diff --git a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java index b6d6963..5aa2834 100644 --- a/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java +++ b/custom-query/custom-query-biz/src/main/java/cn/fateverse/query/service/impl/PortalServiceImpl.java @@ -77,7 +77,7 @@ public class PortalServiceImpl implements PortalService { this.handlerReader = handlerReader; taskExecuteExecutor.execute(() -> { PortalQuery query = new PortalQuery(); - query.setState(QueryConstant.PORTAL_PUBLISH); +// query.setState(QueryConstant.PORTAL_PUBLISH); List portalList = portalMapper.selectList(query); if (ObjectUtils.isEmpty(portalList)) { log.info("portal is empty!"); @@ -247,9 +247,13 @@ public class PortalServiceImpl implements PortalService { if (!ObjectUtils.isEmpty(old)) { throw new CustomException("系统中存在当前请求路径"); } - UniQuery uniQuery = queryMapper.selectById(portal.getQueryId()); - if (ObjectUtils.isEmpty(uniQuery)) { - throw new CustomException("未找到对应的查询接口!"); + if (PortalEnum.EXTERNAL.equals(portal.getType())) { + createPortalInterface(portalDto, portal); + }else { + UniQuery uniQuery = queryMapper.selectById(portal.getQueryId()); + if (ObjectUtils.isEmpty(uniQuery)) { + throw new CustomException("未找到对应的查询接口!"); + } } portal.setState(QueryConstant.PORTAL_DEV); // 判断是否需要创建数据适配器 @@ -261,9 +265,6 @@ public class PortalServiceImpl implements PortalService { throw new CustomException("未找到对应的数据适配器!"); } } - if (PortalEnum.EXTERNAL.equals(portal.getType())) { - createPortalInterface(portalDto, portal); - } portalMapper.insert(portal); List mappings = portalDto.getMappings(); if (!ObjectUtils.isEmpty(mappings)) { diff --git a/custom-query/custom-query-biz/src/main/resources/mapper/PortalInterfaceMapper.xml b/custom-query/custom-query-biz/src/main/resources/mapper/PortalInterfaceMapper.xml index c1d54dd..f24051c 100644 --- a/custom-query/custom-query-biz/src/main/resources/mapper/PortalInterfaceMapper.xml +++ b/custom-query/custom-query-biz/src/main/resources/mapper/PortalInterfaceMapper.xml @@ -5,19 +5,20 @@ - insert into portal_interface (interface_id, content_type, request_method) - values (#{interfaceId}, #{contentType}, #{requestMethod}) + insert into portal_interface (interface_id, url, content_type, request_method) + values (#{interfaceId}, #{url}, #{contentType}, #{requestMethod}) update portal_interface set content_type = #{contentType}, + url = #{url}, request_method = #{requestMethod} where interface_id = #{interfaceId} diff --git a/workflow/src/main/java/cn/fateverse/workflow/process/TriggerService.java b/workflow/src/main/java/cn/fateverse/workflow/process/TriggerService.java index a4ecdbb..3e62d8e 100644 --- a/workflow/src/main/java/cn/fateverse/workflow/process/TriggerService.java +++ b/workflow/src/main/java/cn/fateverse/workflow/process/TriggerService.java @@ -1,7 +1,6 @@ package cn.fateverse.workflow.process; import cn.fateverse.common.code.engine.JavaScriptEngine; -import cn.fateverse.common.code.model.EngineResult; import cn.fateverse.workflow.constant.ProcessConstant; import cn.fateverse.workflow.entity.bpmn.*; import cn.hutool.core.util.StrUtil; @@ -159,14 +158,14 @@ public class TriggerService { } //获取到请求的返回结果 Map result = response.getBody(); - EngineResult engineResult; + ScriptObjectMirror jsResult; try { //判断请求是否有效 if (response.getStatusCode() == HttpStatus.OK) { - engineResult = JavaScriptEngine.execute(http.getSuccess(), "handlerSuccess", false, result); + jsResult = (ScriptObjectMirror) JavaScriptEngine.execute(http.getSuccess(), "handlerSuccess", result); operation.setState(OperationStateEnums.SUCCESS); } else { - engineResult = JavaScriptEngine.execute(http.getFail(), "handlerFail", false, result); + jsResult = (ScriptObjectMirror) JavaScriptEngine.execute(http.getFail(), "handlerFail", result); operation.setState(OperationStateEnums.FAILURE); } } catch (Exception e) { @@ -178,7 +177,6 @@ public class TriggerService { } return; } - ScriptObjectMirror jsResult = (ScriptObjectMirror) engineResult.getResult(); //获取到自定义脚本的状态 boolean state = (Boolean) jsResult.get("state"); //获取到js脚本中的内容