diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java index 0293f4058..6966bef5a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java @@ -8,7 +8,6 @@ */ package com.yomahub.liteflow.core; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReUtil; @@ -31,8 +30,6 @@ import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.parser.LocalXmlFlowParser; import com.yomahub.liteflow.parser.XmlFlowParser; import com.yomahub.liteflow.parser.ZookeeperXmlFlowParser; - -import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -263,21 +260,16 @@ public class FlowExecutor { init(); } - /** - * callback by implicit subflow - * - * @param chainId - * @param param - * @param slotClazz - * @param slotIndex - * @param - * @throws Exception - */ + //隐式流程的调用方法 public void invoke(String chainId, Object param, Class slotClazz, Integer slotIndex) throws Exception { this.execute(chainId, param, slotClazz, slotIndex, true); } + public DefaultSlot execute(String chainId) throws Exception { + return this.execute(chainId, null, DefaultSlot.class, null, false); + } + public DefaultSlot execute(String chainId, Object param) throws Exception { return this.execute(chainId, param, DefaultSlot.class, null, false); } @@ -296,6 +288,10 @@ public class FlowExecutor { } } + public LiteflowResponse execute2Resp(String chainId) { + return this.execute2Resp(chainId, null, DefaultSlot.class); + } + public LiteflowResponse execute2Resp(String chainId, Object param) { return this.execute2Resp(chainId, param, DefaultSlot.class); } @@ -349,10 +345,14 @@ public class FlowExecutor { } if (!isInnerChain) { - slot.setRequestData(param); + if (ObjectUtil.isNotNull(param)){ + slot.setRequestData(param); + } slot.setChainName(chainId); } else { - slot.setChainReqData(chainId, param); + if (ObjectUtil.isNotNull(param)){ + slot.setChainReqData(chainId, param); + } } Chain chain = null; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java index f2f95132a..98e9328d4 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/data/AbsSlot.java @@ -7,6 +7,9 @@ */ package com.yomahub.liteflow.entity.data; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.exception.NullParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; @@ -27,7 +30,7 @@ public abstract class AbsSlot implements Slot { private static final String RESPONSE = "_response"; - private static final String CHAINNAME = "_chain_name"; + private static final String CHAIN_NAME = "_chain_name"; private static final String COND_NODE_PREFIX = "_cond_"; @@ -47,6 +50,14 @@ public abstract class AbsSlot implements Slot { protected ConcurrentHashMap dataMap = new ConcurrentHashMap(); + private void putDataMap(String key, T t) { + if (ObjectUtil.isNull(t)) { + //data slot is a ConcurrentHashMap, so null value will trigger NullPointerException + throw new NullParamException("data slot can't accept null param"); + } + dataMap.put(key, t); + } + public T getInput(String nodeId){ return (T)dataMap.get(NODE_INPUT_PREFIX + nodeId); } @@ -56,11 +67,11 @@ public abstract class AbsSlot implements Slot { } public void setInput(String nodeId,T t){ - dataMap.put(NODE_INPUT_PREFIX + nodeId, t); + putDataMap(NODE_INPUT_PREFIX + nodeId, t); } public void setOutput(String nodeId,T t){ - dataMap.put(NODE_OUTPUT_PREFIX + nodeId, t); + putDataMap(NODE_OUTPUT_PREFIX + nodeId, t); } public T getRequestData(){ @@ -68,7 +79,7 @@ public abstract class AbsSlot implements Slot { } public void setRequestData(T t){ - dataMap.put(REQUEST, t); + putDataMap(REQUEST, t); } public T getResponseData(){ @@ -76,7 +87,7 @@ public abstract class AbsSlot implements Slot { } public void setResponseData(T t){ - dataMap.put(RESPONSE, t); + putDataMap(RESPONSE, t); } public T getChainReqData(String chainId) { @@ -84,7 +95,7 @@ public abstract class AbsSlot implements Slot { } public void setChainReqData(String chainId, T t) { - dataMap.put(CHAIN_REQ_PREFIX + chainId, t); + putDataMap(CHAIN_REQ_PREFIX + chainId, t); } public boolean hasData(String key){ @@ -96,7 +107,7 @@ public abstract class AbsSlot implements Slot { } public void setData(String key, T t){ - dataMap.put(key, t); + putDataMap(key, t); } public void setPrivateDeliveryData(String nodeId, T t){ @@ -124,7 +135,7 @@ public abstract class AbsSlot implements Slot { } public void setCondResult(String key, T t){ - dataMap.put(COND_NODE_PREFIX + key, t); + putDataMap(COND_NODE_PREFIX + key, t); } public T getCondResult(String key){ @@ -132,11 +143,11 @@ public abstract class AbsSlot implements Slot { } public void setChainName(String chainName) { - dataMap.put(CHAINNAME, chainName); + putDataMap(CHAIN_NAME, chainName); } public String getChainName() { - return (String)dataMap.get(CHAINNAME); + return (String)dataMap.get(CHAIN_NAME); } public void addStep(CmpStep step){ @@ -159,7 +170,7 @@ public abstract class AbsSlot implements Slot { @Override public void generateRequestId() { - dataMap.put(REQUEST_ID, new Long(System.nanoTime()).toString()); + dataMap.put(REQUEST_ID, IdUtil.nanoId()); } @Override @@ -178,6 +189,6 @@ public abstract class AbsSlot implements Slot { @Override public void setException(Exception e) { - this.dataMap.put(EXCEPTION, e); + putDataMap(EXCEPTION, e); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NullParamException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NullParamException.java new file mode 100644 index 000000000..295555ac5 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NullParamException.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.exception; + +import java.io.Serializable; + +/** + * null param exception + * when param is null, dataMap (ConcurrentHashMap) cann't accept a null value + * + * @Author LeoLee + * @Date 2021/12/10 16:47 + * @Version 1.0 + */ +public class NullParamException extends RuntimeException implements Serializable { + + private static final long serialVersionUID = -864259139568071245L; + + private String message; + + public NullParamException(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java new file mode 100644 index 000000000..efb52759f --- /dev/null +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java @@ -0,0 +1,41 @@ +package com.yomahub.liteflow.test.nullParam; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.entity.data.DefaultSlot; +import com.yomahub.liteflow.entity.data.LiteflowResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * 单元测试:传递null param导致NPE的优化代码 + * + * @author LeoLee + * @since 2.6.6 + */ +@RunWith(SpringRunner.class) +@TestPropertySource(value = "classpath:/nullParam/application.properties") +@SpringBootTest(classes = NullParamTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.nullParam.cmp"}) +public class NullParamTest { + + @Autowired + private FlowExecutor flowExecutor; + + /** + * 支持无参的flow执行,以及param 为null时的异常抛出 + */ + @Test + public void testNullParam() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1"); + Assert.assertTrue(response.isSuccess()); + } + +} diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java new file mode 100644 index 000000000..3acf3c2c8 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java @@ -0,0 +1,22 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + System.out.println("get request data:" + this.getSlot().getRequestData()); + this.getSlot().setInput("BCmp", "param for BCmp"); + } +} diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java new file mode 100644 index 000000000..52bdc4633 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java @@ -0,0 +1,23 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + System.out.println("BCmp param:" + this.getSlot().getInput("BCmp")); + this.getSlot().setOutput("CCmp", "param for CCmp"); + } + +} diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java new file mode 100644 index 000000000..5707caf27 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java @@ -0,0 +1,22 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("c") +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + System.out.println("CCmp param:" + this.getSlot().getOutput("CCmp")); + } + +} diff --git a/liteflow-testcase-springboot/src/test/resources/nullParam/application.properties b/liteflow-testcase-springboot/src/test/resources/nullParam/application.properties new file mode 100644 index 000000000..7f320b595 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/resources/nullParam/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=nullParam/flow.xml diff --git a/liteflow-testcase-springboot/src/test/resources/nullParam/flow.xml b/liteflow-testcase-springboot/src/test/resources/nullParam/flow.xml new file mode 100644 index 000000000..eb30c8e40 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/resources/nullParam/flow.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java new file mode 100644 index 000000000..491d1a23d --- /dev/null +++ b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java @@ -0,0 +1,35 @@ +package com.yomahub.liteflow.test.nullParam; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.entity.data.DefaultSlot; +import com.yomahub.liteflow.entity.data.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; + +/** + * 单元测试:传递null param导致NPE的优化代码 + * @author LeoLee + * @since 2.6.6 + **/ +@RunWith(SpringRunner.class) +@ContextConfiguration("classpath:/nullParam/application-local.xml") +public class NullParamTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + /** + * 支持无参的flow执行,以及param 为null时的异常抛出 + */ + @Test + public void testNullParam() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1"); + Assert.assertTrue(response.isSuccess()); + } +} diff --git a/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java new file mode 100644 index 000000000..3acf3c2c8 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/ACmp.java @@ -0,0 +1,22 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + System.out.println("get request data:" + this.getSlot().getRequestData()); + this.getSlot().setInput("BCmp", "param for BCmp"); + } +} diff --git a/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java new file mode 100644 index 000000000..52bdc4633 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/BCmp.java @@ -0,0 +1,23 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + System.out.println("BCmp param:" + this.getSlot().getInput("BCmp")); + this.getSlot().setOutput("CCmp", "param for CCmp"); + } + +} diff --git a/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java new file mode 100644 index 000000000..5707caf27 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/cmp/CCmp.java @@ -0,0 +1,22 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.nullParam.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("c") +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + System.out.println("CCmp param:" + this.getSlot().getOutput("CCmp")); + } + +} diff --git a/liteflow-testcase-springnative/src/test/resources/nullParam/application-local.xml b/liteflow-testcase-springnative/src/test/resources/nullParam/application-local.xml new file mode 100644 index 000000000..36bf18e53 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/resources/nullParam/application-local.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/liteflow-testcase-springnative/src/test/resources/nullParam/flow.xml b/liteflow-testcase-springnative/src/test/resources/nullParam/flow.xml new file mode 100644 index 000000000..cd45c1a17 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/resources/nullParam/flow.xml @@ -0,0 +1,8 @@ + + + + + + + +