diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/pom.xml b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/pom.xml
new file mode 100644
index 000000000..11a493817
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+ liteflow-testcase-el
+ com.yomahub
+ 2.8.0
+
+ 4.0.0
+
+ liteflow-testcase-el-script-qlexpress-springboot
+
+
+
+ com.yomahub
+ liteflow-spring-boot-starter
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.aspectj
+ aspectjweaver
+
+
+ org.apache.curator
+ curator-test
+ test
+
+
+ com.101tec
+ zkclient
+
+
+ com.yomahub
+ liteflow-script-qlexpress
+ ${project.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${springboot.version}
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java
new file mode 100644
index 000000000..25df2d03c
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/BaseTest.java
@@ -0,0 +1,22 @@
+package com.yomahub.liteflow.test;
+
+import com.yomahub.liteflow.flow.FlowBus;
+import com.yomahub.liteflow.flow.id.IdGeneratorHelper;
+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();
+ IdGeneratorHelper.getInstance().clear();
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteFlowXmlScriptBuilderQLExpressELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteFlowXmlScriptBuilderQLExpressELTest.java
new file mode 100644
index 000000000..e93e3b012
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteFlowXmlScriptBuilderQLExpressELTest.java
@@ -0,0 +1,98 @@
+package com.yomahub.liteflow.test.script.qlexpress;
+
+import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
+import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
+import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
+import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+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.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = LiteFlowXmlScriptBuilderQLExpressELTest.class)
+@EnableAutoConfiguration
+public class LiteFlowXmlScriptBuilderQLExpressELTest extends BaseTest {
+
+ @Resource
+ private FlowExecutor flowExecutor;
+
+ //测试通过builder方式运行普通script节点,以脚本文本的方式运行
+ @Test
+ public void testBuilderScript1() {
+ LiteFlowNodeBuilder.createNode().setId("a")
+ .setName("组件A")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.ACmp")
+ .build();
+ LiteFlowNodeBuilder.createNode().setId("b")
+ .setName("组件B")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.BCmp")
+ .build();
+ LiteFlowNodeBuilder.createNode().setId("c")
+ .setName("组件C")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.CCmp")
+ .build();
+ LiteFlowNodeBuilder.createScriptNode()
+ .setId("s1")
+ .setName("普通脚本S1")
+ .setType(NodeTypeEnum.SCRIPT)
+ .setScript("a=3;b=2;defaultContext.setData(\"s1\",a*b);")
+ .build();
+
+ LiteFlowChainELBuilder.createChain().setChainName("chain1")
+ .setEL("THEN(a,b,c,s1)")
+ .build();
+
+
+ LiteflowResponse response = flowExecutor.execute2Resp("chain1","arg1");
+ DefaultContext context = response.getFirstContextBean();
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals(Integer.valueOf(6), context.getData("s1"));
+ }
+
+ //测试通过builder方式运行普通script节点,以file的方式运行
+ @Test
+ public void testBuilderScript2() {
+ LiteFlowNodeBuilder.createNode().setId("d")
+ .setName("组件D")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.DCmp")
+ .build();
+ LiteFlowNodeBuilder.createNode().setId("s2")
+ .setName("条件脚本S2")
+ .setType(NodeTypeEnum.SWITCH_SCRIPT)
+ .setFile("builder/s2.ql")
+ .build();
+ LiteFlowNodeBuilder.createNode().setId("a")
+ .setName("组件A")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.ACmp")
+ .build();
+ LiteFlowNodeBuilder.createNode().setId("b")
+ .setName("组件B")
+ .setType(NodeTypeEnum.COMMON)
+ .setClazz("com.yomahub.liteflow.test.script.qlexpress.cmp.BCmp")
+ .build();
+
+ LiteFlowChainELBuilder.createChain().setChainName("chain2")
+ .setEL("THEN(d,SWITCH(s2).to(a,b))")
+ .build();
+
+
+ LiteflowResponse response = flowExecutor.execute2Resp("chain2","arg1");
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals("d[组件D]==>s2[条件脚本S2]==>b[组件B]", response.getExecuteStepStr());
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptFileQLExpressELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptFileQLExpressELTest.java
new file mode 100644
index 000000000..098a15507
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptFileQLExpressELTest.java
@@ -0,0 +1,92 @@
+package com.yomahub.liteflow.test.script.qlexpress;
+
+import cn.hutool.core.io.resource.ResourceUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.enums.FlowParserTypeEnum;
+import com.yomahub.liteflow.flow.FlowBus;
+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下的脚本组件
+ * @author Bryan.Zhang
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/json-script-file/application.properties")
+@SpringBootTest(classes = LiteflowJsonScriptFileQLExpressELTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.script.qlexpress.cmp"})
+public class LiteflowJsonScriptFileQLExpressELTest extends BaseTest {
+
+ @Resource
+ private FlowExecutor flowExecutor;
+
+ //测试普通脚本节点
+ @Test
+ public void testScript1() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+ DefaultContext context = response.getFirstContextBean();
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals(Integer.valueOf(6), context.getData("s1"));
+ }
+
+ //测试条件脚本节点
+ @Test
+ public void testScript2() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", response.getExecuteStepStr());
+ }
+
+ @Test
+ public void testScript3() throws Exception{
+ //根据配置,加载的应该是flow.xml,执行原来的规则
+ LiteflowResponse responseOld = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseOld.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", responseOld.getExecuteStepStr());
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /json-script-file/flow_update.el.json");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_JSON, newContent);
+
+ //重新执行chain2这个链路,结果会变
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本_改]==>a==>s3[普通脚本_新增]", responseNew.getExecuteStepStr());
+ }
+
+ //测试脚本&规则平滑重载刷新
+ @Test
+ public void testScript4() throws Exception{
+ new Thread(() -> {
+ try{
+ Thread.sleep(2000L);
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /json-script-file/flow_update.el.json");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_JSON, newContent);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }).start();
+
+ for (int i = 0; i < 300; i++) {
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Thread.sleep(10L);
+ }
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptQLExpressELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptQLExpressELTest.java
new file mode 100644
index 000000000..2ca63eb07
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowJsonScriptQLExpressELTest.java
@@ -0,0 +1,70 @@
+package com.yomahub.liteflow.test.script.qlexpress;
+
+import cn.hutool.core.io.resource.ResourceUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.enums.FlowParserTypeEnum;
+import com.yomahub.liteflow.flow.FlowBus;
+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下的脚本组件
+ * @author Bryan.Zhang
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/json-script/application.properties")
+@SpringBootTest(classes = LiteflowJsonScriptQLExpressELTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.script.qlexpress.cmp"})
+public class LiteflowJsonScriptQLExpressELTest extends BaseTest {
+
+ @Resource
+ private FlowExecutor flowExecutor;
+
+ //测试普通脚本节点
+ @Test
+ public void testScript1() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+ DefaultContext context = response.getFirstContextBean();
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals(Integer.valueOf(6), context.getData("s1"));
+ }
+
+ //测试条件脚本节点
+ @Test
+ public void testScript2() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", response.getExecuteStepStr());
+ }
+
+ @Test
+ public void testScript3() throws Exception{
+ //根据配置,加载的应该是flow.xml,执行原来的规则
+ LiteflowResponse responseOld = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseOld.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", responseOld.getExecuteStepStr());
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /json-script/flow_update.el.json");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_JSON, newContent);
+
+ //重新执行chain2这个链路,结果会变
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本_改]==>a==>s3[普通脚本_新增]", responseNew.getExecuteStepStr());
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptFileQLExpressELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptFileQLExpressELTest.java
new file mode 100644
index 000000000..d19a40e0c
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptFileQLExpressELTest.java
@@ -0,0 +1,92 @@
+package com.yomahub.liteflow.test.script.qlexpress;
+
+import cn.hutool.core.io.resource.ResourceUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.enums.FlowParserTypeEnum;
+import com.yomahub.liteflow.flow.FlowBus;
+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下的脚本组件
+ * @author Bryan.Zhang
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/xml-script-file/application.properties")
+@SpringBootTest(classes = LiteflowXmlScriptFileQLExpressELTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.script.qlexpress.cmp"})
+public class LiteflowXmlScriptFileQLExpressELTest extends BaseTest {
+
+ @Resource
+ private FlowExecutor flowExecutor;
+
+ //测试普通脚本节点
+ @Test
+ public void testScript1() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+ DefaultContext context = response.getFirstContextBean();
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals(Integer.valueOf(6), context.getData("s1"));
+ }
+
+ //测试条件脚本节点
+ @Test
+ public void testScript2() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", response.getExecuteStepStr());
+ }
+
+ @Test
+ public void testScript3() throws Exception{
+ //根据配置,加载的应该是flow.xml,执行原来的规则
+ LiteflowResponse responseOld = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseOld.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", responseOld.getExecuteStepStr());
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /xml-script-file/flow_update.el.xml");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, newContent);
+
+ //重新执行chain2这个链路,结果会变
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本_改]==>a==>s3[普通脚本_新增]", responseNew.getExecuteStepStr());
+ }
+
+ //测试脚本&规则平滑重载刷新
+ @Test
+ public void testScript4() throws Exception{
+ new Thread(() -> {
+ try{
+ Thread.sleep(2000L);
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /xml-script-file/flow_update.el.xml");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, newContent);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }).start();
+
+ for (int i = 0; i < 300; i++) {
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Thread.sleep(10L);
+ }
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELTest.java
new file mode 100644
index 000000000..223dd9bbc
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELTest.java
@@ -0,0 +1,70 @@
+package com.yomahub.liteflow.test.script.qlexpress;
+
+import cn.hutool.core.io.resource.ResourceUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.enums.FlowParserTypeEnum;
+import com.yomahub.liteflow.flow.FlowBus;
+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下的脚本组件
+ * @author Bryan.Zhang
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/xml-script/application.properties")
+@SpringBootTest(classes = LiteflowXmlScriptQLExpressELTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.script.qlexpress.cmp"})
+public class LiteflowXmlScriptQLExpressELTest extends BaseTest {
+
+ @Resource
+ private FlowExecutor flowExecutor;
+
+ //测试普通脚本节点
+ @Test
+ public void testScript1() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+ DefaultContext context = response.getFirstContextBean();
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals(Integer.valueOf(6), context.getData("s1"));
+ }
+
+ //测试条件脚本节点
+ @Test
+ public void testScript2() {
+ LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(response.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", response.getExecuteStepStr());
+ }
+
+ @Test
+ public void testScript3() throws Exception{
+ //根据配置,加载的应该是flow.xml,执行原来的规则
+ LiteflowResponse responseOld = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseOld.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本]==>b", responseOld.getExecuteStepStr());
+ //更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取
+ String newContent = ResourceUtil.readUtf8Str("classpath: /xml-script/flow_update.el.xml");
+ //进行刷新
+ FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, newContent);
+
+ //重新执行chain2这个链路,结果会变
+ LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg");
+ Assert.assertTrue(responseNew.isSuccess());
+ Assert.assertEquals("d==>s2[条件脚本_改]==>a==>s3[普通脚本_新增]", responseNew.getExecuteStepStr());
+ }
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/ACmp.java
new file mode 100644
index 000000000..0db8d6bf5
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/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.qlexpress.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-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/BCmp.java
new file mode 100644
index 000000000..f303cc6f1
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/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.qlexpress.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-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/CCmp.java
new file mode 100644
index 000000000..e195df5ca
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/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.qlexpress.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-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/DCmp.java
new file mode 100644
index 000000000..ecc648de5
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/cmp/DCmp.java
@@ -0,0 +1,25 @@
+/**
+ * Title: liteflow
+ * Description: 轻量级的组件式流程框架
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.script.qlexpress.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.slot.Slot;
+
+@LiteflowComponent("d")
+public class DCmp extends NodeComponent {
+
+ @Override
+ public void process() {
+ DefaultContext context = this.getFirstContextBean();
+ context.setData("count",97);
+ System.out.println("DCmp executed!");
+ }
+
+}
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s1.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s1.ql
new file mode 100644
index 000000000..560850daf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s1.ql
@@ -0,0 +1,3 @@
+a=3;
+b=2;
+defaultContext.setData("s1",a*b);
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s2.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s2.ql
new file mode 100644
index 000000000..e27901531
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/builder/s2.ql
@@ -0,0 +1,6 @@
+count = defaultContext.getData("count");
+if(count > 100){
+ return "a";
+}else{
+ return "b";
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/application.properties b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/application.properties
new file mode 100644
index 000000000..cc98759cf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/application.properties
@@ -0,0 +1 @@
+liteflow.rule-source=json-script-file/flow.el.json
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow.el.json b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow.el.json
new file mode 100644
index 000000000..cd325117f
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow.el.json
@@ -0,0 +1,30 @@
+{
+ "flow": {
+ "nodes": {
+ "node": [
+ {
+ "id": "s1",
+ "name": "普通脚本",
+ "type": "script",
+ "file": "json-script-file/s1.ql"
+ },
+ {
+ "id": "s2",
+ "name": "条件脚本",
+ "type": "switch_script",
+ "file": "json-script-file/s2.ql"
+ }
+ ]
+ },
+ "chain": [
+ {
+ "name": "chain1",
+ "value": "THEN(a,b,c,s1)"
+ },
+ {
+ "name": "chain2",
+ "value": "THEN(d,SWITCH(s2).to(a,b))"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow_update.el.json b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow_update.el.json
new file mode 100644
index 000000000..58ba6fd8e
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/flow_update.el.json
@@ -0,0 +1,36 @@
+{
+ "flow": {
+ "nodes": {
+ "node": [
+ {
+ "id": "s1",
+ "name": "普通脚本",
+ "type": "script",
+ "file": "json-script-file/s1.ql"
+ },
+ {
+ "id": "s2",
+ "name": "条件脚本_改",
+ "type": "switch_script",
+ "file": "json-script-file/s2_update.ql"
+ },
+ {
+ "id": "s3",
+ "name": "普通脚本_新增",
+ "type": "script",
+ "file": "json-script-file/s3.ql"
+ }
+ ]
+ },
+ "chain": [
+ {
+ "name": "chain1",
+ "value": "THEN(a,b,c,s1)"
+ },
+ {
+ "name": "chain2",
+ "value": "THEN(d,SWITCH(s2).to(a,b), s3)"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s1.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s1.ql
new file mode 100644
index 000000000..560850daf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s1.ql
@@ -0,0 +1,3 @@
+a=3;
+b=2;
+defaultContext.setData("s1",a*b);
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2.ql
new file mode 100644
index 000000000..e27901531
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2.ql
@@ -0,0 +1,6 @@
+count = defaultContext.getData("count");
+if(count > 100){
+ return "a";
+}else{
+ return "b";
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2_update.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2_update.ql
new file mode 100644
index 000000000..ebfa25e4c
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s2_update.ql
@@ -0,0 +1,6 @@
+count = defaultContext.getData("count");
+if(count > 150){
+ return "b";
+}else{
+ return "a";
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s3.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s3.ql
new file mode 100644
index 000000000..f7285edbf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script-file/s3.ql
@@ -0,0 +1,4 @@
+a=3;
+b=2;
+c=10;
+defaultContext.setData("s1",a*b+c);
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/application.properties b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/application.properties
new file mode 100644
index 000000000..f0401bf0f
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/application.properties
@@ -0,0 +1 @@
+liteflow.rule-source=json-script/flow.el.json
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow.el.json b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow.el.json
new file mode 100644
index 000000000..73a2935c3
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow.el.json
@@ -0,0 +1,36 @@
+{
+ "flow": {
+ "nodes": {
+ "node": [
+ {
+ "id": "s1",
+ "name": "普通脚本",
+ "type": "script",
+ "value": "a=3;b=2;defaultContext.setData(\"s1\",a*b);"
+ },
+ {
+ "id": "s2",
+ "name": "条件脚本",
+ "type": "switch_script",
+ "value": "count = defaultContext.getData(\"count\");if(count > 100){return \"a\";}else{return \"b\";}"
+ },
+ {
+ "id": "s3",
+ "name": "普通脚本2",
+ "type": "script",
+ "value": "a=30;b=2;defaultContext.setData(\"s1\",a*b);"
+ }
+ ]
+ },
+ "chain": [
+ {
+ "name": "chain1",
+ "value": "THEN(a,b,c,s1)"
+ },
+ {
+ "name": "chain2",
+ "value": "THEN(d,SWITCH(s2).to(a,b))"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow_update.el.json b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow_update.el.json
new file mode 100644
index 000000000..52497f7ad
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/json-script/flow_update.el.json
@@ -0,0 +1,36 @@
+{
+ "flow": {
+ "nodes": {
+ "node": [
+ {
+ "id": "s1",
+ "name": "普通脚本",
+ "type": "script",
+ "value": "a=3;b=2;defaultContext.setData(\"s1\",a*b);"
+ },
+ {
+ "id": "s2",
+ "name": "条件脚本_改",
+ "type": "switch_script",
+ "value": "count = defaultContext.getData(\"count\");if(count > 150){return \"b\";}else{return \"a\";}"
+ },
+ {
+ "id": "s3",
+ "name": "普通脚本_新增",
+ "type": "script",
+ "value": "a=3;b=2;c=10;defaultContext.setData(\"s1\",a*b+c);"
+ }
+ ]
+ },
+ "chain": [
+ {
+ "name": "chain1",
+ "value": "THEN(a,b,c,s1)"
+ },
+ {
+ "name": "chain2",
+ "value": "THEN(d,SWITCH(s2).to(a,b), s3)"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/application.properties b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/application.properties
new file mode 100644
index 000000000..ea52aae86
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/application.properties
@@ -0,0 +1 @@
+liteflow.rule-source=xml-script-file/flow.el.xml
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow.el.xml
new file mode 100644
index 000000000..b2d24762b
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow.el.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+ THEN(a,b,c,s1)
+
+
+
+ THEN(d,SWITCH(s2).to(a,b))
+
+
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow_update.el.xml b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow_update.el.xml
new file mode 100644
index 000000000..479d2b28f
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/flow_update.el.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+ THEN(a,b,c,s1)
+
+
+
+ THEN(d,SWITCH(s2).to(a,b), s3)
+
+
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s1.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s1.ql
new file mode 100644
index 000000000..560850daf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s1.ql
@@ -0,0 +1,3 @@
+a=3;
+b=2;
+defaultContext.setData("s1",a*b);
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2.ql
new file mode 100644
index 000000000..e27901531
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2.ql
@@ -0,0 +1,6 @@
+count = defaultContext.getData("count");
+if(count > 100){
+ return "a";
+}else{
+ return "b";
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2_update.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2_update.ql
new file mode 100644
index 000000000..ebfa25e4c
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s2_update.ql
@@ -0,0 +1,6 @@
+count = defaultContext.getData("count");
+if(count > 150){
+ return "b";
+}else{
+ return "a";
+}
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s3.ql b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s3.ql
new file mode 100644
index 000000000..f7285edbf
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script-file/s3.ql
@@ -0,0 +1,4 @@
+a=3;
+b=2;
+c=10;
+defaultContext.setData("s1",a*b+c);
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application.properties b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application.properties
new file mode 100644
index 000000000..3f599a573
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application.properties
@@ -0,0 +1 @@
+liteflow.rule-source=xml-script/flow.el.xml
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow.el.xml
new file mode 100644
index 000000000..16b31eb3d
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow.el.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+ 100){
+ return "a";
+ }else{
+ return "b";
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+ THEN(a,b,c,s1)
+
+
+
+ THEN(d,SWITCH(s2).to(a,b))
+
+
\ No newline at end of file
diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow_update.el.xml b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow_update.el.xml
new file mode 100644
index 000000000..d47255350
--- /dev/null
+++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/flow_update.el.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+ 150){
+ return "b";
+ }else{
+ return "a";
+ }
+ ]]>
+
+
+
+
+
+
+
+
+ THEN(a,b,c,s1)
+
+
+
+ THEN(d,SWITCH(s2).to(a,b), s3)
+
+
\ No newline at end of file
diff --git a/liteflow-testcase-el/pom.xml b/liteflow-testcase-el/pom.xml
index 6dd2a9f66..a5b96290f 100644
--- a/liteflow-testcase-el/pom.xml
+++ b/liteflow-testcase-el/pom.xml
@@ -17,5 +17,6 @@
liteflow-testcase-el-springboot
liteflow-testcase-el-declare-springboot
liteflow-testcase-el-script-groovy-springboot
+ liteflow-testcase-el-script-qlexpress-springboot
\ No newline at end of file