Explorar el Código

优化script执行器的逻辑

everywhere.z hace 2 años
padre
commit
f9c83ea311

+ 72 - 6
liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java

@@ -1,6 +1,17 @@
 package com.yomahub.liteflow.script;
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.yomahub.liteflow.annotation.util.AnnoUtil;
+import com.yomahub.liteflow.context.ContextBean;
 import com.yomahub.liteflow.enums.ScriptTypeEnum;
+import com.yomahub.liteflow.exception.LiteFlowException;
+import com.yomahub.liteflow.slot.DataBus;
+import com.yomahub.liteflow.slot.Slot;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
 
 /**
  * 脚本执行器接口
@@ -8,16 +19,71 @@ import com.yomahub.liteflow.enums.ScriptTypeEnum;
  * @author Bryan.Zhang
  * @since 2.6.0
  */
-public interface ScriptExecutor {
+public abstract class ScriptExecutor {
+
+	public ScriptExecutor init(){
+		return this;
+	}
+
+	public abstract void load(String nodeId, String script);
+
+	public Object execute(ScriptExecuteWrap wrap) throws Exception{
+		try {
+			return executeScript(wrap);
+		}catch (Exception e) {
+			if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof LiteFlowException) {
+				throw (LiteFlowException) e.getCause();
+			}
+			else if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof RuntimeException) {
+				throw (RuntimeException) e.getCause();
+			}
+			else {
+				throw e;
+			}
+		}
+	}
+
+	public abstract Object executeScript(ScriptExecuteWrap wrap) throws Exception;
+
+	public abstract void cleanCache();
+
+	public abstract ScriptTypeEnum scriptType();
+
+	public void bindParam(ScriptExecuteWrap wrap, BiConsumer<String, Object> putConsumer, BiConsumer<String, Object> putIfAbsentConsumer){
+		// 往脚本语言绑定表里循环增加绑定上下文的key
+		// key的规则为自定义上下文的simpleName
+		// 比如你的自定义上下文为AbcContext,那么key就为:abcContext
+		// 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法,而不是参数,所以脚本语言的绑定表里也是放多个上下文
+		DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
+			ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class);
+			String key;
+			if (contextBean != null && contextBean.value().trim().length() > 0) {
+				key = contextBean.value();
+			}
+			else {
+				key = StrUtil.lowerFirst(o.getClass().getSimpleName());
+			}
+			putConsumer.accept(key, o);
+		});
 
-	ScriptExecutor init();
+		// 把wrap对象转换成元数据map
+		Map<String, Object> metaMap = BeanUtil.beanToMap(wrap);
 
-	void load(String nodeId, String script);
+		// 在元数据里放入主Chain的流程参数
+		Slot slot = DataBus.getSlot(wrap.getSlotIndex());
+		metaMap.put("requestData", slot.getRequestData());
 
-	Object execute(ScriptExecuteWrap wrap) throws Exception;
+		// 如果有隐式流程,则放入隐式流程的流程参数
+		Object subRequestData = slot.getChainReqData(wrap.getCurrChainId());
+		if (ObjectUtil.isNotNull(subRequestData)) {
+			metaMap.put("subRequestData", subRequestData);
+		}
 
-	void cleanCache();
+		// 往脚本上下文里放入元数据
+		putConsumer.accept("_meta", metaMap);
 
-	ScriptTypeEnum scriptType();
+		// 放入用户自己定义的bean
+		ScriptBeanManager.getScriptBeanMap().forEach(putIfAbsentConsumer);
+	}
 
 }

+ 10 - 64
liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java

@@ -1,24 +1,16 @@
 package com.yomahub.liteflow.script.jsr223;
 
-import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
-import com.yomahub.liteflow.annotation.util.AnnoUtil;
-import com.yomahub.liteflow.context.ContextBean;
 import com.yomahub.liteflow.exception.LiteFlowException;
-import com.yomahub.liteflow.script.ScriptBeanManager;
 import com.yomahub.liteflow.script.ScriptExecuteWrap;
 import com.yomahub.liteflow.script.ScriptExecutor;
 import com.yomahub.liteflow.script.exception.ScriptLoadException;
-import com.yomahub.liteflow.slot.DataBus;
-import com.yomahub.liteflow.slot.Slot;
 import com.yomahub.liteflow.util.CopyOnWriteHashMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import javax.script.*;
 import java.util.Map;
-import java.util.Optional;
 
 /**
  * JSR223 script engine的统一实现抽象类
@@ -26,7 +18,7 @@ import java.util.Optional;
  * @author Bryan.Zhang
  * @since 2.9.5
  */
-public abstract class JSR223ScriptExecutor implements ScriptExecutor {
+public abstract class JSR223ScriptExecutor extends ScriptExecutor {
 
 	protected final Logger log = LoggerFactory.getLogger(this.getClass());
 
@@ -59,64 +51,18 @@ public abstract class JSR223ScriptExecutor implements ScriptExecutor {
 	}
 
 	@Override
-	public Object execute(ScriptExecuteWrap wrap) throws Exception {
-		try {
-			if (!compiledScriptMap.containsKey(wrap.getNodeId())) {
-				String errorMsg = StrUtil.format("script for node[{}] is not loaded", wrap.getNodeId());
-				throw new ScriptLoadException(errorMsg);
-			}
-
-			CompiledScript compiledScript = compiledScriptMap.get(wrap.getNodeId());
-			Bindings bindings = new SimpleBindings();
-
-			// 往脚本语言绑定表里循环增加绑定上下文的key
-			// key的规则为自定义上下文的simpleName
-			// 比如你的自定义上下文为AbcContext,那么key就为:abcContext
-			// 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法,而不是参数,所以脚本语言的绑定表里也是放多个上下文
-			DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
-				ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class);
-				String key;
-				if (contextBean != null && contextBean.value().trim().length() > 0) {
-					key = contextBean.value();
-				}
-				else {
-					key = StrUtil.lowerFirst(o.getClass().getSimpleName());
-				}
-				bindings.put(key, o);
-			});
-
-			// 把wrap对象转换成元数据map
-			Map<String, Object> metaMap = BeanUtil.beanToMap(wrap);
-
-			// 在元数据里放入主Chain的流程参数
-			Slot slot = DataBus.getSlot(wrap.getSlotIndex());
-			metaMap.put("requestData", slot.getRequestData());
-
-			// 如果有隐式流程,则放入隐式流程的流程参数
-			Object subRequestData = slot.getChainReqData(wrap.getCurrChainId());
-			if (ObjectUtil.isNotNull(subRequestData)) {
-				metaMap.put("subRequestData", subRequestData);
-			}
+	public Object executeScript(ScriptExecuteWrap wrap) throws Exception {
+		if (!compiledScriptMap.containsKey(wrap.getNodeId())) {
+			String errorMsg = StrUtil.format("script for node[{}] is not loaded", wrap.getNodeId());
+			throw new ScriptLoadException(errorMsg);
+		}
 
-			// 往脚本上下文里放入元数据
-			bindings.put("_meta", metaMap);
+		CompiledScript compiledScript = compiledScriptMap.get(wrap.getNodeId());
+		Bindings bindings = new SimpleBindings();
 
-			// 放入用户自己定义的bean
-			ScriptBeanManager.getScriptBeanMap().forEach(bindings::putIfAbsent);
+		bindParam(wrap, bindings::put, bindings::putIfAbsent);
 
-			return compiledScript.eval(bindings);
-		}
-		catch (Exception e) {
-			if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof LiteFlowException) {
-				throw (LiteFlowException) e.getCause();
-			}
-			else if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof RuntimeException) {
-				throw (RuntimeException) e.getCause();
-			}
-			else {
-				throw e;
-			}
-		}
+		return compiledScript.eval(bindings);
 	}
 
 	@Override

+ 5 - 44
liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java

@@ -1,17 +1,10 @@
 package com.yomahub.liteflow.script.graaljs;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
-import com.yomahub.liteflow.annotation.util.AnnoUtil;
-import com.yomahub.liteflow.context.ContextBean;
 import com.yomahub.liteflow.enums.ScriptTypeEnum;
-import com.yomahub.liteflow.script.ScriptBeanManager;
 import com.yomahub.liteflow.script.ScriptExecuteWrap;
 import com.yomahub.liteflow.script.ScriptExecutor;
 import com.yomahub.liteflow.script.exception.ScriptLoadException;
-import com.yomahub.liteflow.slot.DataBus;
-import com.yomahub.liteflow.slot.Slot;
 import com.yomahub.liteflow.util.CopyOnWriteHashMap;
 import org.graalvm.polyglot.Context;
 import org.graalvm.polyglot.Engine;
@@ -25,7 +18,7 @@ import java.util.Map;
  * @author zendwang
  * @since 2.9.4
  */
-public class GraalJavaScriptExecutor implements ScriptExecutor {
+public class GraalJavaScriptExecutor extends ScriptExecutor {
 
 	private final Map<String, Source> scriptMap = new CopyOnWriteHashMap<>();
 
@@ -50,49 +43,17 @@ public class GraalJavaScriptExecutor implements ScriptExecutor {
 	}
 
 	@Override
-	public Object execute(ScriptExecuteWrap wrap) throws Exception {
+	public Object executeScript(ScriptExecuteWrap wrap) {
 		if (!scriptMap.containsKey(wrap.getNodeId())) {
 			String errorMsg = StrUtil.format("script for node[{}] is not loaded", wrap.getNodeId());
 			throw new ScriptLoadException(errorMsg);
 		}
 		try (Context context = Context.newBuilder().allowAllAccess(true).engine(this.engine).build()) {
 			Value bindings = context.getBindings("js");
-			// 往脚本语言绑定表里循环增加绑定上下文的key
-			// key的规则为自定义上下文的simpleName
-			// 比如你的自定义上下文为AbcContext,那么key就为:abcContext
-			// 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法,而不是参数,所以脚本语言的绑定表里也是放多个上下文
-			DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
-				ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class);
-				String key;
-				if (contextBean != null && contextBean.value().trim().length() > 0) {
-					key = contextBean.value();
-				}
-				else {
-					key = StrUtil.lowerFirst(o.getClass().getSimpleName());
-				}
-				bindings.putMember(key, o);
-			});
-
-			// 把wrap对象转换成元数据map
-			Map<String, Object> metaMap = BeanUtil.beanToMap(wrap);
-
-			// 在元数据里放入主Chain的流程参数
-			Slot slot = DataBus.getSlot(wrap.getSlotIndex());
-			metaMap.put("requestData", slot.getRequestData());
-
-			// 如果有隐式流程,则放入隐式流程的流程参数
-			Object subRequestData = slot.getChainReqData(wrap.getCurrChainId());
-			if (ObjectUtil.isNotNull(subRequestData)) {
-				metaMap.put("subRequestData", subRequestData);
-			}
-
-			// 往脚本上下文里放入元数据
-			bindings.putMember("_meta", metaMap);
 
-			// 放入用户自己定义的bean
-			ScriptBeanManager.getScriptBeanMap().forEach((key, value) -> {
-				if (!bindings.hasMember(key)) {
-					bindings.putMember(key, value);
+			bindParam(wrap, bindings::putMember, (s, o) -> {
+				if (!bindings.hasMember(s)) {
+					bindings.putMember(s, o);
 				}
 			});
 

+ 3 - 45
liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java

@@ -1,26 +1,18 @@
 package com.yomahub.liteflow.script.qlexpress;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 import com.ql.util.express.DefaultContext;
 import com.ql.util.express.ExpressLoader;
 import com.ql.util.express.ExpressRunner;
 import com.ql.util.express.InstructionSet;
-import com.yomahub.liteflow.annotation.util.AnnoUtil;
-import com.yomahub.liteflow.context.ContextBean;
 import com.yomahub.liteflow.enums.ScriptTypeEnum;
-import com.yomahub.liteflow.script.ScriptBeanManager;
 import com.yomahub.liteflow.script.ScriptExecuteWrap;
-import com.yomahub.liteflow.slot.DataBus;
-import com.yomahub.liteflow.slot.Slot;
 import com.yomahub.liteflow.script.ScriptExecutor;
 import com.yomahub.liteflow.script.exception.ScriptLoadException;
 import com.yomahub.liteflow.util.CopyOnWriteHashMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -31,7 +23,7 @@ import java.util.Map;
  * @author Bryan.Zhang
  * @since 2.6.0
  */
-public class QLExpressScriptExecutor implements ScriptExecutor {
+public class QLExpressScriptExecutor extends ScriptExecutor {
 
 	private final Logger log = LoggerFactory.getLogger(this.getClass());
 
@@ -58,7 +50,7 @@ public class QLExpressScriptExecutor implements ScriptExecutor {
 	}
 
 	@Override
-	public Object execute(ScriptExecuteWrap wrap) throws Exception {
+	public Object executeScript(ScriptExecuteWrap wrap) throws Exception {
 		List<String> errorList = new ArrayList<>();
 		try {
 			if (!compiledScriptMap.containsKey(wrap.getNodeId())) {
@@ -69,41 +61,7 @@ public class QLExpressScriptExecutor implements ScriptExecutor {
 			InstructionSet instructionSet = compiledScriptMap.get(wrap.getNodeId());
 			DefaultContext<String, Object> context = new DefaultContext<>();
 
-			// 往脚本语言绑定表里循环增加绑定上下文的key
-			// key的规则为自定义上下文的simpleName
-			// 比如你的自定义上下文为AbcContext,那么key就为:abcContext
-			// 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法,而不是参数,所以脚本语言的绑定表里也是放多个上下文
-			DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
-				ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class);
-				String key;
-				if (contextBean != null && contextBean.value().trim().length() > 0) {
-					key = contextBean.value();
-				}
-				else {
-					key = StrUtil.lowerFirst(o.getClass().getSimpleName());
-				}
-				context.put(key, o);
-			});
-
-			// 把wrap对象转换成元数据map
-			Map<String, Object> metaMap = BeanUtil.beanToMap(wrap);
-
-			// 在元数据里放入主Chain的流程参数
-			Slot slot = DataBus.getSlot(wrap.getSlotIndex());
-			metaMap.put("requestData", slot.getRequestData());
-
-			// 如果有隐式流程,则放入隐式流程的流程参数
-			Object subRequestData = slot.getChainReqData(wrap.getCurrChainName());
-			if (ObjectUtil.isNotNull(subRequestData)) {
-				metaMap.put("subRequestData", subRequestData);
-			}
-
-			// 往脚本上下文里放入元数据
-			context.put("_meta", metaMap);
-
-			// 放入用户自己定义的bean
-			// 放入用户自己定义的bean
-			ScriptBeanManager.getScriptBeanMap().forEach(context::putIfAbsent);
+			bindParam(wrap, context::put, context::putIfAbsent);
 
 			return expressRunner.execute(instructionSet, context, errorList, true, false, null);
 		}

+ 43 - 0
liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/common/ScriptAviatorCommonELTest.java

@@ -0,0 +1,43 @@
+package com.yomahub.liteflow.test.script.aviator.common;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+
+/**
+ * 测试springboot下的python脚本组件,基于xml配置
+ *
+ * @author Bryan.Zhang
+ * @since 2.9.5
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/common/application.properties")
+@SpringBootTest(classes = ScriptAviatorCommonELTest.class)
+@EnableAutoConfiguration
+@ComponentScan({ "com.yomahub.liteflow.test.script.aviator.common.cmp" })
+public class ScriptAviatorCommonELTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 测试普通脚本节点
+	@Test
+	public void testCommon1() {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		DefaultContext context = response.getFirstContextBean();
+		Assert.assertTrue(response.isSuccess());
+		Assert.assertEquals(Long.valueOf(6), context.getData("s1"));
+	}
+
+}