diff --git a/liteflow-script-plugin/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java b/liteflow-script-plugin/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java index c415d13e3..23064ddf7 100644 --- a/liteflow-script-plugin/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java +++ b/liteflow-script-plugin/liteflow-script-groovy/src/main/java/com/yomahub/liteflow/script/groovy/GroovyScriptExecutor.java @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import javax.script.*; import java.util.Map; -import java.util.function.Consumer; /** * Groovy脚本语言的执行器实现 diff --git a/liteflow-script-plugin/liteflow-script-javascript/pom.xml b/liteflow-script-plugin/liteflow-script-javascript/pom.xml new file mode 100644 index 000000000..2796eeefc --- /dev/null +++ b/liteflow-script-plugin/liteflow-script-javascript/pom.xml @@ -0,0 +1,23 @@ + + + + liteflow-script-plugin + com.yomahub + ${revision} + ../pom.xml + + 4.0.0 + + liteflow-script-javascript + + + + com.yomahub + liteflow-core + ${revision} + provided + + + \ No newline at end of file diff --git a/liteflow-script-plugin/liteflow-script-javascript/src/main/java/com/yomahub/liteflow/script/javascript/JavaScriptExecutor.java b/liteflow-script-plugin/liteflow-script-javascript/src/main/java/com/yomahub/liteflow/script/javascript/JavaScriptExecutor.java new file mode 100644 index 000000000..a5888eecd --- /dev/null +++ b/liteflow-script-plugin/liteflow-script-javascript/src/main/java/com/yomahub/liteflow/script/javascript/JavaScriptExecutor.java @@ -0,0 +1,103 @@ +package com.yomahub.liteflow.script.javascript; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +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.ScriptExecuteException; +import com.yomahub.liteflow.script.exception.ScriptLoadException; +import com.yomahub.liteflow.util.CopyOnWriteHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.*; +import java.util.Map; + +/** + * JavaScript脚本语言的执行器实现 + * @author Bryan.Zhang + * @since 2.9.1 + */ +public class JavaScriptExecutor implements ScriptExecutor { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private ScriptEngine scriptEngine; + + private final Map compiledScriptMap = new CopyOnWriteHashMap<>(); + + @Override + public ScriptExecutor init() { + ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); + scriptEngine = scriptEngineManager.getEngineByName("javascript"); + return this; + } + + @Override + public void load(String nodeId, String script) { + try{ + CompiledScript compiledScript = ((Compilable) scriptEngine).compile(script); + compiledScriptMap.put(nodeId, compiledScript); + }catch (Exception e){ + String errorMsg = StrUtil.format("script loading error for node[{}], error msg:{}", nodeId, e.getMessage()); + throw new ScriptLoadException(errorMsg); + } + + } + + @Override + public Object execute(ScriptExecuteWrap wrap) { + 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 -> { + String 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.getCurrChainName()); + if (ObjectUtil.isNotNull(subRequestData)){ + metaMap.put("subRequestData", subRequestData); + } + + //往脚本上下文里放入元数据 + bindings.put("_meta", metaMap); + + //放入用户自己定义的bean + bindings.putAll(ScriptBeanManager.getScriptBeanMap()); + + return compiledScript.eval(bindings); + }catch (Exception e){ + log.error(e.getMessage(), e); + String errorMsg = StrUtil.format("script execute error for node[{}]", wrap.getNodeId()); + throw new ScriptExecuteException(errorMsg); + } + } + + @Override + public void cleanCache() { + compiledScriptMap.clear(); + } +} diff --git a/liteflow-script-plugin/liteflow-script-javascript/src/main/resources/META-INF/services/com.yomahub.liteflow.script.ScriptExecutor b/liteflow-script-plugin/liteflow-script-javascript/src/main/resources/META-INF/services/com.yomahub.liteflow.script.ScriptExecutor new file mode 100644 index 000000000..c331aee2e --- /dev/null +++ b/liteflow-script-plugin/liteflow-script-javascript/src/main/resources/META-INF/services/com.yomahub.liteflow.script.ScriptExecutor @@ -0,0 +1,2 @@ +# JavaScript的实现 +com.yomahub.liteflow.script.javascript.JavaScriptExecutor \ No newline at end of file diff --git a/liteflow-script-plugin/pom.xml b/liteflow-script-plugin/pom.xml index be8c8929b..7ca6966e9 100644 --- a/liteflow-script-plugin/pom.xml +++ b/liteflow-script-plugin/pom.xml @@ -17,6 +17,7 @@ liteflow-script-qlexpress liteflow-script-groovy + liteflow-script-javascript \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/pom.xml b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/pom.xml new file mode 100644 index 000000000..3bbe2ccae --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/pom.xml @@ -0,0 +1,33 @@ + + + + liteflow-testcase-el + com.yomahub + ${revision} + ../pom.xml + + 4.0.0 + + liteflow-testcase-el-script-javascript-springboot + + + + com.yomahub + liteflow-spring-boot-starter + ${revision} + + + com.yomahub + liteflow-script-javascript + ${revision} + test + + + org.springframework.boot + spring-boot-starter-test + + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java new file mode 100644 index 000000000..64886670f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test; + +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner; +import com.yomahub.liteflow.spring.ComponentScanner; +import com.yomahub.liteflow.thread.ExecutorHelper; +import org.junit.AfterClass; + +public class BaseTest { + + @AfterClass + public static void cleanScanCache(){ + ComponentScanner.cleanCache(); + FlowBus.cleanCache(); + ExecutorHelper.loadInstance().clearExecutorServiceMap(); + SpiFactoryCleaner.clean(); + LiteflowConfigGetter.clean(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/LiteflowXmlScriptJsELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/LiteflowXmlScriptJsELTest.java new file mode 100644 index 000000000..792e4d93e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/LiteflowXmlScriptJsELTest.java @@ -0,0 +1,45 @@ +package com.yomahub.liteflow.test.script.javascript.common; + +import cn.hutool.core.io.resource.ResourceUtil; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.enums.FlowParserTypeEnum; +import com.yomahub.liteflow.flow.FlowBus; +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下的groovy脚本组件,基于xml配置 + * @author Bryan.Zhang + * @since 2.6.0 + */ +@RunWith(SpringRunner.class) +@TestPropertySource(value = "classpath:/common/application.properties") +@SpringBootTest(classes = LiteflowXmlScriptJsELTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.script.javascript.common.cmp"}) +public class LiteflowXmlScriptJsELTest 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(Double.valueOf(11), context.getData("s1")); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/ACmp.java new file mode 100644 index 000000000..c746d2bca --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/ACmp.java @@ -0,0 +1,20 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.script.javascript.common.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/BCmp.java new file mode 100644 index 000000000..67196ecce --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/BCmp.java @@ -0,0 +1,21 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.script.javascript.common.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/CCmp.java new file mode 100644 index 000000000..ca9a0da81 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/CCmp.java @@ -0,0 +1,21 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.script.javascript.common.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("c") +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/DCmp.java new file mode 100644 index 000000000..05541c698 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/common/cmp/DCmp.java @@ -0,0 +1,24 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.script.javascript.common.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; + +@LiteflowComponent("d") +public class DCmp extends NodeComponent { + + @Override + public void process() { + DefaultContext context = this.getFirstContextBean(); + context.setData("count",198); + System.out.println("DCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/application.properties b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/application.properties new file mode 100644 index 000000000..4c9c216b6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=common/flow.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/flow.xml new file mode 100644 index 000000000..8583b8558 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/resources/common/flow.xml @@ -0,0 +1,29 @@ + + + + + + + + + + THEN(a, b, c, s1); + + \ No newline at end of file diff --git a/liteflow-testcase-el/pom.xml b/liteflow-testcase-el/pom.xml index f4e4388ca..690b7fbb8 100644 --- a/liteflow-testcase-el/pom.xml +++ b/liteflow-testcase-el/pom.xml @@ -22,6 +22,7 @@ liteflow-testcase-el-declare-multi-springboot liteflow-testcase-el-script-groovy-springboot liteflow-testcase-el-script-qlexpress-springboot + liteflow-testcase-el-script-javascript-springboot liteflow-testcase-el-zk-springboot liteflow-testcase-el-sql-springboot liteflow-testcase-el-nacos-springboot diff --git a/pom.xml b/pom.xml index 249f23d9f..4cef7d4e4 100644 --- a/pom.xml +++ b/pom.xml @@ -340,7 +340,7 @@ liteflow-spring-boot-starter liteflow-spring liteflow-testcase-el - +