diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java index 7ce55ea09..4ce4ce0c8 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java @@ -31,6 +31,7 @@ import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.util.ElRegexUtil; +import com.yomahub.liteflow.util.QlExpressUtils; import java.util.ArrayList; import java.util.Arrays; @@ -66,53 +67,10 @@ public class LiteFlowChainELBuilder { * 所以在这里做一个缓存,等conditionList全部build完毕后,再去一次性替换chain里面的conditionList */ private final List conditionList; - - /** - * EL解析引擎 - */ - public final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); - - 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()); - - } + /** + * EL解析引擎 + */ + private final static ExpressRunner EXPRESS_RUNNER = QlExpressUtils.getInstance(); public static LiteFlowChainELBuilder createChain() { return new LiteFlowChainELBuilder(); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeIdUnIllegalException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeIdUnIllegalException.java new file mode 100644 index 000000000..820aa03fa --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeIdUnIllegalException.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.exception; + +/** + * node id不合法异常 + * + * @author tangkc + * @since 2.13.2 + */ +public class NodeIdUnIllegalException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 异常信息 + */ + private String message; + + public NodeIdUnIllegalException(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index 940e90893..60f03c688 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -24,6 +24,7 @@ import com.yomahub.liteflow.enums.FlowParserTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.enums.ParseModeEnum; import com.yomahub.liteflow.exception.ComponentCannotRegisterException; +import com.yomahub.liteflow.exception.NodeIdUnIllegalException; import com.yomahub.liteflow.exception.NullNodeTypeException; import com.yomahub.liteflow.flow.element.Chain; import com.yomahub.liteflow.flow.element.Node; @@ -43,6 +44,7 @@ import com.yomahub.liteflow.spi.ContextAware; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder; import com.yomahub.liteflow.util.CopyOnWriteHashMap; +import com.yomahub.liteflow.util.QlExpressUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -303,6 +305,14 @@ public class FlowBus { // 调用到这里,分两种情况,一是脚本组件,二是通过LiteFlowNodeBuilder代码进行组装的组件 private static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz, String script, String language) { try { + String nodeIdStr = StrUtil.isBlank(nodeId) ? name : nodeId; + // 检查nodeId是否合法 + boolean nodeIdFlag = QlExpressUtils.checkVariableName(nodeIdStr); + // node id 不合法 + if (!nodeIdFlag) { + throw new NodeIdUnIllegalException(nodeIdStr); + } + // 获得初始化好的NodeComponent // 按理说一个nodeId对应一个NodeComponent,这里得到的是List的原因是,声明式组件有可能会有多个nodeId。 // 声明式组件又分类声明和方法声明,如果对于方法声明来说,这里的nodeId其实并不是最终真正的nodeId。 @@ -314,7 +324,22 @@ public class FlowBus { for (int i = 0; i < nodes.size(); i++) { addCompiledNode2Map(nodes.get(i), nodeId, script, language, type, cmpInstanceList.get(i)); } - } catch (Exception e) { + } catch (NodeIdUnIllegalException e) { + String nodeIdStr = e.getMessage(); + String error = StrUtil.format( + "component[{}] register error", + StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name) + ); + + error = "Invalid node id: [" + nodeIdStr + "]. " + + "node id must follow variable naming rules: " + + "cannot start with a digit, must consist of letters, digits, underscores (_), or dollar signs ($), " + + "and must not contain hyphens (-). " + + error; + + LOG.error(error, e); + throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage())); + } catch (Exception e) { String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name)); LOG.error(e.getMessage()); throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage())); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/QlExpressUtils.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/QlExpressUtils.java new file mode 100644 index 000000000..a840e91cb --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/QlExpressUtils.java @@ -0,0 +1,94 @@ +package com.yomahub.liteflow.util; + +import com.ql.util.express.ExpressRunner; +import com.yomahub.liteflow.builder.el.operator.*; +import com.yomahub.liteflow.common.ChainConstant; + +/** + * EL 工具类 + * + * @author tangkc + * @since 2.13.2 + */ +public class QlExpressUtils { + + /** + * EL解析引擎 + */ + private final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); + + 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()); + + } + + /** + * 获取QLExpress的实例 + */ + public static ExpressRunner getInstance() { + return EXPRESS_RUNNER; + } + + /** + * 检查变量名是否符合 变量命名规则 + * + * @param variableName 变量名 + * @return 如果符合规范返回 true,否则返回 false + */ + public static boolean checkVariableName(String variableName) { + if (variableName == null || variableName.isEmpty()) { + return false; + } + + // 首字符必须是合法的 Java 标识符起始字符 + if (!Character.isJavaIdentifierStart(variableName.charAt(0))) { + return false; + } + + // 后续字符必须是合法的 Java 标识符部分 + for (int i = 1; i < variableName.length(); i++) { + if (!Character.isJavaIdentifierPart(variableName.charAt(i))) { + return false; + } + } + + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/utils/QlExpressUtilsTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/utils/QlExpressUtilsTest.java new file mode 100644 index 000000000..047c1d4ec --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/utils/QlExpressUtilsTest.java @@ -0,0 +1,27 @@ +package com.yomahub.liteflow.test.utils; + +import cn.hutool.core.lang.Assert; +import com.yomahub.liteflow.util.QlExpressUtils; +import org.junit.jupiter.api.Test; + +/** + * EL 工具类测试 + * + * @author tangkc + * @since 2.13.2 + */ +public class QlExpressUtilsTest { + + @Test + public void checkVariableNameTest(){ + // 错误的 + Assert.isFalse(QlExpressUtils.checkVariableName("")); + Assert.isFalse(QlExpressUtils.checkVariableName("11a")); + Assert.isFalse(QlExpressUtils.checkVariableName("a-a")); + // 正确的 + Assert.isTrue(QlExpressUtils.checkVariableName("aa")); + Assert.isTrue(QlExpressUtils.checkVariableName("_aa")); + Assert.isTrue(QlExpressUtils.checkVariableName("$aa")); + Assert.isTrue(QlExpressUtils.checkVariableName("$a_a")); + } +}