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..5acda6a36 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 @@ -278,6 +278,17 @@ public class FlowExecutor { this.execute(chainId, param, slotClazz, slotIndex, true); } + /** + * 无参执行器 + * @Author LeoLee + * @Date 17:52 2021/12/10 + * @param chainId 业务链id + * @return com.yomahub.liteflow.entity.data.DefaultSlot + */ + public DefaultSlot execute(String chainId) throws Exception { + return this.execute(chainId, DefaultSlot.class, null, false); + } + public DefaultSlot execute(String chainId, Object param) throws Exception { return this.execute(chainId, param, DefaultSlot.class, null, false); } @@ -286,8 +297,39 @@ public class FlowExecutor { return this.execute(chainId, param, slotClazz, null, false); } + /** + * 有param的execute方法,将会对param进行判断,若为null,抛出逻辑异常NullParamException + * @Author: LeoLee + * @Date: 2021/12/11 15:34 + */ public T execute(String chainId, Object param, Class slotClazz, Integer slotIndex, boolean isInnerChain) throws Exception { + if (ObjectUtil.isNull(param)) { + //data slot is a ConcurrentHashMap, so null value will trigger NullPointerException + throw new NullParamException("data slot can't accept null param"); + } + return this.execute0(chainId, param, slotClazz, slotIndex, isInnerChain); + } + + /** + * 无param的execute方法,没有param参数 + * 调用execute0时,param默认给null,之后会在doExecute方法中进行判断,如果param为null,则不进行slot数据槽设置 + * @Author: LeoLee + * @Date: 2021/12/11 15:35 + */ + public T execute(String chainId, Class slotClazz, + Integer slotIndex, boolean isInnerChain) throws Exception { + //默认param为null,在doExecute中会被过滤 + return this.execute0(chainId, null, slotClazz, slotIndex, isInnerChain); + } + + /** + * doExecute私有封装 + * 为了区别无param和有param的执行方法 + * @Author LeoLee + * @Date 17:53 2021/12/10 + */ + private T execute0(String chainId, Object param, Class slotClazz, Integer slotIndex, boolean isInnerChain) throws Exception { T slot = this.doExecute(chainId, param, slotClazz, slotIndex, isInnerChain); if (ObjectUtil.isNotNull(slot.getException())) { throw slot.getException(); @@ -296,6 +338,17 @@ public class FlowExecutor { } } + /** + * 无param执行器 + * @Param: [chainId] 业务链id + * @Return: com.yomahub.liteflow.entity.data.LiteflowResponse + * @Author: LeoLee + * @Date: 2021/12/11 15:54 + */ + public LiteflowResponse execute2Resp(String chainId) { + return this.execute2Resp(chainId, DefaultSlot.class, null, false); + } + public LiteflowResponse execute2Resp(String chainId, Object param) { return this.execute2Resp(chainId, param, DefaultSlot.class); } @@ -306,8 +359,39 @@ public class FlowExecutor { private final ArrayList> notFailExceptionList = ListUtil.toList(ChainEndException.class); + /** + * 带param参数的execute2Resp方法,会对param进行判断 + * 如果param为null,则抛出NullParamException + * @Author: LeoLee + * @Date: 2021/12/11 15:55 + */ public LiteflowResponse execute2Resp(String chainId, Object param, Class slotClazz, Integer slotIndex, boolean isInnerChain) { + if (ObjectUtil.isNull(param)) { + //data slot is a ConcurrentHashMap, so null value will trigger NullPointerException + throw new NullParamException("data slot can't accept null param"); + } + return execute2Resp0(chainId, param, slotClazz, slotIndex, isInnerChain); + } + + /** + * 无param参数的execute2Resp方法 + * 调用doExecute方法时,param默认传递null,会在doExecute房中进行 + * @Author: LeoLee + * @Date: 2021/12/11 16:10 + */ + public LiteflowResponse execute2Resp(String chainId, Class slotClazz, Integer slotIndex, + boolean isInnerChain) { + //默认param为null,在doExecute中会被过滤 + return execute2Resp0(chainId, null, slotClazz, slotIndex, isInnerChain); + } + + /** + * doExecute私有封装 + * @Author LeoLee + * @Date 17:54 2021/12/10 + */ + private LiteflowResponse execute2Resp0(String chainId, Object param, Class slotClazz, Integer slotIndex, boolean isInnerChain) { LiteflowResponse response = new LiteflowResponse<>(); T slot = doExecute(chainId, param, slotClazz, slotIndex, isInnerChain); @@ -349,10 +433,16 @@ public class FlowExecutor { } if (!isInnerChain) { - slot.setRequestData(param); + //对param进行判空,如果为null,则不进行slot设置 + if (ObjectUtil.isNotNull(param)) { + slot.setRequestData(param); + } slot.setChainName(chainId); } else { - slot.setChainReqData(chainId, param); + //对param进行判空,如果为null,则不进行slot设置 + 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..f6f24706d 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,8 @@ */ package com.yomahub.liteflow.entity.data; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.exception.NullParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; @@ -47,6 +49,14 @@ public abstract class AbsSlot implements Slot { protected ConcurrentHashMap dataMap = new ConcurrentHashMap(); + private void dataMapPut(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 +66,11 @@ public abstract class AbsSlot implements Slot { } public void setInput(String nodeId,T t){ - dataMap.put(NODE_INPUT_PREFIX + nodeId, t); + dataMapPut(NODE_INPUT_PREFIX + nodeId, t); } public void setOutput(String nodeId,T t){ - dataMap.put(NODE_OUTPUT_PREFIX + nodeId, t); + dataMapPut(NODE_OUTPUT_PREFIX + nodeId, t); } public T getRequestData(){ @@ -68,7 +78,7 @@ public abstract class AbsSlot implements Slot { } public void setRequestData(T t){ - dataMap.put(REQUEST, t); + dataMapPut(REQUEST, t); } public T getResponseData(){ @@ -76,7 +86,7 @@ public abstract class AbsSlot implements Slot { } public void setResponseData(T t){ - dataMap.put(RESPONSE, t); + dataMapPut(RESPONSE, t); } public T getChainReqData(String chainId) { @@ -84,7 +94,7 @@ public abstract class AbsSlot implements Slot { } public void setChainReqData(String chainId, T t) { - dataMap.put(CHAIN_REQ_PREFIX + chainId, t); + dataMapPut(CHAIN_REQ_PREFIX + chainId, t); } public boolean hasData(String key){ @@ -96,7 +106,7 @@ public abstract class AbsSlot implements Slot { } public void setData(String key, T t){ - dataMap.put(key, t); + dataMapPut(key, t); } public void setPrivateDeliveryData(String nodeId, T t){ @@ -124,7 +134,7 @@ public abstract class AbsSlot implements Slot { } public void setCondResult(String key, T t){ - dataMap.put(COND_NODE_PREFIX + key, t); + dataMapPut(COND_NODE_PREFIX + key, t); } public T getCondResult(String key){ @@ -132,7 +142,7 @@ public abstract class AbsSlot implements Slot { } public void setChainName(String chainName) { - dataMap.put(CHAINNAME, chainName); + dataMapPut(CHAINNAME, chainName); } public String getChainName() { @@ -178,6 +188,6 @@ public abstract class AbsSlot implements Slot { @Override public void setException(Exception e) { - this.dataMap.put(EXCEPTION, e); + dataMapPut(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..26bc2d2f8 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java @@ -0,0 +1,43 @@ +package com.yomahub.liteflow.test.nullParam; + +import com.yomahub.liteflow.core.FlowExecutor; +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 + * @Date 2021/12/9 16:58 + * @Version 1.0 + */ +@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时的异常抛出 + * @Author LeoLee + * @Date 17:25 2021/12/9 + */ + @Test + public void testNullParam() throws Exception { + //flowExecutor.execute("chain1", null);//NullParamException: data slot can't accept null param + flowExecutor.execute("chain1"); + //flowExecutor.execute2Resp("chain1", null);//NullParamException: data slot can't accept null param + flowExecutor.execute2Resp("chain1"); + } + +} 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..e98eccac3 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/resources/nullParam/application.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=nullParam/flow.xml +liteflow.print-banner=true 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..cd45c1a17 --- /dev/null +++ b/liteflow-testcase-springboot/src/test/resources/nullParam/flow.xml @@ -0,0 +1,8 @@ + + + + + + + + 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..709544449 --- /dev/null +++ b/liteflow-testcase-springnative/src/test/java/com/yomahub/liteflow/test/nullParam/NullParamTest.java @@ -0,0 +1,38 @@ +package com.yomahub.liteflow.test.nullParam; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.test.BaseTest; +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 + * @Date 2021/12/11 + * @Version V1.0 + **/ +@RunWith(SpringRunner.class) +@ContextConfiguration("classpath:/nullParam/application-local.xml") +public class NullParamTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + /** + * 支持无参的flow执行,以及param 为null时的异常抛出 + * @Author: LeoLee + * @Date: 2021/12/11 21:38 + */ + @Test + public void testNullParam() throws Exception { + //flowExecutor.execute("chain1", null);//NullParamException: data slot can't accept null param + flowExecutor.execute("chain1"); + //flowExecutor.execute2Resp("chain1", null);//NullParamException: data slot can't accept null param + flowExecutor.execute2Resp("chain1"); + } +} 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 @@ + + + + + + + +