All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yomahub.liteflow.flow.element.Node Maven / Gradle / Ivy

The newest version!
/**
 * 

Title: liteflow

*

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

* @author Bryan.Zhang * @email [email protected] * @Date 2020/4/1 */ package com.yomahub.liteflow.flow.element; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.ttl.TransmittableThreadLocal; import com.fasterxml.jackson.annotation.JsonIgnore; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.enums.ExecuteableTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.exception.ChainEndException; import com.yomahub.liteflow.exception.FlowSystemException; import com.yomahub.liteflow.flow.element.condition.LoopCondition; import com.yomahub.liteflow.flow.executor.NodeExecutor; import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.Slot; import com.yomahub.liteflow.util.TupleOf2; import java.util.Stack; import java.util.concurrent.locks.ReentrantLock; /** * Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例 * * @author Bryan.Zhang * @author luo yi */ public class Node implements Executable, Cloneable, Rollbackable{ private static final LFLog LOG = LFLoggerManager.getLogger(Node.class); private String id; private String name; private String clazz; private NodeTypeEnum type; private String script; private String language; // 增加该注解,避免在使用 Jackson 序列化检测循环引用时出现不必要异常 @JsonIgnore private NodeComponent instance; private String tag; private String cmpData; private String currChainId; // node 的 isAccess 结果,主要用于 WhenCondition 的提前 isAccess 判断,避免 isAccess 方法重复执行 private TransmittableThreadLocal accessResult = new TransmittableThreadLocal<>(); // 循环下标 private TransmittableThreadLocal>> loopIndexTL = new TransmittableThreadLocal<>(); // 迭代对象 private TransmittableThreadLocal>> loopObjectTL = new TransmittableThreadLocal<>(); // 当前slot的index private TransmittableThreadLocal slotIndexTL = new TransmittableThreadLocal<>(); // 是否结束整个流程,这个只对串行流程有效,并行流程无效 private TransmittableThreadLocal isEndTL = new TransmittableThreadLocal<>(); // isContinueOnError 结果 private TransmittableThreadLocal isContinueOnErrorResult = new TransmittableThreadLocal<>(); public Node() { } public Node(NodeComponent instance) { this.id = instance.getNodeId(); this.name = instance.getName(); this.instance = instance; this.type = instance.getType(); this.clazz = instance.getClass().getName(); } @Override public String getId() { return id; } @Override public void setId(String id) { this.id = id; } @Override public String getTag() { return tag; } @Override public void setTag(String tag) { this.tag = tag; } public String getName() { return name; } public void setName(String name) { this.name = name; } public NodeTypeEnum getType() { return type; } public void setType(NodeTypeEnum type) { this.type = type; } public NodeComponent getInstance() { return instance; } public void setInstance(NodeComponent instance) { this.instance = instance; } // node的执行主要逻辑 // 所有的可执行节点,其实最终都会落到node上来,因为chain中包含的也是node @Override public void execute(Integer slotIndex) throws Exception { if (ObjectUtil.isNull(instance)) { throw new FlowSystemException("there is no instance for node id " + id); } try { // 把线程属性赋值给组件对象 this.setSlotIndex(slotIndex); instance.setRefNode(this); // 判断是否可执行,所以isAccess经常作为一个组件进入的实际判断要素,用作检查slot里的参数的完备性 if (getAccessResult() || instance.isAccess()) { LOG.info("[O]start component[{}] execution", instance.getDisplayName()); // 这里开始进行重试的逻辑和主逻辑的运行 NodeExecutor nodeExecutor = NodeExecutorHelper.loadInstance() .buildNodeExecutor(instance.getNodeExecutorClass()); // 调用节点执行器进行执行 nodeExecutor.execute(instance); } else { LOG.info("[X]skip component[{}] execution", instance.getDisplayName()); } // 如果组件覆盖了isEnd方法,或者在在逻辑中主要调用了setEnd(true)的话,流程就会立马结束 if (instance.isEnd()) { String errorInfo = StrUtil.format("[{}] lead the chain to end", instance.getDisplayName()); throw new ChainEndException(errorInfo); } } catch (ChainEndException e) { throw e; } catch (Exception e) { // 如果组件覆盖了isEnd方法,或者在在逻辑中主要调用了setEnd(true)的话,流程就会立马结束 if (instance.isEnd()) { String errorInfo = StrUtil.format("[{}] lead the chain to end", instance.getDisplayName()); throw new ChainEndException(errorInfo); } // 如果组件覆盖了isContinueOnError方法,返回为true,那即便出了异常,也会继续流程 else if (getIsContinueOnErrorResult() || instance.isContinueOnError()) { String errorMsg = StrUtil.format("component[{}] cause error,but flow is still go on", id); LOG.error(errorMsg); } else { String errorMsg = StrUtil.format("component[{}] cause error,error:{}", id, e.getMessage()); LOG.error(errorMsg); throw e; } } finally { // 移除threadLocal里的信息 this.getInstance().removeRefNode(); removeSlotIndex(); removeIsEnd(); removeLoopIndex(); removeAccessResult(); removeIsContinueOnErrorResult(); } } // 回滚的主要逻辑 @Override public void rollback(Integer slotIndex) throws Exception { Slot slot = DataBus.getSlot(slotIndex); try { // 把线程属性赋值给组件对象 this.setSlotIndex(slotIndex); instance.setRefNode(this); instance.doRollback(); } catch (Exception e) { String errorMsg = StrUtil.format("component[{}] rollback error,error:{}", id, e.getMessage()); LOG.error(errorMsg); } finally { // 移除threadLocal里的信息 this.removeSlotIndex(); instance.removeRefNode(); } } // 在同步场景并不会单独执行这方法,同步场景会在execute里面去判断isAccess。 // 但是在异步场景的any=true情况下,如果isAccess返回了false,那么异步的any有可能会认为这个组件先执行完。就会导致不正常 // 增加这个方法是为了在异步的时候,先去过滤掉isAccess为false的异步组件。然后再异步执行。 // 详情见这个issue:https://gitee.com/dromara/liteFlow/issues/I4XRBA @Override public boolean isAccess(Integer slotIndex) throws Exception { // 把线程属性赋值给组件对象 this.setSlotIndex(slotIndex); instance.setRefNode(this); return instance.isAccess(); } @Override public ExecuteableTypeEnum getExecuteType() { return ExecuteableTypeEnum.NODE; } public String getScript() { return script; } public void setScript(String script) { this.script = script; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public String getCmpData() { return cmpData; } public void setCmpData(String cmpData) { this.cmpData = cmpData; } @Override public void setCurrChainId(String currentChainId) { this.currChainId = currentChainId; } public String getCurrChainId() { return currChainId; } public boolean getAccessResult() { Boolean result = this.accessResult.get(); return result != null && result; } public void setAccessResult(boolean accessResult) { this.accessResult.set(accessResult); } public void removeAccessResult() { this.accessResult.remove(); } public boolean getIsContinueOnErrorResult() { Boolean result = this.isContinueOnErrorResult.get(); return result != null && result; } public void setIsContinueOnErrorResult(boolean accessResult) { this.isContinueOnErrorResult.set(accessResult); } public void removeIsContinueOnErrorResult() { this.isContinueOnErrorResult.remove(); } // 这个锁用于异步循环场景 private ReentrantLock lock4LoopIndex = new ReentrantLock(); public void setLoopIndex(LoopCondition condition, int index) { try{ lock4LoopIndex.lock(); if (this.loopIndexTL.get() == null){ Stack> stack = new Stack<>(); TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), index); stack.push(tuple); this.loopIndexTL.set(stack); }else{ Stack> stack = this.loopIndexTL.get(); TupleOf2 thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null); if (thisConditionTuple != null){ thisConditionTuple.setB(index); }else{ TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), index); stack.push(tuple); } } }finally { lock4LoopIndex.unlock(); } } public Integer getLoopIndex() { Stack> stack = this.loopIndexTL.get(); if (stack != null){ return stack.peek().getB(); }else{ return null; } } public Integer getPreLoopIndex(){ return getPreNLoopIndex(1); } public Integer getPreNLoopIndex(int n){ Stack> stack = this.loopIndexTL.get(); if (stack != null && stack.size() > n){ return stack.elementAt(stack.size() - (n + 1)).getB(); }else{ return null; } } public void removeLoopIndex() { try{ lock4LoopIndex.lock(); Stack> stack = this.loopIndexTL.get(); if (stack != null){ if (stack.size() > 1){ stack.pop(); }else{ this.loopIndexTL.remove(); } } }finally { lock4LoopIndex.unlock(); } } // 这个锁用于异步循环场景 private ReentrantLock lock4LoopObj = new ReentrantLock(); public void setCurrLoopObject(LoopCondition condition, Object obj) { try{ lock4LoopObj.lock(); if (this.loopObjectTL.get() == null){ Stack> stack = new Stack<>(); TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), obj); stack.push(tuple); this.loopObjectTL.set(stack); }else{ Stack> stack = this.loopObjectTL.get(); TupleOf2 thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null); if (thisConditionTuple != null){ thisConditionTuple.setB(obj); }else{ TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), obj); stack.push(tuple); } } }finally { lock4LoopObj.unlock(); } } public T getCurrLoopObject() { Stack> stack = this.loopObjectTL.get(); if (stack != null){ return (T) stack.peek().getB(); }else{ return null; } } public T getPreLoopObject(){ return getPreNLoopObject(1); } public T getPreNLoopObject(int n){ Stack> stack = this.loopObjectTL.get(); if (stack != null && stack.size() > n){ return (T) stack.elementAt(stack.size() - (n + 1)).getB(); }else{ return null; } } public void removeCurrLoopObject() { try{ lock4LoopObj.lock(); Stack> stack = this.loopObjectTL.get(); if (stack != null){ if (stack.size() > 1){ stack.pop(); }else{ this.loopObjectTL.remove(); } } }finally { lock4LoopObj.unlock(); } } public Integer getSlotIndex(){ return this.slotIndexTL.get(); } public void setSlotIndex(Integer slotIndex){ this.slotIndexTL.set(slotIndex); } public void removeSlotIndex(){ this.slotIndexTL.remove(); } public Boolean getIsEnd(){ return this.isEndTL.get(); } public void setIsEnd(Boolean isEnd){ this.isEndTL.set(isEnd); } public void removeIsEnd(){ this.isEndTL.remove(); } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } @Override public T getItemResultMetaValue(Integer slotIndex) { return instance.getItemResultMetaValue(slotIndex); } @Override public Node clone() throws CloneNotSupportedException { Node node = (Node)super.clone(); node.loopIndexTL = new TransmittableThreadLocal<>(); node.loopObjectTL = new TransmittableThreadLocal<>(); node.accessResult = new TransmittableThreadLocal<>(); node.slotIndexTL = new TransmittableThreadLocal<>(); node.isEndTL = new TransmittableThreadLocal<>(); node.isContinueOnErrorResult = new TransmittableThreadLocal<>(); node.lock4LoopIndex = new ReentrantLock(); node.lock4LoopObj = new ReentrantLock(); return node; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy