commit d86369b30ca68d397ef9bcad9f286c6fb7e1b4f3 Author: bryan.zhang Date: Mon Aug 7 14:41:55 2017 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e19921af --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip +*.war +*.del +*.pmd +.tern-project + + +# Logs and databases # +###################### +*.log +*.log.* +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + + +# Editor Files # +################ +*~ +*.swp + + +# Gradle Files # +################ +.gradle + + +# Build output directies +/target +*/target +/build +*/build + + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata +.myeclipse + + +# NetBeans specific files/directories +.nbattrs + +*.mymetadata +/logs +*/logs + +/payClear-timer/.tern-project diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..953a937d --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ + + + com.thebeastshop.liteflow + liteflow + jar + 4.0.0 + 1.0.0 + + + UTF-8 + 1.7 + 3.4 + 4.1 + 4.1.7.RELEASE + 1.7.21 + 1.2.17 + 1.7.5 + 1.7.13 + 1.2.7 + 1.0 + 1.6.1 + + + + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + + + org.apache.commons + commons-collections4 + 4.1 + + + org.springframework + spring-jdbc + ${spring.version} + + + log4j + log4j + ${log4j.version} + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + org.slf4j + slf4j-log4j12 + ${log4j-slf4j.version} + + + com.alibaba + fastjson + ${fastjson.version} + + + com.poolik + classfinder + ${classfinder.version} + + + dom4j + dom4j + ${dom4j.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + UTF-8 + ${java.version} + ${java.version} + + + + + \ No newline at end of file diff --git a/src/main/java/com/thebeastshop/liteflow/core/Component.java b/src/main/java/com/thebeastshop/liteflow/core/Component.java new file mode 100644 index 00000000..34d16aa4 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/core/Component.java @@ -0,0 +1,78 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.core; + +import org.apache.commons.lang3.time.StopWatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.thebeastshop.liteflow.entity.data.DataBus; +import com.thebeastshop.liteflow.entity.data.Slot; +import com.thebeastshop.liteflow.entity.monitor.CompStatistics; +import com.thebeastshop.liteflow.monitor.MonitorBus; + +public abstract class Component { + + private static final Logger LOG = LoggerFactory.getLogger(Component.class); + + private InheritableThreadLocal slotIndexTL = new InheritableThreadLocal(); + + private String nodeId; + + private boolean continueOnError; + + public void execute() throws Exception{ + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + long initm=Runtime.getRuntime().freeMemory(); + + process(); + stopWatch.stop(); + long timeSpent = stopWatch.getTime(); + long endm=Runtime.getRuntime().freeMemory(); + + this.getSlot().addStep(nodeId); + + //性能统计 + CompStatistics statistics = new CompStatistics(); + statistics.setComponentClazzName(this.getClass().getSimpleName()); + statistics.setTimeSpent(timeSpent); + statistics.setMemorySpent(initm-endm); + MonitorBus.addStatistics(statistics); + + LOG.debug("componnet[{}] finished in {} milliseconds",this.getClass().getSimpleName(),timeSpent); + } + + protected abstract void process() throws Exception; + + public boolean isContinueOnError() { + return continueOnError; + } + + public void setContinueOnError(boolean continueOnError) { + this.continueOnError = continueOnError; + } + + public Component setSlotIndex(Integer slotIndex) { + this.slotIndexTL.set(slotIndex); + return this; + } + + public Slot getSlot(){ + return DataBus.getSlot(this.slotIndexTL.get()); + } + + public String getNodeId() { + return nodeId; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java new file mode 100644 index 00000000..739cba7c --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java @@ -0,0 +1,136 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-31 + * @version 1.0 + */ +package com.thebeastshop.liteflow.core; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.thebeastshop.liteflow.entity.config.Chain; +import com.thebeastshop.liteflow.entity.config.Condition; +import com.thebeastshop.liteflow.entity.config.Node; +import com.thebeastshop.liteflow.entity.config.ThenCondition; +import com.thebeastshop.liteflow.entity.config.WhenCondition; +import com.thebeastshop.liteflow.entity.data.DataBus; +import com.thebeastshop.liteflow.flow.FlowBus; +import com.thebeastshop.liteflow.parser.FlowParser; + +public class FlowExecutor { + + private static final Logger LOG = LoggerFactory.getLogger(FlowExecutor.class); + + private List rulePath; + + public void init() { + for(String path : rulePath){ + try { + FlowParser.parseLocal(path); + } catch (Exception e) { + LOG.error("init flow executor cause error,cannot parse rule file{}", path, e); + } + } + } + + public void reloadRule(){ + init(); + } + + public T execute(String chainId,Object param){ + int slotIndex = -1; + try{ + Chain chain = FlowBus.getChain(chainId); + + if(chain == null){ + LOG.error("couldn't find chain with the id[{}]",chainId); + } + + slotIndex = DataBus.offerSlot(); + LOG.info("slot[{}] offered",slotIndex); + if(slotIndex == -1){ + throw new Exception("there is no available slot"); + } + + DataBus.getSlot(slotIndex).setRequestData(param); + + List conditionList = chain.getConditionList(); + + List nodeList = null; + Component component = null; + for(Condition condition : conditionList){ + nodeList = condition.getNodeList(); + + if(condition instanceof ThenCondition){ + for(Node node : nodeList){ + component = node.getInstance(); + try{ + component.setSlotIndex(slotIndex).execute(); + }catch(Throwable t){ + if(component.isContinueOnError()){ + LOG.error("component[{}] cause error,but flow is still go on",t,component.getClass().getSimpleName()); + }else{ + throw new Exception(t); + } + } + } + }else if(condition instanceof WhenCondition){ + final CountDownLatch latch = new CountDownLatch(nodeList.size()); + for(Node node : nodeList){ + new WhenConditionThread(node,slotIndex,latch).start(); + } + latch.await(15, TimeUnit.SECONDS); + } + } + DataBus.getSlot(slotIndex).printStep(); + return DataBus.getSlot(slotIndex).getResponseData(); + }catch(Exception e){ + LOG.error("executor cause error",e); + return null; + }finally{ + DataBus.releaseSlot(slotIndex); + } + } + + private class WhenConditionThread extends Thread{ + + private Node node; + + private Integer slotIndex; + + private CountDownLatch latch; + + public WhenConditionThread(Node node,Integer slotIndex,CountDownLatch latch){ + this.node = node; + this.slotIndex = slotIndex; + this.latch = latch; + } + + @Override + public void run() { + try{ + node.getInstance().setSlotIndex(slotIndex).execute(); + }catch(Exception e){ + LOG.error("component [{}] execute cause error",node.getClazz(),e); + }finally{ + latch.countDown(); + } + } + } + + public List getRulePath() { + return rulePath; + } + + public void setRulePath(List rulePath) { + this.rulePath = rulePath; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/config/Chain.java b/src/main/java/com/thebeastshop/liteflow/entity/config/Chain.java new file mode 100644 index 00000000..1c4f22a8 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/config/Chain.java @@ -0,0 +1,29 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.config; + +import java.util.List; + +public class Chain { + + private List conditionList; + + public Chain(List conditionList) { + this.conditionList = conditionList; + } + + public List getConditionList() { + return conditionList; + } + + public void setConditionList(List conditionList) { + this.conditionList = conditionList; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/config/Condition.java b/src/main/java/com/thebeastshop/liteflow/entity/config/Condition.java new file mode 100644 index 00000000..58e957e9 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/config/Condition.java @@ -0,0 +1,29 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.config; + +import java.util.List; + +public class Condition { + + private List nodeList; + + public Condition(List nodeList) { + this.nodeList = nodeList; + } + + public List getNodeList() { + return nodeList; + } + + public void setNodeList(List nodeList) { + this.nodeList = nodeList; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/config/Node.java b/src/main/java/com/thebeastshop/liteflow/entity/config/Node.java new file mode 100644 index 00000000..8e313c62 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/config/Node.java @@ -0,0 +1,45 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.config; + +import com.thebeastshop.liteflow.core.Component; + +public class Node { + + private String id; + + private String clazz; + + private Component instance; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getClazz() { + return clazz; + } + + public void setClazz(String clazz) { + this.clazz = clazz; + } + + public Component getInstance() { + return instance; + } + + public void setInstance(Component instance) { + this.instance = instance; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/config/ThenCondition.java b/src/main/java/com/thebeastshop/liteflow/entity/config/ThenCondition.java new file mode 100644 index 00000000..d7086201 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/config/ThenCondition.java @@ -0,0 +1,20 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.config; + +import java.util.List; + +public class ThenCondition extends Condition { + + public ThenCondition(List nodeList) { + super(nodeList); + } + +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/config/WhenCondition.java b/src/main/java/com/thebeastshop/liteflow/entity/config/WhenCondition.java new file mode 100644 index 00000000..137ea823 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/config/WhenCondition.java @@ -0,0 +1,20 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.config; + +import java.util.List; + +public class WhenCondition extends Condition{ + + public WhenCondition(List nodeList) { + super(nodeList); + } + +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/data/DataBus.java b/src/main/java/com/thebeastshop/liteflow/entity/data/DataBus.java new file mode 100644 index 00000000..dbbebe44 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/data/DataBus.java @@ -0,0 +1,50 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.data; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DataBus { + + private static final Logger LOG = LoggerFactory.getLogger(DataBus.class); + + public static final int SLOT_SIZE = 1024; + + public static AtomicInteger OCCUPY_COUNT = new AtomicInteger(0); + + private static Slot[] slots = new Slot[SLOT_SIZE]; + + public synchronized static int offerSlot(){ + for(int i = 0; i < slots.length; i++){ + if(slots[i] == null){ + slots[i] = new Slot(); + OCCUPY_COUNT.incrementAndGet(); + return i; + } + } + return -1; + } + + public static Slot getSlot(int slotIndex){ + return slots[slotIndex]; + } + + public static void releaseSlot(int slotIndex){ + if(slots[slotIndex] != null){ + slots[slotIndex] = null; + OCCUPY_COUNT.decrementAndGet(); + }else{ + LOG.warn("the slot[{}] has been released",slotIndex); + } + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/data/Slot.java b/src/main/java/com/thebeastshop/liteflow/entity/data/Slot.java new file mode 100644 index 00000000..f5715285 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/data/Slot.java @@ -0,0 +1,90 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-3 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.data; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("unchecked") +public class Slot { + + private static final Logger LOG = LoggerFactory.getLogger(Slot.class); + + private final String REQUEST = "request"; + + private final String RESPONSE = "response"; + + private final String NODE_INPUT_PREFIX = "input_"; + + private final String NODE_OUTPUT_PREFIX = "output_"; + + private List executeSteps = new ArrayList(); + + private ConcurrentHashMap dataMap = new ConcurrentHashMap(); + + public T getInput(String nodeId){ + return (T)dataMap.get(NODE_INPUT_PREFIX + nodeId); + } + + public T getOutput(String nodeId){ + return (T)dataMap.get(NODE_OUTPUT_PREFIX + nodeId); + } + + public void setInput(String nodeId,T t){ + dataMap.put(NODE_INPUT_PREFIX + nodeId, t); + } + + public void setOutput(String nodeId,T t){ + dataMap.put(NODE_OUTPUT_PREFIX + nodeId, t); + } + + public T getRequestData(){ + return (T)dataMap.get(REQUEST); + } + + public void setRequestData(T t){ + dataMap.put(REQUEST, t); + } + + public T getResponseData(){ + return (T)dataMap.get(RESPONSE); + } + + public void setResponseData(T t){ + dataMap.put(RESPONSE, t); + } + + public T getData(String key){ + return (T)dataMap.get(key); + } + + public void setData(String key, T t){ + dataMap.put(key, t); + } + + public void addStep(String nodeId){ + this.executeSteps.add(nodeId); + } + + public void printStep(){ + StringBuffer str = new StringBuffer(); + for(int i = 0; i < this.executeSteps.size(); i++){ + str.append(executeSteps.get(i)); + if(i < this.executeSteps.size()-1){ + str.append("==>"); + } + } + LOG.info(str.toString()); + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/entity/monitor/CompStatistics.java b/src/main/java/com/thebeastshop/liteflow/entity/monitor/CompStatistics.java new file mode 100644 index 00000000..62313a1e --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/entity/monitor/CompStatistics.java @@ -0,0 +1,43 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-4 + * @version 1.0 + */ +package com.thebeastshop.liteflow.entity.monitor; + +public class CompStatistics { + + private String componentClazzName; + + private long timeSpent; + + private long memorySpent; + + public String getComponentClazzName() { + return componentClazzName; + } + + public void setComponentClazzName(String componentClazzName) { + this.componentClazzName = componentClazzName; + } + + public long getTimeSpent() { + return timeSpent; + } + + public void setTimeSpent(long timeSpent) { + this.timeSpent = timeSpent; + } + + public long getMemorySpent() { + return memorySpent; + } + + public void setMemorySpent(long memorySpent) { + this.memorySpent = memorySpent; + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java b/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java new file mode 100644 index 00000000..72e14242 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java @@ -0,0 +1,34 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.flow; + +import java.util.HashMap; +import java.util.Map; + +import com.thebeastshop.liteflow.entity.config.Chain; + +public class FlowBus { + + private static Map chainMap; + + public static Chain getChain(String id) throws Exception{ + if(chainMap == null || chainMap.isEmpty()){ + throw new Exception("please config the rule first"); + } + return chainMap.get(id); + } + + public static void addChain(String name,Chain chain){ + if(chainMap == null){ + chainMap = new HashMap(); + } + chainMap.put(name, chain); + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/monitor/MonitorBus.java b/src/main/java/com/thebeastshop/liteflow/monitor/MonitorBus.java new file mode 100644 index 00000000..ae740cc1 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/monitor/MonitorBus.java @@ -0,0 +1,81 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-4 + * @version 1.0 + */ +package com.thebeastshop.liteflow.monitor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.Map; +import java.util.TimerTask; +import java.util.Map.Entry; +import java.util.Timer; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.thebeastshop.liteflow.entity.data.DataBus; +import com.thebeastshop.liteflow.entity.monitor.CompStatistics; +import com.thebeastshop.liteflow.util.LimitQueue; + +public class MonitorBus { + + private static final int QUEUE_LIMIT_SIZE = 200; + + private static ConcurrentHashMap> statisticsMap = new ConcurrentHashMap>(); + + static{ + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + public void run() { + MonitorBus.printStatistics(); + } + }, 30*1000L, 10*60*1000L); + } + + public static void addStatistics(CompStatistics statistics){ + if(statisticsMap.containsKey(statistics.getComponentClazzName())){ + statisticsMap.get(statistics.getComponentClazzName()).add(statistics); + }else{ + LimitQueue queue = new LimitQueue(QUEUE_LIMIT_SIZE); + queue.add(statistics); + statisticsMap.put(statistics.getComponentClazzName(), queue); + } + } + + public static void printStatistics(){ + Map compAverageTimeSpent = new HashMap(); + Map compAverageMemorySpent = new HashMap(); + + long totalTimeSpent = 0; + long totalMemorySpent = 0; + + for(Entry> entry : statisticsMap.entrySet()){ + for(CompStatistics statistics : entry.getValue()){ + totalTimeSpent += statistics.getTimeSpent(); + totalMemorySpent += statistics.getMemorySpent(); + } + compAverageTimeSpent.put(entry.getKey(), new BigDecimal(totalTimeSpent).divide(new BigDecimal(entry.getValue().size()), 2, RoundingMode.HALF_UP).longValue()); + compAverageMemorySpent.put(entry.getKey(), new BigDecimal(totalMemorySpent).divide(new BigDecimal(entry.getValue().size()), 2, RoundingMode.HALF_UP).longValue()); + } + System.out.println("======================================================================================"); + System.out.println("===================================SLOT INFO=========================================="); + System.out.println("SLOT TOTAL SIZE : "+DataBus.SLOT_SIZE); + System.out.println("SLOT OCCUPY COUNT : "+DataBus.OCCUPY_COUNT); + System.out.println("===============================TIME AVERAGE SPENT====================================="); + for(Entry entry : compAverageTimeSpent.entrySet()){ + System.out.println("COMPONENT["+entry.getKey()+"] AVERAGE TIME SPENT : " + entry.getValue()); + } + System.out.println("==============================MEMORY AVERAGE SPENT===================================="); + for(Entry entry : compAverageMemorySpent.entrySet()){ + System.out.println("COMPONENT["+entry.getKey()+"] AVERAGE MEMORY SPENT : "+ new BigDecimal(entry.getValue()).divide(new BigDecimal(1024), 2, RoundingMode.HALF_UP) + "K"); + } + System.out.println("======================================================================================"); + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java b/src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java new file mode 100644 index 00000000..32dedef9 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java @@ -0,0 +1,122 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-28 + * @version 1.0 + */ +package com.thebeastshop.liteflow.parser; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.dom4j.Document; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.poolik.classfinder.ClassFinder; +import com.poolik.classfinder.filter.Subclass; +import com.poolik.classfinder.info.ClassInfo; +import com.thebeastshop.liteflow.core.Component; +import com.thebeastshop.liteflow.entity.config.Chain; +import com.thebeastshop.liteflow.entity.config.Condition; +import com.thebeastshop.liteflow.entity.config.Node; +import com.thebeastshop.liteflow.entity.config.ThenCondition; +import com.thebeastshop.liteflow.entity.config.WhenCondition; +import com.thebeastshop.liteflow.flow.FlowBus; +import com.thebeastshop.liteflow.util.Dom4JReader; +import com.thebeastshop.liteflow.util.IOUtil; + +@SuppressWarnings("unchecked") +public class FlowParser { + + private static final Logger LOG = LoggerFactory.getLogger(FlowParser.class); + + private static final String ENCODING_FORMAT = "UTF-8"; + + public static void parseLocal(String rulePath) throws Exception{ + String ruleContent = IOUtil.read(rulePath, ENCODING_FORMAT); + parse(ruleContent); + } + + public static void parse(String content) throws Exception{ + Document document = Dom4JReader.getFormatDocument(content); + parse(document); + } + + public static void parse(Document document) throws Exception{ + Element rootElement = document.getRootElement(); + + //find所有的组件实现类并实例化 + Map compInstanceMap = new HashMap(); + ClassFinder finder = new ClassFinder().addClasspath(); + Collection classList = finder.findClasses(Subclass.of(Component.class)); + Component component = null; + for(ClassInfo classInfo : classList){ + LOG.info("component [{}] has been registered to flow parse manager",classInfo.getClassName()); + component = (Component)ClassUtils.getClass(classInfo.getClassName()).newInstance(); + compInstanceMap.put(classInfo.getClassName(), component); + } + + //解析node节点 + List nodeList = rootElement.element("nodes").elements("node"); + String id = null; + String clazz = null; + Node node = null; + Map nodeMap = new HashMap(); + for(Element e : nodeList){ + node = new Node(); + id = e.attributeValue("id"); + clazz = e.attributeValue("class"); + node.setId(id); + node.setClazz(clazz); + if(!compInstanceMap.containsKey(clazz)){ + LOG.error("couldn't find [{}] in registered component map",clazz); + } + component = compInstanceMap.get(clazz); + component.setNodeId(id); + node.setInstance(component); + nodeMap.put(id, node); + } + + //解析chain节点 + String chainName = null; + String condArrayStr = null; + String[] condArray = null; + List chainNodeList = null; + List conditionList = null; + + List chainList = rootElement.elements("chain"); + for(Element e : chainList){ + chainName = e.attributeValue("name"); + conditionList = new ArrayList(); + for (Iterator it = e.elementIterator(); it.hasNext();) { + Element condE = it.next(); + condArrayStr = condE.attributeValue("value"); + if(StringUtils.isBlank(condArrayStr)){ + continue; + } + chainNodeList = new ArrayList(); + condArray = condArrayStr.split(","); + for (int i = 0; i < condArray.length; i++) { + chainNodeList.add(nodeMap.get(condArray[i])); + } + if(condE.getName().equals("then")){ + conditionList.add(new ThenCondition(chainNodeList)); + }else if(condE.getName().equals("when")){ + conditionList.add(new WhenCondition(chainNodeList)); + } + } + FlowBus.addChain(chainName, new Chain(conditionList)); + } + } +} diff --git a/src/main/java/com/thebeastshop/liteflow/util/Dom4JReader.java b/src/main/java/com/thebeastshop/liteflow/util/Dom4JReader.java new file mode 100644 index 00000000..7c1f458a --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/util/Dom4JReader.java @@ -0,0 +1,117 @@ +package com.thebeastshop.liteflow.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.URL; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.io.SAXReader; +import org.xml.sax.InputSource; + +public class Dom4JReader { + + private static final String ENCODING_FORMAT = "UTF-8"; + + public static Document getDocument(String text) throws DocumentException { + return DocumentHelper.parseText(text); + } + + public static Document getFormatDocument(String text) throws DocumentException, UnsupportedEncodingException { + return getFormatDocument(text, ENCODING_FORMAT); + } + + public static Document getFormatDocument(String text, String charset) throws DocumentException, UnsupportedEncodingException { + String formatText = new String(text.getBytes("ISO-8859-1"), ENCODING_FORMAT); + + return getDocument(formatText); + } + + public static Document getDocument(File file) throws DocumentException, IOException { + InputStream inputStream = new FileInputStream(file); + + return getDocument(inputStream); + } + + public static Document getFormatDocument(File file) throws DocumentException, IOException, UnsupportedEncodingException { + return getFormatDocument(file, ENCODING_FORMAT); + } + + public static Document getFormatDocument(File file, String charset) throws DocumentException, IOException, UnsupportedEncodingException { + InputStream inputStream = new FileInputStream(file); + + return getFormatDocument(inputStream, charset); + } + + public static Document getDocument(InputSource inputSource) throws DocumentException { + SAXReader saxReader = new SAXReader(); + + return saxReader.read(inputSource); + } + + public static Document getFormatDocument(InputSource inputSource) throws DocumentException { + return getFormatDocument(inputSource, ENCODING_FORMAT); + } + + public static Document getFormatDocument(InputSource inputSource, String charset) throws DocumentException { + inputSource.setEncoding(charset); + + return getDocument(inputSource); + } + + public static Document getDocument(InputStream inputStream) throws DocumentException, IOException { + SAXReader saxReader = new SAXReader(); + + Document document = null; + try { + document = saxReader.read(inputStream); + } catch (DocumentException e) { + throw e; + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + + return document; + } + + public static Document getFormatDocument(InputStream inputStream) throws DocumentException, IOException, UnsupportedEncodingException { + return getFormatDocument(inputStream, ENCODING_FORMAT); + } + + public static Document getFormatDocument(InputStream inputStream, String charset) throws DocumentException, IOException, UnsupportedEncodingException { + Reader inputStreamReader = new InputStreamReader(inputStream, charset); + + return getDocument(inputStreamReader); + } + + public static Document getDocument(Reader reader) throws DocumentException, IOException { + SAXReader saxReader = new SAXReader(); + + Document document = null; + try { + document = saxReader.read(reader); + } catch (DocumentException e) { + throw e; + } finally { + if (reader != null) { + reader.close(); + } + } + + return document; + } + + public static Document getDocument(URL url) throws DocumentException { + SAXReader saxReader = new SAXReader(); + + return saxReader.read(url); + } +} \ No newline at end of file diff --git a/src/main/java/com/thebeastshop/liteflow/util/IOUtil.java b/src/main/java/com/thebeastshop/liteflow/util/IOUtil.java new file mode 100644 index 00000000..09c612b5 --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/util/IOUtil.java @@ -0,0 +1,29 @@ +package com.thebeastshop.liteflow.util; + +import java.io.FileInputStream; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; + +public class IOUtil { + public static String read(String path, String encoding) throws Exception { + String content = null; + + InputStream inputStream = null; + try { + // 从Resource路径获取 + inputStream = IOUtil.class.getClassLoader().getResourceAsStream(path); + if (inputStream == null) { + // 从文件路径获取 + inputStream = new FileInputStream(path); + } + content = IOUtils.toString(inputStream, encoding); + } finally { + if (inputStream != null) { + IOUtils.closeQuietly(inputStream); + } + } + + return content; + } +} \ No newline at end of file diff --git a/src/main/java/com/thebeastshop/liteflow/util/LimitQueue.java b/src/main/java/com/thebeastshop/liteflow/util/LimitQueue.java new file mode 100644 index 00000000..5122898a --- /dev/null +++ b/src/main/java/com/thebeastshop/liteflow/util/LimitQueue.java @@ -0,0 +1,151 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-4 + * @version 1.0 + */ +package com.thebeastshop.liteflow.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; + +public class LimitQueue implements Queue { + + /** + * 队列长度,实例化类的时候指定 + */ + private int limit; + + Queue queue = new LinkedList(); + + public LimitQueue(int limit) { + this.limit = limit; + } + + /** + * 入队 + */ + @Override + public boolean offer(E e) { + if (queue.size() >= limit) { + // 如果超出长度,入队时,先出队 + queue.poll(); + } + return queue.offer(e); + } + + /** + * 出队 + */ + @Override + public E poll() { + return queue.poll(); + } + + /** + * 获取队列 + * + * @return + * @author SHANHY + * @date 2015年11月9日 + */ + public Queue getQueue() { + return queue; + } + + /** + * 获取限制大小 + * + * @return + * @author SHANHY + * @date 2015年11月9日 + */ + public int getLimit() { + return limit; + } + + @Override + public boolean add(E e) { + return queue.add(e); + } + + @Override + public E element() { + return queue.element(); + } + + @Override + public E peek() { + return queue.peek(); + } + + @Override + public boolean isEmpty() { + return queue.size() == 0 ? true : false; + } + + @Override + public int size() { + return queue.size(); + } + + @Override + public E remove() { + return queue.remove(); + } + + @Override + public boolean addAll(Collection c) { + return queue.addAll(c); + } + + @Override + public void clear() { + queue.clear(); + } + + @Override + public boolean contains(Object o) { + return queue.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + return queue.containsAll(c); + } + + @Override + public Iterator iterator() { + return queue.iterator(); + } + + @Override + public boolean remove(Object o) { + return queue.remove(o); + } + + @Override + public boolean removeAll(Collection c) { + return queue.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return queue.retainAll(c); + } + + @Override + public Object[] toArray() { + return queue.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return queue.toArray(a); + } +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/TestMain.java b/src/test/java/com/thebeastshop/liteflow/test/TestMain.java new file mode 100644 index 00000000..3e7b8d51 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/TestMain.java @@ -0,0 +1,32 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-7-27 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test; + +import java.util.Arrays; + +import com.thebeastshop.liteflow.core.FlowExecutor; +import com.thebeastshop.liteflow.parser.FlowParser; + +public class TestMain { + public static void main(String[] args) throws Exception { + final FlowExecutor executor = new FlowExecutor(); + executor.setRulePath(Arrays.asList(new String[]{"flow.xml"})); + executor.init(); + + for(int i=0;i<200;i++){ + String response = executor.execute("chain2", "it's a request"); + System.out.println(response); + } + + + System.in.read(); + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/AComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/AComponent.java new file mode 100644 index 00000000..5ba32c65 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/AComponent.java @@ -0,0 +1,25 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; + +public class AComponent extends Component { + + @Override + public void process() { + String str = this.getSlot().getRequestData(); + System.out.println(str); + System.out.println("Acomponent executed!"); + + this.getSlot().setOutput(this.getNodeId(), "A component output"); + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/BComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/BComponent.java new file mode 100644 index 00000000..a2539420 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/BComponent.java @@ -0,0 +1,31 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import java.util.ArrayList; +import java.util.List; + +import com.thebeastshop.liteflow.core.Component; + +public class BComponent extends Component { + + @Override + public void process() { + try { + Thread.sleep(400L); + String[] temp = new String[1000]; + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Bcomponent executed!"); + + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/CComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/CComponent.java new file mode 100644 index 00000000..187c5c3f --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/CComponent.java @@ -0,0 +1,28 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; + +public class CComponent extends Component { + + @Override + public void process() { + try { + String[] temp = new String[4000]; + Thread.sleep(300L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Ccomponent executed!"); + + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/DComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/DComponent.java new file mode 100644 index 00000000..8ed3cdc5 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/DComponent.java @@ -0,0 +1,36 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; +import com.thebeastshop.liteflow.entity.data.Slot; + +public class DComponent extends Component { + + @Override + public void process() { + try { + Slot slot = this.getSlot(); + String e = slot.getOutput("e"); + if(e == null){ + System.out.println(slot); + } + System.out.println("D:" + slot.getOutput("e")); + + String[] temp = new String[1400]; + Thread.sleep(450L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Dcomponent executed!"); + + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/EComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/EComponent.java new file mode 100644 index 00000000..40d4105f --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/EComponent.java @@ -0,0 +1,29 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; + +public class EComponent extends Component { + + @Override + public void process() { + try { + Thread.sleep(120L); + System.out.println("E:" + this.getSlot().getOutput("a")); + this.getSlot().setOutput(this.getNodeId(), "E component output"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Eomponent executed!"); + + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/FComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/FComponent.java new file mode 100644 index 00000000..73f9655e --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/FComponent.java @@ -0,0 +1,28 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; + +public class FComponent extends Component { + + @Override + public void process() { + try { + String[] temp = new String[400]; + Thread.sleep(40L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Fcomponent executed!"); + + } + +} diff --git a/src/test/java/com/thebeastshop/liteflow/test/component/GComponent.java b/src/test/java/com/thebeastshop/liteflow/test/component/GComponent.java new file mode 100644 index 00000000..9b59ea37 --- /dev/null +++ b/src/test/java/com/thebeastshop/liteflow/test/component/GComponent.java @@ -0,0 +1,22 @@ +/** + *

Title: liteFlow

+ *

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

+ *

Copyright: Copyright (c) 2017

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2017-8-1 + * @version 1.0 + */ +package com.thebeastshop.liteflow.test.component; + +import com.thebeastshop.liteflow.core.Component; + +public class GComponent extends Component { + + @Override + public void process() { + System.out.println("Gcomponent executed!"); + this.getSlot().setResponseData("i am a response"); + } + +} diff --git a/src/test/resources/flow.xml b/src/test/resources/flow.xml new file mode 100644 index 00000000..900e855c --- /dev/null +++ b/src/test/resources/flow.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/log4j.xml b/src/test/resources/log4j.xml new file mode 100644 index 00000000..0783f0b6 --- /dev/null +++ b/src/test/resources/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file