From 99413e6c3e434d62536800dcabfb6a8003fd4404 Mon Sep 17 00:00:00 2001 From: Dale Lee <1658850308@qq.com> Date: Wed, 12 Jul 2023 11:38:11 +0800 Subject: [PATCH] =?UTF-8?q?feature=20#I7I3LL=20=E5=A2=9E=E5=8A=A0TimeoutCo?= =?UTF-8?q?ndition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../el/operator/MaxWaitSecondsOperator.java | 35 +++++------ .../element/condition/TimeoutCondition.java | 58 +++++++++++++++++++ .../MaxWaitSecondsELSpringbootTest.java | 27 ++++++++- .../test/resources/maxWaitSeconds/flow.el.xml | 12 ++++ 4 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/TimeoutCondition.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MaxWaitSecondsOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MaxWaitSecondsOperator.java index 89bb528be..e76252402 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MaxWaitSecondsOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MaxWaitSecondsOperator.java @@ -1,15 +1,13 @@ 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.builder.el.operator.base.BaseOperator; import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; import com.yomahub.liteflow.flow.element.Condition; import com.yomahub.liteflow.flow.element.Executable; -import com.yomahub.liteflow.flow.element.condition.ConditionKey; -import com.yomahub.liteflow.flow.element.condition.FinallyCondition; -import com.yomahub.liteflow.flow.element.condition.ThenCondition; -import com.yomahub.liteflow.flow.element.condition.WhenCondition; +import com.yomahub.liteflow.flow.element.condition.*; import java.util.List; import java.util.concurrent.TimeUnit; @@ -35,7 +33,7 @@ public class MaxWaitSecondsOperator extends BaseOperator { return whenCondition; } else if (executable instanceof FinallyCondition) { // FINALLY,报错 - String errorMsg = "The caller cannot be FinallyCondition item"; + String errorMsg = StrFormatter.format("The caller [{}] cannot use the keyword \"maxWaitSeconds'\"", executable.toString()); throw new QLException(errorMsg); } else if (containsFinally(executable)) { // 处理 THEN 中的 FINALLY @@ -43,26 +41,28 @@ public class MaxWaitSecondsOperator extends BaseOperator { return handleFinally(thenCondition, maxWaitSeconds); } else { // 其他情况,被 WHEN 包装 - return wrappedByWhen(executable, maxWaitSeconds); + return wrappedByTimeout(executable, maxWaitSeconds); } } /** - * 将一个 Executable 包装为带有单独超时控制的 WhenCondition - * @param executable 带包装对象 + * 将一个 Executable 包装为带有单独超时控制的 TimeoutCondition + * + * @param executable 待包装对象 * @param maxWaitSeconds 最大等待秒数 - * @return 包装后的 WhenCondition + * @return 包装后的 TimeoutCondition */ - private WhenCondition wrappedByWhen(Executable executable, Integer maxWaitSeconds) { - WhenCondition whenCondition = new WhenCondition(); - whenCondition.addExecutable(executable); - whenCondition.setMaxWaitTime(maxWaitSeconds); - whenCondition.setMaxWaitTimeUnit(TimeUnit.SECONDS); - return whenCondition; + private TimeoutCondition wrappedByTimeout(Executable executable, Integer maxWaitSeconds) { + TimeoutCondition timeoutCondition = new TimeoutCondition(); + timeoutCondition.addExecutable(executable); + timeoutCondition.setMaxWaitTime(maxWaitSeconds); + timeoutCondition.setMaxWaitTimeUnit(TimeUnit.SECONDS); + return timeoutCondition; } /** * 判断 THEN 中是否含有 FINALLY 组件 + * * @param executable 判断对象 * @return 含有 FINALLY 组件返回 true,否则返回 false */ @@ -73,7 +73,8 @@ public class MaxWaitSecondsOperator extends BaseOperator { /** * 将 FINALLY 排除在超时控制之外 - * @param thenCondition 待处理的 ThenCondition + * + * @param thenCondition 待处理的 ThenCondition * @param maxWaitSeconds 最大等待秒数 * @return 处理后的 ThenCondition */ @@ -95,7 +96,7 @@ public class MaxWaitSecondsOperator extends BaseOperator { finallyList.clear(); // 包装内部 THEN - WhenCondition whenCondition = wrappedByWhen(thenCondition, maxWaitSeconds); + WhenCondition whenCondition = wrappedByTimeout(thenCondition, maxWaitSeconds); outerThenCondition.addExecutable(whenCondition); return outerThenCondition; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/TimeoutCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/TimeoutCondition.java new file mode 100644 index 000000000..290535f62 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/TimeoutCondition.java @@ -0,0 +1,58 @@ +package com.yomahub.liteflow.flow.element.condition; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.exception.WhenTimeoutException; +import com.yomahub.liteflow.flow.element.Chain; +import com.yomahub.liteflow.flow.element.Condition; +import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.Node; + +import java.util.concurrent.TimeoutException; + +/** + * 超时控制 Condition + * + * @author DaleLee + * @since 2.11.0 + */ +public class TimeoutCondition extends WhenCondition { + + @Override + public void executeCondition(Integer slotIndex) throws Exception { + try { + super.executeCondition(slotIndex); + } catch (WhenTimeoutException ex) { + // 将 WhenTimeoutException 转换为 TimeoutException + String errMsg = StrFormatter.format("Timed out when executing the chain [{}] because [{}] exceeded {} {}.", + this.getCurrChainId(), this.getCurrentExecutableId(), this.getMaxWaitTime(), this.getMaxWaitTimeUnit().toString().toLowerCase()); + throw new TimeoutException(errMsg); + } + } + + /** + * 获取当前组件的 id + * + * @return + */ + private String getCurrentExecutableId() { + // TimeoutCondition 只有一个 Executable + Executable executable = this.getExecutableList().get(0); + if (ObjectUtil.isNotNull(executable.getId())) { + // 已经有 id 了 + return executable.getId(); + } + // 定义 id + switch (executable.getExecuteType()) { + // chain 和 node 一般都有 id + case CHAIN: + return ((Chain) executable).getChainId(); + case CONDITION: + return "condition-" + ((Condition) executable).getConditionType().getName(); + case NODE: + return "node-" + ((Node) executable).getType().getCode(); + default: + return "unknown-executable"; + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java index b654afa19..7e980d595 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java @@ -22,6 +22,8 @@ import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; +import java.util.concurrent.TimeoutException; + import static com.yomahub.liteflow.test.maxWaitSeconds.cmp.DCmp.CONTENT_KEY; /** @@ -34,7 +36,7 @@ import static com.yomahub.liteflow.test.maxWaitSeconds.cmp.DCmp.CONTENT_KEY; @TestPropertySource(value = "classpath:/maxWaitSeconds/application.properties") @SpringBootTest(classes = MaxWaitSecondsELSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.maxWaitSeconds.cmp" }) +@ComponentScan({"com.yomahub.liteflow.test.maxWaitSeconds.cmp"}) public class MaxWaitSecondsELSpringbootTest extends BaseTest { @Resource @@ -55,7 +57,8 @@ public class MaxWaitSecondsELSpringbootTest extends BaseTest { // 测试 When 的超时情况 @Test public void testWhen1() { - assertTimeout("when1"); + // WHEN 抛出的是 WhenTimeoutException + assertWhenTimeout("when1"); } // 测试 WHEN 的非超时情况 @@ -141,7 +144,7 @@ public class MaxWaitSecondsELSpringbootTest extends BaseTest { public void testFinally1() { LiteflowResponse response = flowExecutor.execute2Resp("finally", "arg"); Assert.assertFalse(response.isSuccess()); - Assert.assertEquals(WhenTimeoutException.class, response.getCause().getClass()); + Assert.assertEquals(TimeoutException.class, response.getCause().getClass()); // FINALLY 执行时在默认数据上下文中放入了 CONTENT_KEY DefaultContext contextBean = response.getFirstContextBean(); Assert.assertTrue(contextBean.hasData(CONTENT_KEY)); @@ -172,7 +175,25 @@ public class MaxWaitSecondsELSpringbootTest extends BaseTest { Assert.assertFalse(LiteFlowChainELBuilder.validate("THEN(a, b, FINALLY(c).maxWaitSeconds(10))")); } + // 测试 chain 的超时情况 + @Test + public void testChain1() { + assertTimeout("chain1"); + } + + // 测试 chain 的非超时情况 + @Test + public void testChain2() { + assertNotTimeout("chain2"); + } + private void assertTimeout(String chainId) { + LiteflowResponse response = flowExecutor.execute2Resp(chainId, "arg"); + Assert.assertFalse(response.isSuccess()); + Assert.assertEquals(TimeoutException.class, response.getCause().getClass()); + } + + private void assertWhenTimeout(String chainId) { LiteflowResponse response = flowExecutor.execute2Resp(chainId, "arg"); Assert.assertFalse(response.isSuccess()); Assert.assertEquals(WhenTimeoutException.class, response.getCause().getClass()); diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml index e32dc3203..91869ae50 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml @@ -95,4 +95,16 @@ THEN(PRE(a), b, FINALLY(d)).maxWaitSeconds(2); + + + THEN(b) + + + + testChain.maxWaitSeconds(1); + + + + testChain.maxWaitSeconds(3); + \ No newline at end of file