diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java index 10de81bb1..0f4be12dd 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java +++ b/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 { - ScriptExecutor init(); + public ScriptExecutor init(){ + return this; + } - void load(String nodeId, String script); + public abstract void load(String nodeId, String script); - Object execute(ScriptExecuteWrap wrap) throws Exception; + 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; + } + } + } - void cleanCache(); + public abstract Object executeScript(ScriptExecuteWrap wrap) throws Exception; - ScriptTypeEnum scriptType(); + public abstract void cleanCache(); + + public abstract ScriptTypeEnum scriptType(); + + public void bindParam(ScriptExecuteWrap wrap, BiConsumer putConsumer, BiConsumer 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); + }); + + // 把wrap对象转换成元数据map + Map 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); + } + + // 往脚本上下文里放入元数据 + putConsumer.accept("_meta", metaMap); + + // 放入用户自己定义的bean + ScriptBeanManager.getScriptBeanMap().forEach(putIfAbsentConsumer); + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java index 53536b288..a7c53e56e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java +++ b/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 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.put("_meta", metaMap); - - // 放入用户自己定义的bean - ScriptBeanManager.getScriptBeanMap().forEach(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; - } + 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); } + + CompiledScript compiledScript = compiledScriptMap.get(wrap.getNodeId()); + Bindings bindings = new SimpleBindings(); + + bindParam(wrap, bindings::put, bindings::putIfAbsent); + + return compiledScript.eval(bindings); } @Override diff --git a/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java b/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java index f2563e769..e6b194a9d 100644 --- a/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java +++ b/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 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 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); } }); diff --git a/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java b/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java index 67b17edac..610bacb37 100644 --- a/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java +++ b/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 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 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 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); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/common/ScriptAviatorCommonELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/common/ScriptAviatorCommonELTest.java new file mode 100644 index 000000000..76067318b --- /dev/null +++ b/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")); + } + +}