Merge branch 'qlexpress4' into dev

# Conflicts:
#	.gitignore
This commit is contained in:
everywhere.z
2025-12-05 11:06:56 +08:00
71 changed files with 909 additions and 222 deletions

1
.gitignore vendored
View File

@@ -81,6 +81,5 @@ atlassian-ide-plugin.xml
*/logs
.flattened-pom.xml
.claude
.vscode

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-javax-pro</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-javax</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -1,10 +1,13 @@
package com.yomahub.liteflow.benchmark;
import cn.hutool.core.io.resource.ResourceUtil;
import com.yomahub.liteflow.benchmark.cmp.TestContext;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
@@ -42,7 +45,12 @@ public class CommonBenchmark {
@Benchmark
public void test1(){
flowExecutor.execute2Resp("chain1");
TestContext context = new TestContext();
context.setName("tom");
context.setAge(21);
DefaultContext defaultContext = new DefaultContext();
flowExecutor.execute2Resp("chain1", null, context,defaultContext);
}
public static void main(String[] args) throws RunnerException {

View File

@@ -0,0 +1,41 @@
package com.yomahub.liteflow.benchmark;
import cn.hutool.core.io.resource.ResourceUtil;
import com.yomahub.liteflow.benchmark.cmp.TestContext;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.util.JsonUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:application.properties")
@SpringBootTest(classes = CommonTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.benchmark.cmp" })
public class CommonTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void test1() throws Exception {
TestContext context = new TestContext();
context.setName("tom");
context.setAge(21);
DefaultContext defaultContext = new DefaultContext();
LiteflowResponse response = flowExecutor.execute2Resp("chain1", null, context,defaultContext);
if (!response.isSuccess()){
throw response.getCause();
}
}
}

View File

@@ -22,7 +22,7 @@ public class ACmp extends NodeComponent {
public void process() {
int v1 = 2;
int v2 = 3;
DefaultContext ctx = this.getFirstContextBean();
DefaultContext ctx = this.getContextBean(DefaultContext.class);
ctx.setData("s1", v1 * v2);
TestDomain domain = ContextAwareHolder.loadContextAware().getBean(TestDomain.class);

View File

@@ -0,0 +1,21 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b")
public void processA(NodeComponent bindCmp, @LiteflowFact("name") String name) {
// System.out.println(name);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "c")
public void processB(NodeComponent bindCmp, @LiteflowFact("age") int age) {
// System.out.println(age);
}
}

View File

@@ -0,0 +1,24 @@
package com.yomahub.liteflow.benchmark.cmp;
public class TestContext {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

View File

@@ -2,6 +2,6 @@
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
<flow>
<chain name="chain1">
THEN(a);
THEN(a,b,c);
</chain>
</flow>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>liteflow-benchmark</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>liteflow-benchmark-compile</artifactId>
</project>

View File

@@ -0,0 +1,79 @@
package com.yomahub.liteflow.benchmark;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:application.properties")
@SpringBootTest(classes = TestCompile.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.benchmark.cmp" })
public class TestCompile {
@Resource
private FlowExecutor flowExecutor;
@Test
public void test1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("channelSenderChain", null, BatchMessageResultContext.class);
BatchMessageResultContext context = response.getFirstContextBean();
if (response.isSuccess()){
System.out.println("执行成功,最终选择的渠道是:" + context.getFinalResultChannel());
}else{
System.out.println("执行失败:" + response.getCause());
}
}
//每个el都一样走QL的缓存
@Test
public void test2() throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
String el = "selectBestChannel = THEN(WHEN(channel1Query, channel2Query, channel3Query,channel4Query, channel5Query, channel6Query),channelSelector).id(\"branch1\");\n" +
" selectBizChannel = THEN(biz1,SWITCH(if_2).to(channel3,channel4,SWITCH(if_3).to(channel5, channel6).id(\"s3\")).id(\"s2\")).id(\"branch2\");\n" +
" THEN(packageData,SWITCH(if_1).to(channel1,channel2,selectBestChannel,selectBizChannel),batchSender);";
for (int i = 1; i <= 20000; i++) {
LiteFlowChainELBuilder.createChain().setChainId("chain_build_"+i).setEL(el).build();
}
stopWatch.stop();
System.out.println(StrUtil.format("耗时:{}",stopWatch.getTotalTimeMillis()));
}
//每个el都不一样不走QL的缓存
@Test
public void test3() throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
String el = "selectBestChannel = THEN(WHEN(channel1Query, channel2Query, channel3Query,channel4Query, channel5Query, channel6Query),channelSelector).id(\"branch1\");\n" +
" selectBizChannel = THEN(biz1,SWITCH(if_2).to(channel3,channel4,SWITCH(if_3).to(channel5, channel6).id(\"s3\")).id(\"s2\")).id(\"branch2\");\n" +
" THEN(packageData.tag(\"{}\"),SWITCH(if_1).to(channel1,channel2,selectBestChannel,selectBizChannel),batchSender);";
for (int i = 1; i <= 20000; i++) {
LiteFlowChainELBuilder.createChain().setChainId("chain_build_"+i).setEL(StrUtil.format(el,i)).build();
}
stopWatch.stop();
System.out.println(StrUtil.format("耗时:{}",stopWatch.getTotalTimeMillis()));
}
}

View File

@@ -0,0 +1,21 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.core.NodeComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
@LiteflowComponent(id = "batchSender", name = "批量发送器")
public class BatchSenderCmp extends NodeComponent {
private static final Logger log = LoggerFactory.getLogger(BatchSenderCmp.class);
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
log.info("最终获取到的渠道为:[{}]", context.getFinalResultChannel());
}
}

View File

@@ -0,0 +1,13 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "biz1", name = "获取业务动态值")
public class Biz1Cmp extends NodeComponent {
@Override
public void process() throws Exception {
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel1", name = "返回渠道1")
public class Channel1Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel1");
}
}

View File

@@ -0,0 +1,18 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "channel1Query", name = "获取渠道1剩余量")
public class Channel1QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道1有2w条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel1", 20000));
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel2", name = "返回渠道2")
public class Channel2Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel2");
}
}

View File

@@ -0,0 +1,19 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "channel2Query", name = "获取渠道2剩余量")
public class Channel2QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道2有1w条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel2", 10000));
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel3", name = "返回渠道3")
public class Channel3Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel3");
}
}

View File

@@ -0,0 +1,19 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "channel3Query", name = "获取渠道3剩余量")
public class Channel3QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道3有10w条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel3", 100000));
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel4", name = "返回渠道4")
public class Channel4Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel4");
}
}

View File

@@ -0,0 +1,18 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "channel4Query", name = "获取渠道4剩余量")
public class Channel4QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道4有7w条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel4", 70000));
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel5", name = "返回渠道5")
public class Channel5Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel5");
}
}

View File

@@ -0,0 +1,18 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "channel5Query", name = "获取渠道5剩余量")
public class Channel5QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道5有5000条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel5", 5000));
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel6", name = "返回渠道6")
public class Channel6Cmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
context.setFinalResultChannel("channel6");
}
}

View File

@@ -0,0 +1,17 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent(id = "channel6Query", name = "获取渠道6剩余量")
public class Channel6QueryCmp extends NodeComponent {
@Override
public void process() throws Exception {
//mock下渠道6有3w条剩余量
BatchMessageResultContext context = this.getFirstContextBean();
context.addQueryVO(new QueryVO("channel6", 30000));
}
}

View File

@@ -0,0 +1,24 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.benchmark.context.BatchMessageResultContext;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.List;
@LiteflowComponent(id = "channelSelector", name = "渠道余量最大选择器")
public class ChannelSelectorCmp extends NodeComponent {
@Override
public void process() throws Exception {
BatchMessageResultContext context = this.getFirstContextBean();
List<QueryVO> queryList = context.getQueryResultList();
//选择渠道余量最大的
QueryVO vo = queryList.stream().min((o1, o2) -> o2.getAvailCount() - o1.getAvailCount()).orElse(null);
assert vo != null;
context.setFinalResultChannel(vo.getChannel());
}
}

View File

@@ -0,0 +1,15 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import java.util.Random;
@LiteflowComponent(id = "if_1", name = "业务判断1")
public class IF1SwitchCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
//这里写死跳到并行获取剩余量那条分支,你可以改成其他分支测试
return "branch1";
}
}

View File

@@ -0,0 +1,15 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import java.util.Random;
@LiteflowComponent(id = "if_2", name = "业务判断2")
public class IF2SwitchCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
//这里写死,你可以改成其他分支
return "s3";
}
}

View File

@@ -0,0 +1,16 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import java.util.Random;
@LiteflowComponent(id = "if_3", name = "业务判断3")
public class IF3SwitchCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
//这里写死,你可以改成其他
return "channel5";
}
}

View File

@@ -0,0 +1,13 @@
package com.yomahub.liteflow.benchmark.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import java.util.Random;
@LiteflowComponent(id = "packageData", name = "组装数据")
public class PackageDataCmp extends NodeComponent {
@Override
public void process() throws Exception {
}
}

View File

@@ -0,0 +1,37 @@
package com.yomahub.liteflow.benchmark.context;
import com.yomahub.liteflow.benchmark.vo.QueryVO;
import java.util.ArrayList;
import java.util.List;
public class BatchMessageResultContext {
private List<QueryVO> queryResultList;
private String finalResultChannel;
public List<QueryVO> getQueryResultList() {
return queryResultList;
}
public void setQueryResultList(List<QueryVO> queryResultList) {
this.queryResultList = queryResultList;
}
public String getFinalResultChannel() {
return finalResultChannel;
}
public void setFinalResultChannel(String finalResultChannel) {
this.finalResultChannel = finalResultChannel;
}
public void addQueryVO(QueryVO queryVO){
if (queryResultList == null){
queryResultList = new ArrayList<>();
}
queryResultList.add(queryVO);
}
}

View File

@@ -0,0 +1,31 @@
package com.yomahub.liteflow.benchmark.vo;
public class QueryVO {
//渠道名称
private String channel;
//剩余短信包数量
private int availCount;
public QueryVO(String channel, int availCount) {
this.channel = channel;
this.availCount = availCount;
}
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
public int getAvailCount() {
return availCount;
}
public void setAvailCount(int availCount) {
this.availCount = availCount;
}
}

View File

@@ -0,0 +1,2 @@
liteflow.rule-source=flow.xml
liteflow.fast-load=true

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="channelSenderChain">
selectBestChannel = THEN(WHEN(channel1Query, channel2Query, channel3Query,channel4Query, channel5Query, channel6Query),channelSelector).id("branch1");
selectBizChannel = THEN(biz1,SWITCH(if_2).to(channel3,channel4,SWITCH(if_3).to(channel5, channel6).id("s3")).id("s2")).id("branch2");
THEN(packageData,SWITCH(if_1).to(channel1,channel2,selectBestChannel,selectBizChannel),batchSender);
</chain>
</flow>

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-groovy</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-java</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-javax-pro</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -16,7 +16,7 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-javax</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -16,13 +16,14 @@
<properties>
<jmh.version>1.37</jmh.version>
<liteflow-version>${revision}</liteflow-version>
</properties>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${revision}</version>
<version>${liteflow-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -48,5 +49,6 @@
<module>liteflow-benchmark-script-groovy</module>
<module>liteflow-benchmark-common</module>
<module>liteflow-benchmark-common-example</module>
<module>liteflow-benchmark-compile</module>
</modules>
</project>

View File

@@ -54,7 +54,7 @@
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<artifactId>qlexpress4</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>

View File

@@ -1,18 +1,20 @@
package com.yomahub.liteflow.builder.el;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.*;
import com.alibaba.qlexpress4.Express4Runner;
import com.alibaba.qlexpress4.QLOptions;
import com.alibaba.qlexpress4.QLResult;
import com.alibaba.qlexpress4.exception.QLException;
import java.util.HashMap;
import java.util.Map;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.common.entity.ValidationResp;
import com.yomahub.liteflow.enums.ParseModeEnum;
@@ -34,9 +36,9 @@ import com.yomahub.liteflow.util.ElRegexUtil;
import com.yomahub.liteflow.util.QlExpressUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
@@ -70,7 +72,7 @@ public class LiteFlowChainELBuilder {
/**
* EL解析引擎
*/
private final static ExpressRunner EXPRESS_RUNNER = QlExpressUtils.getInstance();
private final static Express4Runner EXPRESS_RUNNER = QlExpressUtils.getELExpressRunner();
public static LiteFlowChainELBuilder createChain() {
return new LiteFlowChainELBuilder();
@@ -131,15 +133,17 @@ public class LiteFlowChainELBuilder {
String msg = buildDataNotFoundExceptionMsg(routeEl);
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
String causeMsg = e.getCause().getMessage();
throw new ELParseException(StrUtil.isNotBlank(causeMsg) ? causeMsg : e.getMessage());
}else{
throw new ELParseException(e.getMessage());
throw new ELParseException(StrUtil.isNotBlank(e.getMessage()) ? e.getMessage() : "Unknown EL parse error");
}
}catch (RouteELInvalidException e){
throw e;
}catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
String exMsg = e.getMessage();
throw new ELParseException(errMsg + (StrUtil.isNotBlank(exMsg) ? exMsg : e.getClass().getSimpleName()));
}
}
@@ -166,7 +170,7 @@ public class LiteFlowChainELBuilder {
Condition condition = compile(elStr, errorList, true);
if (Objects.isNull(condition)){
throw new QLException(StrUtil.format("parse el fail,el:[{}]", elStr));
throw new ELParseException(StrUtil.format("parse el fail,el:[{}]", elStr));
}
if (liteflowConfig.getEnableNodeInstanceId()) {
@@ -178,18 +182,17 @@ public class LiteFlowChainELBuilder {
return this;
} catch (QLException e) {
// EL 底层会包装异常,这里是曲线处理
if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
if (ObjectUtil.isNotNull(e.getCause())) {
// 构建错误信息
String msg = buildDataNotFoundExceptionMsg(elStr);
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
}else{
throw new ELParseException(e.getMessage());
throw new ELParseException(StrUtil.isNotBlank(e.getMessage()) ? e.getMessage() : "Unknown EL parse error");
}
} catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
String exMsg = e.getMessage();
throw new ELParseException(errMsg + (StrUtil.isNotBlank(exMsg) ? exMsg : e.getClass().getSimpleName()));
}
}
@@ -283,19 +286,15 @@ public class LiteFlowChainELBuilder {
String msg = String.format("[node/chain is not exist or node/chain not register]\n EL: %s",
StrUtil.trim(elStr));
try {
InstructionSet parseResult = EXPRESS_RUNNER.getInstructionSetFromLocalCache(elStr);
if (parseResult == null) {
return msg;
}
String[] outAttrNames = parseResult.getOutAttrNames();
if (ArrayUtil.isEmpty(outAttrNames)) {
// 使用 QLExpress4 的 getOutVarNames 方法获取脚本中使用的所有外部变量
Set<String> outVarNames = EXPRESS_RUNNER.getOutVarNames(elStr);
if (CollUtil.isEmpty(outVarNames)) {
return msg;
}
List<String> chainIds = CollUtil.map(FlowBus.getChainMap().values(), Chain::getChainId, true);
List<String> nodeIds = CollUtil.map(FlowBus.getNodeMap().values(), Node::getId, true);
for (String attrName : outAttrNames) {
for (String attrName : outVarNames) {
if (!chainIds.contains(attrName) && !nodeIds.contains(attrName)) {
msg = String.format(
"[%s] is not exist or [%s] is not registered, you need to define a node or chain with id [%s] and register it \n EL: ",
@@ -319,7 +318,7 @@ public class LiteFlowChainELBuilder {
// 还有一种特殊情况,就是 EL 表达式中的节点使用 node("a")
int nodeIndex = sourceEl.indexOf(String.format("node(\"%s\")", attrName));
if (nodeIndex != -1) {
// 需要加上 "EL: " 的长度 4再加上 node(" 长度 6再加上 "^" 的长度 1indexOf 从 0
// 需要加上 "EL: " 的长度 4再加上 "node("" 长度 6再加上 "^" 的长度 1indexOf 从 0
// 开始,所以还需要加 1
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 12, true);
}
@@ -342,9 +341,8 @@ public class LiteFlowChainELBuilder {
return;
}
List<String> errorList = new ArrayList<>();
try {
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
// 这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
// 往上下文里放入所有的chain是的el表达式可以直接引用到chain
@@ -360,9 +358,8 @@ public class LiteFlowChainELBuilder {
// 只有当PARSE_ONE_ON_FIRST_EXEC时才会执行这个方法
// 那么会有一种级联的情况这个EL中含有其他的chain如果这时候不先解析其他chain就到导致诸如循环场景无法设置index或者obj的情况
// 所以这里要判断表达式里有没有其他的chain如果有进行先行解析
String[] itemArray = EXPRESS_RUNNER.getOutVarNames(chain.getEl());
Arrays.stream(itemArray).forEach(item -> {
Set<String> itemSet = EXPRESS_RUNNER.getOutVarNames(chain.getEl());
itemSet.stream().forEach(item -> {
if (FlowBus.containChain(item) && !chain.getChainId().equals(item)) {
Chain itemChain = FlowBus.getChain(item);
if (!itemChain.isCompiled()){
@@ -374,10 +371,11 @@ public class LiteFlowChainELBuilder {
// 解析el成为一个Condition
// 为什么这里只是一个Condition而不是一个List<Condition>呢
// 这里无论多复杂的外面必定有一个最外层的Condition所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
Condition condition = (Condition) EXPRESS_RUNNER.execute(chain.getEl(), context, errorList, true, true);
QLResult expressResult = EXPRESS_RUNNER.execute(chain.getEl(), context, QLOptions.builder().cache(true).build());
Condition condition = (Condition) expressResult.getResult();
if (Objects.isNull(condition)){
throw new QLException(StrUtil.format("parse el fail,el:[{}]", chain.getEl()));
throw new ELParseException(StrUtil.format("parse el fail,el:[{}]", chain.getEl()));
}
// 设置实例id
@@ -400,19 +398,21 @@ public class LiteFlowChainELBuilder {
String msg = buildDataNotFoundExceptionMsg(chain.getEl());
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
String causeMsg = e.getCause().getMessage();
throw new ELParseException(StrUtil.isNotBlank(causeMsg) ? causeMsg : e.getMessage());
}else{
throw new ELParseException(e.getMessage());
throw new ELParseException(StrUtil.isNotBlank(e.getMessage()) ? e.getMessage() : "Unknown EL parse error");
}
} catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
String exMsg = e.getMessage();
throw new ELParseException(errMsg + (StrUtil.isNotBlank(exMsg) ? exMsg : e.getClass().getSimpleName()));
}
}
@SuppressWarnings("unchecked")
private <T extends Executable> T compile(String elStr, List<String> errorList, boolean putChain2Context) throws Exception{
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
if (putChain2Context){
// 这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
@@ -431,8 +431,8 @@ public class LiteFlowChainELBuilder {
// 解析el成为一个Condition
// 为什么这里只是一个Condition而不是一个List<Condition>呢
// 这里无论多复杂的外面必定有一个最外层的Condition所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
return (T) EXPRESS_RUNNER.execute(elStr, context, errorList, true, true);
QLResult expressResult = EXPRESS_RUNNER.execute(elStr, context, QLOptions.builder().cache(true).build());
return (T) expressResult.getResult();
}
}

View File

@@ -1,6 +1,6 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
@@ -41,7 +41,7 @@ public class DoOperator extends BaseOperator<Condition> {
}
else {
String errorMsg = "The caller must be LoopCondition or CatchCondition item";
throw new QLException(errorMsg);
throw new ELParseException(errorMsg);
}
}

View File

@@ -1,6 +1,6 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;

View File

@@ -3,7 +3,7 @@ package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.core.NodeComponent;
@@ -46,7 +46,7 @@ public class ForOperator extends BaseOperator<ForCondition> {
node.setId(nodeForComponent.getNodeId());
}
else {
throw new QLException("The parameter must be Node item");
throw new ELParseException("The parameter must be Node item");
}
ForCondition forCondition = new ForCondition();

View File

@@ -1,6 +1,6 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;

View File

@@ -1,7 +1,7 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.NodeTypeEnum;

View File

@@ -2,7 +2,7 @@ package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrFormatter;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Condition;
@@ -42,7 +42,7 @@ public abstract class MaxWaitTimeOperator extends BaseOperator<Condition> {
} else if (executable instanceof FinallyCondition) {
// FINALLY报错
String errorMsg = StrFormatter.format("The caller [{}] cannot use the keyword \"{}'\"", executable.toString(), operatorName());
throw new QLException(errorMsg);
throw new ELParseException(errorMsg);
} else if (containsFinally(executable)) {
// 处理 THEN 中的 FINALLY
ThenCondition thenCondition = OperatorHelper.convert(executable, ThenCondition.class);

View File

@@ -1,6 +1,6 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.ParallelStrategyEnum;
@@ -24,7 +24,7 @@ public class PercentageOperator extends BaseOperator<WhenCondition> {
Double percentage = OperatorHelper.convert2Double(objects[1]);
if (percentage > 1 || percentage < 0) {
throw new QLException("The percentage must be between 0 and 1.");
throw new ELParseException("The percentage must be between 0 and 1.");
}
whenCondition.setParallelStrategy(ParallelStrategyEnum.PERCENTAGE);

View File

@@ -1,7 +1,7 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.NodeTypeEnum;

View File

@@ -1,7 +1,7 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.FlowBus;

View File

@@ -1,6 +1,6 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Condition;
@@ -37,7 +37,7 @@ public class ThreadPoolOperator extends BaseOperator<Condition> {
return condition;
} else {
String errorMsg = "The caller must be WhenCondition or LoopCondition item";
throw new QLException(errorMsg);
throw new ELParseException(errorMsg);
}
}

View File

@@ -1,23 +1,23 @@
package com.yomahub.liteflow.builder.el.operator.base;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.alibaba.qlexpress4.api.QLFunctionalVarargs;
import com.alibaba.qlexpress4.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
/**
* BaseOperator 为了强化 executeInner 方法,会捕获抛出的 QLException 错误,输出友好的错误提示
* BaseOperator 为了强化 call 方法,会捕获抛出的 QLException 错误,输出友好的错误提示
*
* @author gaibu
* @since 2.8.6
*/
public abstract class BaseOperator<T extends Executable> extends Operator {
public abstract class BaseOperator<T extends Executable> implements QLFunctionalVarargs {
@Override
public T executeInner(Object[] objects) throws Exception {
public T call(Object... parameters) {
try {
OperatorHelper.checkItemNotNull(objects);
return build(objects);
OperatorHelper.checkItemNotNull(parameters);
return build(parameters);
}
catch (QLException e) {
throw e;

View File

@@ -2,11 +2,11 @@ package com.yomahub.liteflow.builder.el.operator.base;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.ExecuteableTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.DataNotFoundException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
@@ -25,50 +25,50 @@ public class OperatorHelper {
/**
* 检查参数数量,大于 0
* @param objects objects
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeGtZero(Object[] objects) throws QLException {
public static void checkObjectSizeGtZero(Object[] objects) throws ELParseException {
if (objects.length == 0) {
throw new QLException("parameter is empty");
throw new ELParseException("parameter is empty");
}
}
/**
* 检查参数数量,大于等于 2
* @param objects objects
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeGteTwo(Object[] objects) throws QLException {
public static void checkObjectSizeGteTwo(Object[] objects) throws ELParseException {
checkObjectSizeGtZero(objects);
if (objects.length < 2) {
throw new QLException("parameter size error");
throw new ELParseException("parameter size error");
}
}
/**
* 检查参数数量,等于 1
* @param objects objects
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeEqOne(Object[] objects) throws QLException {
public static void checkObjectSizeEqOne(Object[] objects) throws ELParseException {
checkObjectSizeEq(objects, 1);
}
/**
* 检查参数数量,等于 2
* @param objects objects
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeEqTwo(Object[] objects) throws QLException {
public static void checkObjectSizeEqTwo(Object[] objects) throws ELParseException {
checkObjectSizeEq(objects, 2);
}
/**
* 检查参数数量,等于 3
* @param objects objects
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeEqThree(Object[] objects) throws QLException {
public static void checkObjectSizeEqThree(Object[] objects) throws ELParseException {
checkObjectSizeEq(objects, 3);
}
@@ -76,13 +76,13 @@ public class OperatorHelper {
* 检查参数数量,等于 size
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeEq(Object[] objects, int size) throws QLException {
public static void checkObjectSizeEq(Object[] objects, int size) throws ELParseException {
checkObjectSizeGtZero(objects);
if (objects.length != size) {
throw new QLException("parameter size error");
throw new ELParseException("parameter size error");
}
}
@@ -91,13 +91,13 @@ public class OperatorHelper {
* @param objects objects
* @param size1 参数数量1
* @param size2 参数数量2
* @throws QLException QLException
* @throws ELParseException ELParseException
*/
public static void checkObjectSizeEq(Object[] objects, int size1, int size2) throws QLException {
public static void checkObjectSizeEq(Object[] objects, int size1, int size2) throws ELParseException {
checkObjectSizeGtZero(objects);
if (objects.length != size1 && objects.length != size2) {
throw new QLException("parameter size error");
throw new ELParseException("parameter size error");
}
}
@@ -105,12 +105,12 @@ public class OperatorHelper {
* 转换 object 为指定的类型 如果是Node类型的则进行copy
* 为什么要进行copy呢因为原先的Node都是存放在FlowBus的NodeMap中的。有些属性在EL中不是全局的属于当前这个chain的。 所以要进行copy动作
*/
public static <T> T convert(Object object, Class<T> clazz) throws QLException {
public static <T> T convert(Object object, Class<T> clazz) throws ELParseException {
String errorMsg = StrUtil.format("The parameter must be {} item", clazz.getSimpleName());
return convert(object, clazz, errorMsg);
}
public static Double convert2Double(Object object) throws QLException {
public static Double convert2Double(Object object) throws ELParseException {
if (object instanceof Number) {
// 对 float 特别处理,避免精度问题
if (object instanceof Float) {
@@ -119,14 +119,14 @@ public class OperatorHelper {
}
return ((Number) object).doubleValue();
}
throw new QLException(StrUtil.format("Unsupported type: {}, it must be numeric type.", object.getClass().getName()));
throw new ELParseException(StrUtil.format("Unsupported type: {}, it must be numeric type.", object.getClass().getName()));
}
/**
* 转换 object 为指定的类型,自定义错误信息 如果是Node类型的则进行copy
*/
@SuppressWarnings("unchecked")
public static <T> T convert(Object object, Class<T> clazz, String errorMsg) throws QLException {
public static <T> T convert(Object object, Class<T> clazz, String errorMsg) throws ELParseException {
try {
if (clazz.isAssignableFrom(object.getClass())) {
if (object instanceof Node) {
@@ -139,16 +139,16 @@ public class OperatorHelper {
}
}
catch (Exception e) {
throw new QLException("An error occurred while copying an object");
throw new ELParseException("An error occurred while copying an object");
}
throw new QLException(errorMsg);
throw new ELParseException(errorMsg);
}
public static void checkItemNotNull(Object[] objects) throws QLException {
public static void checkItemNotNull(Object[] objects) throws ELParseException {
for (Object object : objects) {
if (Objects.isNull(object)) {
throw new QLException(DataNotFoundException.MSG);
throw new ELParseException(DataNotFoundException.MSG);
}
}
}
@@ -159,13 +159,13 @@ public class OperatorHelper {
*/
public static void checkObjMustBeCommonTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
throw new ELParseException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.COMMON, NodeTypeEnum.SCRIPT, NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be a common type component", node.getId()));
throw new ELParseException(StrUtil.format("The node[{}] must be a common type component", node.getId()));
}
}
}
@@ -178,7 +178,7 @@ public class OperatorHelper {
*/
public static void checkObjMustBeBooleanTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
throw new ELParseException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
@@ -186,21 +186,21 @@ public class OperatorHelper {
if (!ListUtil.toList(NodeTypeEnum.BOOLEAN,
NodeTypeEnum.BOOLEAN_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be boolean type Node.", node.getId()));
throw new ELParseException(StrUtil.format("The node[{}] must be boolean type Node.", node.getId()));
}
}else if(item.getExecuteType().equals(ExecuteableTypeEnum.CONDITION)){
Condition condition = (Condition) item;
if (!ListUtil.toList(ConditionTypeEnum.TYPE_AND_OR_OPT, ConditionTypeEnum.TYPE_NOT_OPT).contains(condition.getConditionType())){
throw new QLException(StrUtil.format("The condition[{}] must be boolean type Condition.", condition.getId()));
throw new ELParseException(StrUtil.format("The condition[{}] must be boolean type Condition.", condition.getId()));
}
}else{
throw new QLException("The parameter error.");
throw new ELParseException("The parameter error.");
}
}
public static void checkObjMustBeForTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
throw new ELParseException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
@@ -208,32 +208,32 @@ public class OperatorHelper {
if (!ListUtil.toList(NodeTypeEnum.FOR,
NodeTypeEnum.FOR_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be For type Node.", node.getId()));
throw new ELParseException(StrUtil.format("The node[{}] must be For type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
throw new ELParseException("The parameter error.");
}
}
public static void checkObjMustBeIteratorTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
throw new ELParseException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.ITERATOR,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be Iterator type Node.", node.getId()));
throw new ELParseException(StrUtil.format("The node[{}] must be Iterator type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
throw new ELParseException("The parameter error.");
}
}
public static void checkObjMustBeSwitchTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
throw new ELParseException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
@@ -241,10 +241,10 @@ public class OperatorHelper {
if (!ListUtil.toList(NodeTypeEnum.SWITCH,
NodeTypeEnum.SWITCH_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be Switch type Node.", node.getId()));
throw new ELParseException(StrUtil.format("The node[{}] must be Switch type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
throw new ELParseException("The parameter error.");
}
}
}

View File

@@ -6,9 +6,9 @@ import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.alibaba.qlexpress4.Express4Runner;
import com.alibaba.qlexpress4.QLResult;
import com.alibaba.qlexpress4.QLOptions;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -1,13 +1,13 @@
package com.yomahub.liteflow.flow.instanceId;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.entity.InstanceInfoDto;
import com.yomahub.liteflow.util.JsonUtil;
import org.apache.commons.lang.StringUtils;
import java.util.*;
@@ -26,7 +26,7 @@ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManag
*/
@Override
public Node getNodeByIdAndInstanceId(String chainId, String instanceId) {
if (StringUtils.isBlank(chainId) || StringUtils.isBlank(instanceId)) {
if (StrUtil.isBlank(chainId) || StrUtil.isBlank(instanceId)) {
return null;
}
Chain chain = FlowBus.getChain(chainId);
@@ -45,7 +45,7 @@ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManag
*/
@Override
public Node getNodeByIdAndIndex(String chainId, String nodeId, Integer index) {
if (StringUtils.isBlank(chainId) || index == null) {
if (StrUtil.isBlank(chainId) || index == null) {
return null;
}
Chain chain = FlowBus.getChain(chainId);
@@ -64,7 +64,7 @@ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManag
*/
@Override
public List<String> getNodeInstanceIds(String chainId, String nodeId) {
if (StringUtils.isBlank(chainId) || StringUtils.isBlank(nodeId)) {
if (StrUtil.isBlank(chainId) || StrUtil.isBlank(nodeId)) {
return Collections.emptyList();
}
// 第一行为elMd5 第二行为实例id json格式信息
@@ -133,7 +133,7 @@ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManag
*/
@Override
public int getNodeLocationById(String chainId, String instanceId) {
if (StringUtils.isBlank(chainId) || StringUtils.isBlank(instanceId)) {
if (StrUtil.isBlank(chainId) || StrUtil.isBlank(instanceId)) {
return -1;
}
// 第一行为elMd5 第二行为实例id json格式信息

View File

@@ -3,9 +3,9 @@ package com.yomahub.liteflow.flow.instanceId;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.flow.entity.InstanceInfoDto;
import com.yomahub.liteflow.util.JsonUtil;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.util.*;
@@ -23,7 +23,7 @@ public class DefaultNodeInstanceIdManageSpiImpl extends BaseNodeInstanceIdManage
@Override
public List<String> readInstanceIdFile(String chainId) {
if (StringUtils.isBlank(chainId)) {
if (StrUtil.isBlank(chainId)) {
return Collections.emptyList();
}
@@ -36,7 +36,7 @@ public class DefaultNodeInstanceIdManageSpiImpl extends BaseNodeInstanceIdManage
@Override
public void writeInstanceIdFile(List<InstanceInfoDto> instanceIdList, String elMd5, String chainId) {
if (StringUtils.isBlank(chainId) || CollUtil.isEmpty(instanceIdList)) {
if (StrUtil.isBlank(chainId) || CollUtil.isEmpty(instanceIdList)) {
return;
}
File nodeDir = new File(basePath + chainId);

View File

@@ -4,12 +4,14 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.alibaba.qlexpress4.Express4Runner;
import com.alibaba.qlexpress4.QLResult;
import com.alibaba.qlexpress4.InitOptions;
import com.alibaba.qlexpress4.QLOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -26,7 +28,7 @@ import java.util.stream.IntStream;
*/
public class LiteflowContextRegexMatcher {
private static final ExpressRunner expressRunner = new ExpressRunner();
private static final Express4Runner expressRunner = QlExpressUtils.getContextSearchExpressRunner();
public static Object searchContext(List<Tuple> contextList, String regPattern){
// 把上下文数据转换成map形式的key为别名value为上下文
@@ -34,16 +36,14 @@ public class LiteflowContextRegexMatcher {
Collectors.toMap(tuple -> tuple.get(0), tuple -> tuple.get(1))
);
List<String> errorList = new ArrayList<>();
Object result = null;
// 根据表达式去上下文里搜索相匹配的数据
for(Map.Entry<String, Object> entry : contextMap.entrySet()){
try{
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(entry.getKey() + "." + regPattern);
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
context.put(entry.getKey(), entry.getValue());
result = expressRunner.execute(instructionSet, context, errorList, false, false);
QLResult expressResult = expressRunner.execute(entry.getKey() + "." + regPattern, context, QLOptions.builder().cache(true).build());
result = expressResult.getResult();
if (result != null){
break;
}
@@ -53,10 +53,10 @@ public class LiteflowContextRegexMatcher {
if (result == null){
try{
// 如果没有搜到,那么尝试推断表达式是指定的上下文,按照指定上下文的方式去再获取
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache("contextMap." + regPattern);
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
context.put("contextMap", contextMap);
result = expressRunner.execute(instructionSet, context, errorList, false, false);
QLResult expressResult = expressRunner.execute("contextMap." + regPattern, context, QLOptions.builder().cache(true).build());
result = expressResult.getResult();
}catch (Exception ignore){}
}
@@ -69,8 +69,6 @@ public class LiteflowContextRegexMatcher {
Collectors.toMap(tuple -> tuple.get(0), tuple -> tuple.get(1))
);
List<String> errorList = new ArrayList<>();
boolean flag = false;
String argStr = IntStream.range(0, args.length).mapToObj(
@@ -83,11 +81,10 @@ public class LiteflowContextRegexMatcher {
for(Map.Entry<String, Object> entry : contextMap.entrySet()){
try{
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(StrUtil.format("{}.{}({})", entry.getKey(), methodExpress, argStr));
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
context.put(entry.getKey(), entry.getValue());
tupleList.forEach(tuple -> context.put(tuple.getA(), args[tuple.getB()]));
expressRunner.execute(instructionSet, context, errorList, false, false);
expressRunner.execute(StrUtil.format("{}.{}({})", entry.getKey(), methodExpress, argStr), context, QLOptions.builder().cache(true).build());
flag = true;
break;
}catch (Exception ignore){}
@@ -97,11 +94,10 @@ public class LiteflowContextRegexMatcher {
if (BooleanUtil.isFalse(flag)){
try{
// 如果没有搜到,那么尝试推断表达式是指定的上下文,按照指定上下文的方式去再获取
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(StrUtil.format("contextMap.{}({})", methodExpress, argStr));
DefaultContext<String, Object> context = new DefaultContext<>();
Map<String, Object> context = new HashMap<>();
context.put("contextMap", contextMap);
tupleList.forEach(tuple -> context.put(tuple.getA(), args[tuple.getB()]));
expressRunner.execute(instructionSet, context, errorList, false, false);
expressRunner.execute(StrUtil.format("contextMap.{}({})", methodExpress, argStr), context, QLOptions.builder().cache(true).build());
}catch (Exception ignore){}
}
}

View File

@@ -1,6 +1,9 @@
package com.yomahub.liteflow.util;
import com.ql.util.express.ExpressRunner;
import com.alibaba.qlexpress4.Express4Runner;
import com.alibaba.qlexpress4.InitOptions;
import com.alibaba.qlexpress4.aparser.InterpolationMode;
import com.alibaba.qlexpress4.security.QLSecurityStrategy;
import com.yomahub.liteflow.builder.el.operator.*;
import com.yomahub.liteflow.common.ChainConstant;
@@ -14,58 +17,72 @@ public class QlExpressUtils {
/**
* EL解析引擎
* InterpolationMode.DISABLE意思是不对插值表达式进行处理
*/
private final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
private final static Express4Runner EXPRESS_RUNNER = new Express4Runner(InitOptions.builder().interpolationMode(InterpolationMode.DISABLE).build());
/**
* 上下文搜索解析引擎
* QLSecurityStrategy.open()意思是将安全策略设置为开放
*/
private final static Express4Runner CONTEXT_SEARCH_EXPRESS_RUNNER = new Express4Runner(InitOptions.builder().securityStrategy(QLSecurityStrategy.open()).build());
static {
// 初始化QLExpress的Runner
EXPRESS_RUNNER.addFunction(ChainConstant.THEN, new ThenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHEN, new WhenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.SER, new ThenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.PAR, new WhenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.SWITCH, new SwitchOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.PRE, new PreOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FINALLY, new FinallyOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.IF, new IfOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE.toUpperCase(), new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.CATCH, new CatchOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.AND, new AndOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.OR, new OrOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NOT, new NotOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELSE, Object.class, new ElseOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELIF, Object.class, new ElifOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TO, Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TO.toLowerCase(), Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DEFAULT, Object.class, new DefaultOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TAG, Object.class, new TagOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ANY, Object.class, new AnyOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MUST, Object.class, new MustOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PERCENTAGE, Object.class, new PercentageOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ID, Object.class, new IdOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.IGNORE_ERROR, Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.THREAD_POOL, Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DO, Object.class, new DoOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BREAK, Object.class, new BreakOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DATA, Object.class, new DataOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PARALLEL, Object.class, new ParallelOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY, Object.class, new RetryOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BIND, Object.class, new BindOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.THEN, new ThenOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.WHEN, new WhenOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.SER, new ThenOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.PAR, new WhenOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.SWITCH, new SwitchOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.PRE, new PreOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.FINALLY, new FinallyOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.IF, new IfOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.NODE.toUpperCase(), new NodeOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.NODE, new NodeOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.FOR, new ForOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.WHILE, new WhileOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.ITERATOR, new IteratorOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.CATCH, new CatchOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.AND, new AndOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.OR, new OrOperator());
EXPRESS_RUNNER.addVarArgsFunction(ChainConstant.NOT, new NotOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.ELSE, Object.class, new ElseOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.ELIF, Object.class, new ElifOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.TO, Object.class, new ToOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.TO.toLowerCase(), Object.class, new ToOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.DEFAULT, Object.class, new DefaultOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.TAG, Object.class, new TagOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.ANY, Object.class, new AnyOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.MUST, Object.class, new MustOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.PERCENTAGE, Object.class, new PercentageOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.ID, Object.class, new IdOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.IGNORE_ERROR, Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.THREAD_POOL, Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.DO, Object.class, new DoOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.BREAK, Object.class, new BreakOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.DATA, Object.class, new DataOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.PARALLEL, Object.class, new ParallelOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.RETRY, Object.class, new RetryOperator());
EXPRESS_RUNNER.addExtendFunction(ChainConstant.BIND, Object.class, new BindOperator());
}
/**
* 获取QLExpress的实例
*/
public static ExpressRunner getInstance() {
public static Express4Runner getELExpressRunner() {
return EXPRESS_RUNNER;
}
/**
* 获取上下文搜索的QLExpress实例
*/
public static Express4Runner getContextSearchExpressRunner() {
return CONTEXT_SEARCH_EXPRESS_RUNNER;
}
/**
* 检查变量名是否符合 变量命名规则
*

View File

@@ -7,7 +7,6 @@ import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.InstanceIdVO;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import org.apache.commons.lang.StringUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -59,7 +58,7 @@ public class InstanceIdRead extends AbstractSqlRead<InstanceIdVO> {
String instanceIdApplicationNameField = super.config.getInstanceIdApplicationNameField();
String applicationName = super.config.getApplicationName();
if (StringUtils.isEmpty(chainId)) {
if (StrUtil.isEmpty(chainId)) {
throw new IllegalArgumentException("You did not define the chainId");
}
return StrUtil.format(SqlReadConstant.SQL_PATTERN_WITH_CHAIN_ID, tableName, instanceIdApplicationNameField

View File

@@ -8,7 +8,6 @@ import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.ScriptVO;
import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import org.apache.commons.lang.StringUtils;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -56,7 +55,7 @@ public class ScriptRead extends AbstractSqlRead<ScriptVO> {
@Override
public String buildQuerySql() {
if (StringUtils.isNotBlank(super.config.getScriptCustomSql())) {
if (StrUtil.isNotBlank(super.config.getScriptCustomSql())) {
return super.config.getScriptCustomSql();
}
@@ -72,7 +71,7 @@ public class ScriptRead extends AbstractSqlRead<ScriptVO> {
@Override
public String buildQuerySql(String scriptNodeId) {
if (StringUtils.isNotBlank(super.config.getScriptCustomSql())) {
if (StrUtil.isNotBlank(super.config.getScriptCustomSql())) {
return super.config.getScriptCustomSql();
}

View File

@@ -16,7 +16,6 @@ import com.yomahub.liteflow.parser.sql.read.SqlReadFactory;
import com.yomahub.liteflow.parser.sql.read.vo.ChainVO;
import com.yomahub.liteflow.parser.sql.read.vo.ScriptVO;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -116,7 +115,7 @@ public class JDBCHelper {
String language = scriptVO.getLanguage();
String elData = scriptVO.getScript();
if (StringUtils.isNotBlank(scriptVO.getLanguage())) {
if (StrUtil.isNotBlank(scriptVO.getLanguage())) {
scriptList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, language, elData));
} else {
scriptList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, elData));

View File

@@ -23,7 +23,7 @@
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<artifactId>qlexpress4</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,11 +1,11 @@
package com.yomahub.liteflow.script.qlexpress;
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.alibaba.qlexpress4.Express4Runner;
import com.alibaba.qlexpress4.QLResult;
import com.alibaba.qlexpress4.InitOptions;
import com.alibaba.qlexpress4.QLOptions;
import com.alibaba.qlexpress4.security.QLSecurityStrategy;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutor;
@@ -13,10 +13,8 @@ import com.yomahub.liteflow.script.exception.ScriptLoadException;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,13 +28,13 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private ExpressRunner expressRunner;
private Express4Runner expressRunner;
private final Map<String, InstructionSet> compiledScriptMap = new CopyOnWriteHashMap<>();
private final Map<String, String> compiledScriptMap = new CopyOnWriteHashMap<>();
@Override
public ScriptExecutor init() {
expressRunner = new ExpressRunner(true, false);
expressRunner = new Express4Runner(InitOptions.builder().securityStrategy(QLSecurityStrategy.open()).build());
//如果有生命周期则执行相应生命周期实现
super.lifeCycle(expressRunner);
return this;
@@ -45,7 +43,8 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
@Override
public void load(String nodeId, String script) {
try {
compiledScriptMap.put(nodeId, (InstructionSet) compile(script));
expressRunner.parseToDefinitionWithCache(script);
compiledScriptMap.put(nodeId, script);
}
catch (Exception e) {
String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage());
@@ -65,24 +64,21 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
@Override
public Object executeScript(ScriptExecuteWrap wrap) throws Exception {
List<String> errorList = new ArrayList<>();
try {
if (!compiledScriptMap.containsKey(wrap.getNodeId())) {
String errorMsg = StrUtil.format("script for node[{}] is not loaded", wrap.getNodeId());
throw new ScriptLoadException(errorMsg);
}
InstructionSet instructionSet = compiledScriptMap.get(wrap.getNodeId());
DefaultContext<String, Object> context = new DefaultContext<>();
String script = compiledScriptMap.get(wrap.getNodeId());
Map<String, Object> context = new HashMap<>();
bindParam(wrap, context::put, context::putIfAbsent);
return expressRunner.execute(instructionSet, context, errorList, true, false);
QLResult expressResult = expressRunner.execute(script, context, QLOptions.builder().cache(true).build());
return expressResult.getResult();
}
catch (Exception e) {
for (String scriptErrorMsg : errorList) {
log.error("\n{}", scriptErrorMsg);
}
throw e;
}
}
@@ -90,8 +86,9 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
@Override
public void cleanCache() {
compiledScriptMap.clear();
expressRunner.clearExpressCache();
ReflectUtil.setFieldValue(expressRunner, "loader", new ExpressLoader(expressRunner));
expressRunner.clearCompileCache();
//如果有生命周期则执行相应生命周期实现
super.lifeCycle(expressRunner);
}
@Override
@@ -101,7 +98,7 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
@Override
public Object compile(String script) throws Exception {
return expressRunner.getInstructionSetFromLocalCache(script);
return expressRunner.parseToDefinitionWithCache(script);
}
}

View File

@@ -0,0 +1,38 @@
package com.yomahub.liteflow.test.parent;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
/**
* springboot环境最普通的例子测试
*
* @author Bryan.Zhang
* @since 2.6.4
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/parent/application.properties")
@SpringBootTest(classes = ParentDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.parent.cmp" })
public class ParentDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testP1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
}
}

View File

@@ -0,0 +1,31 @@
package com.yomahub.liteflow.test.parent.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.test.base.cmp.TestDomain;
import javax.annotation.Resource;
@LiteflowComponent
public class CmpConfig extends ParentClass{
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a")
public void processA(NodeComponent bindCmp) {
this.setName("jack");
System.out.println(this.sayHi());
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b")
public void processB(NodeComponent bindCmp) {
this.setName("tom");
System.out.println(this.sayHi());
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "c")
public void processC(NodeComponent bindCmp) {
this.setName("jerry");
System.out.println(this.sayHi());
}
}

View File

@@ -0,0 +1,14 @@
package com.yomahub.liteflow.test.parent.cmp;
public class ParentClass {
private String name;
public void setName(String name) {
this.name = name;
}
public String sayHi() {
return "hello " + name;
}
}

View File

@@ -0,0 +1 @@
liteflow.rule-source=parent/flow.el.xml

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>

View File

@@ -97,8 +97,17 @@ public class BaseELSpringbootTest extends BaseTest {
// 应返回 chain2
Assertions.assertEquals("chain2", response.getChainId());
LiteflowResponse response1 = flowExecutor.execute2RespWithEL("t1=THEN(c, WHEN(j,k));w1 = WHEN(q, THEN(p, r)).id('w01');t2 = THEN(h, i);\n" +
"THEN(a,b,WHEN(t1, d, t2 ),SWITCH(x).to(m, n, w1),z);");
LiteflowResponse response1 = flowExecutor.execute2RespWithEL("t1 = THEN(c, WHEN(j,k));\n" +
" w1 = WHEN(q, THEN(p, r)).id(\"w01\");\n" +
" t2 = THEN(h, i);\n" +
"\n" +
" THEN(\n" +
" a,b,\n" +
" WHEN(t1, d, t2 ),\n" +
" SWITCH(x).to(m, n, w1),\n" +
" z\n" +
" );\n" +
" THEN(a,b,b,a,SWITCH(e).TO(d,b));");
Assertions.assertTrue(response1.isSuccess());
// 应返回 chain5
Assertions.assertEquals("chain5", response1.getChainId());

View File

@@ -50,5 +50,7 @@
SWITCH(x).to(m, n, w1),
z
);
THEN(a,b,b,a,SWITCH(e).TO(d,b))
</chain>
</flow>
</flow>

View File

@@ -39,7 +39,7 @@
</scm>
<properties>
<revision>2.15.1</revision>
<revision>2.15.2</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
@@ -58,7 +58,7 @@
<zkclient.version>0.10</zkclient.version>
<jetcd.version>0.7.3</jetcd.version>
<nacos.version>1.4.4</nacos.version>
<qlexpress.version>3.3.4</qlexpress.version>
<qlexpress.version>4.0.6-SNAPSHOT</qlexpress.version>
<groovy.version>3.0.25</groovy.version>
<graalvm.version>22.0.0</graalvm.version>
<bytebuddy.version>1.17.7</bytebuddy.version>
@@ -227,7 +227,7 @@
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<artifactId>qlexpress4</artifactId>
<version>${qlexpress.version}</version>
</dependency>
<dependency>