bryan.zhang há 7 anos atrás
commit
d86369b30c
28 ficheiros alterados com 1526 adições e 0 exclusões
  1. 83 0
      .gitignore
  2. 86 0
      pom.xml
  3. 78 0
      src/main/java/com/thebeastshop/liteflow/core/Component.java
  4. 136 0
      src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java
  5. 29 0
      src/main/java/com/thebeastshop/liteflow/entity/config/Chain.java
  6. 29 0
      src/main/java/com/thebeastshop/liteflow/entity/config/Condition.java
  7. 45 0
      src/main/java/com/thebeastshop/liteflow/entity/config/Node.java
  8. 20 0
      src/main/java/com/thebeastshop/liteflow/entity/config/ThenCondition.java
  9. 20 0
      src/main/java/com/thebeastshop/liteflow/entity/config/WhenCondition.java
  10. 50 0
      src/main/java/com/thebeastshop/liteflow/entity/data/DataBus.java
  11. 90 0
      src/main/java/com/thebeastshop/liteflow/entity/data/Slot.java
  12. 43 0
      src/main/java/com/thebeastshop/liteflow/entity/monitor/CompStatistics.java
  13. 34 0
      src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java
  14. 81 0
      src/main/java/com/thebeastshop/liteflow/monitor/MonitorBus.java
  15. 122 0
      src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java
  16. 117 0
      src/main/java/com/thebeastshop/liteflow/util/Dom4JReader.java
  17. 29 0
      src/main/java/com/thebeastshop/liteflow/util/IOUtil.java
  18. 151 0
      src/main/java/com/thebeastshop/liteflow/util/LimitQueue.java
  19. 32 0
      src/test/java/com/thebeastshop/liteflow/test/TestMain.java
  20. 25 0
      src/test/java/com/thebeastshop/liteflow/test/component/AComponent.java
  21. 31 0
      src/test/java/com/thebeastshop/liteflow/test/component/BComponent.java
  22. 28 0
      src/test/java/com/thebeastshop/liteflow/test/component/CComponent.java
  23. 36 0
      src/test/java/com/thebeastshop/liteflow/test/component/DComponent.java
  24. 29 0
      src/test/java/com/thebeastshop/liteflow/test/component/EComponent.java
  25. 28 0
      src/test/java/com/thebeastshop/liteflow/test/component/FComponent.java
  26. 22 0
      src/test/java/com/thebeastshop/liteflow/test/component/GComponent.java
  27. 24 0
      src/test/resources/flow.xml
  28. 28 0
      src/test/resources/log4j.xml

+ 83 - 0
.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

+ 86 - 0
pom.xml

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<groupId>com.thebeastshop.liteflow</groupId>
+    <artifactId>liteflow</artifactId>
+    <packaging>jar</packaging>
+    <modelVersion>4.0.0</modelVersion>
+    <version>1.0.0</version>
+
+    <properties>
+    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<java.version>1.7</java.version>
+		<commons.lang3.version>3.4</commons.lang3.version>
+		<commons-collections.version>4.1</commons-collections.version>
+		<spring.version>4.1.7.RELEASE</spring.version>
+		<org.slf4j.version>1.7.21</org.slf4j.version>
+		<log4j.version>1.2.17</log4j.version>
+		<log4j-slf4j.version>1.7.5</log4j-slf4j.version>
+		<slf4j.version>1.7.13</slf4j.version>
+		<fastjson.version>1.2.7</fastjson.version>
+		<classfinder.version>1.0</classfinder.version>
+		<dom4j.version>1.6.1</dom4j.version>
+    </properties>
+
+	<dependencies>
+		<dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons.lang3.version}</version>
+        </dependency>
+        <dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-collections4</artifactId>
+			<version>4.1</version>
+		</dependency>
+        <dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-jdbc</artifactId>
+			<version>${spring.version}</version>
+		</dependency>
+        <dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>${org.slf4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<version>${log4j-slf4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>${fastjson.version}</version>
+		</dependency>
+		<dependency>
+		    <groupId>com.poolik</groupId>
+		    <artifactId>classfinder</artifactId>
+		    <version>${classfinder.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>dom4j</groupId>
+			<artifactId>dom4j</artifactId>
+			<version>${dom4j.version}</version>
+		</dependency>
+	</dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 78 - 0
src/main/java/com/thebeastshop/liteflow/core/Component.java

@@ -0,0 +1,78 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<Integer> slotIndexTL = new InheritableThreadLocal<Integer>();
+	
+	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;
+	}
+}

+ 136 - 0
src/main/java/com/thebeastshop/liteflow/core/FlowExecutor.java

@@ -0,0 +1,136 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<String> 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> 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<Condition> conditionList = chain.getConditionList();
+			
+			List<Node> 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<String> getRulePath() {
+		return rulePath;
+	}
+
+	public void setRulePath(List<String> rulePath) {
+		this.rulePath = rulePath;
+	}
+}

+ 29 - 0
src/main/java/com/thebeastshop/liteflow/entity/config/Chain.java

@@ -0,0 +1,29 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<Condition> conditionList;
+	
+	public Chain(List<Condition> conditionList) {
+		this.conditionList = conditionList;
+	}
+
+	public List<Condition> getConditionList() {
+		return conditionList;
+	}
+
+	public void setConditionList(List<Condition> conditionList) {
+		this.conditionList = conditionList;
+	}
+}

+ 29 - 0
src/main/java/com/thebeastshop/liteflow/entity/config/Condition.java

@@ -0,0 +1,29 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<Node> nodeList;
+	
+	public Condition(List<Node> nodeList) {
+		this.nodeList = nodeList;
+	}
+
+	public List<Node> getNodeList() {
+		return nodeList;
+	}
+
+	public void setNodeList(List<Node> nodeList) {
+		this.nodeList = nodeList;
+	}
+}

+ 45 - 0
src/main/java/com/thebeastshop/liteflow/entity/config/Node.java

@@ -0,0 +1,45 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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;
+	}
+}

+ 20 - 0
src/main/java/com/thebeastshop/liteflow/entity/config/ThenCondition.java

@@ -0,0 +1,20 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<Node> nodeList) {
+		super(nodeList);
+	}
+
+}

+ 20 - 0
src/main/java/com/thebeastshop/liteflow/entity/config/WhenCondition.java

@@ -0,0 +1,20 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<Node> nodeList) {
+		super(nodeList);
+	}
+
+}

+ 50 - 0
src/main/java/com/thebeastshop/liteflow/entity/data/DataBus.java

@@ -0,0 +1,50 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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);
+		}
+	}
+}

+ 90 - 0
src/main/java/com/thebeastshop/liteflow/entity/data/Slot.java

@@ -0,0 +1,90 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<String> executeSteps = new ArrayList<String>();
+	
+	private ConcurrentHashMap<String, Object> dataMap = new ConcurrentHashMap<String, Object>();
+	
+	public <T> T getInput(String nodeId){
+		return (T)dataMap.get(NODE_INPUT_PREFIX + nodeId);
+	}
+	
+	public <T> T getOutput(String nodeId){
+		return (T)dataMap.get(NODE_OUTPUT_PREFIX + nodeId);
+	}
+	
+	public <T> void setInput(String nodeId,T t){
+		dataMap.put(NODE_INPUT_PREFIX + nodeId, t);
+	}
+	
+	public <T> void setOutput(String nodeId,T t){
+		dataMap.put(NODE_OUTPUT_PREFIX + nodeId, t);
+	}
+	
+	public <T> T getRequestData(){
+		return (T)dataMap.get(REQUEST);
+	}
+	
+	public <T> void setRequestData(T t){
+		dataMap.put(REQUEST, t);
+	}
+	
+	public <T> T getResponseData(){
+		return (T)dataMap.get(RESPONSE);
+	}
+	
+	public <T> void setResponseData(T t){
+		dataMap.put(RESPONSE, t);
+	}
+	
+	public <T> T getData(String key){
+		return (T)dataMap.get(key);
+	}
+	
+	public <T> 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());
+	}
+}

+ 43 - 0
src/main/java/com/thebeastshop/liteflow/entity/monitor/CompStatistics.java

@@ -0,0 +1,43 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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;
+	}
+}

+ 34 - 0
src/main/java/com/thebeastshop/liteflow/flow/FlowBus.java

@@ -0,0 +1,34 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<String, Chain> 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<String, Chain>();
+		}
+		chainMap.put(name, chain);
+	}
+}

+ 81 - 0
src/main/java/com/thebeastshop/liteflow/monitor/MonitorBus.java

@@ -0,0 +1,81 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<String, LimitQueue<CompStatistics>> statisticsMap = new ConcurrentHashMap<String, LimitQueue<CompStatistics>>();
+
+	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<CompStatistics> queue = new LimitQueue<CompStatistics>(QUEUE_LIMIT_SIZE);
+			queue.add(statistics);
+			statisticsMap.put(statistics.getComponentClazzName(), queue);
+		}
+	}
+	
+	public static void printStatistics(){
+		Map<String, Long> compAverageTimeSpent = new HashMap<String, Long>();
+		Map<String, Long> compAverageMemorySpent = new HashMap<String, Long>();
+		
+		long totalTimeSpent = 0;
+		long totalMemorySpent = 0;
+		
+		for(Entry<String, LimitQueue<CompStatistics>> 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<String, Long> entry : compAverageTimeSpent.entrySet()){
+			System.out.println("COMPONENT["+entry.getKey()+"] AVERAGE TIME SPENT : " + entry.getValue());
+		}
+		System.out.println("==============================MEMORY AVERAGE SPENT====================================");
+		for(Entry<String, Long> 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("======================================================================================");
+	}
+}

+ 122 - 0
src/main/java/com/thebeastshop/liteflow/parser/FlowParser.java

@@ -0,0 +1,122 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<String, Component> compInstanceMap = new HashMap<String, Component>();
+        ClassFinder finder = new ClassFinder().addClasspath();
+        Collection<ClassInfo> 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<Element> nodeList = rootElement.element("nodes").elements("node");
+		String id = null;
+		String clazz = null;
+		Node node = null;
+		Map<String, Node> nodeMap = new HashMap<String, Node>();
+        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<Node> chainNodeList = null;
+        List<Condition> conditionList = null;
+        
+        List<Element> chainList = rootElement.elements("chain");
+        for(Element e : chainList){
+        	chainName = e.attributeValue("name");
+        	conditionList = new ArrayList<Condition>();
+        	for (Iterator<Element> it = e.elementIterator(); it.hasNext();) {
+				Element condE = it.next();
+				condArrayStr = condE.attributeValue("value");
+				if(StringUtils.isBlank(condArrayStr)){
+					continue;
+				}
+				chainNodeList = new ArrayList<Node>();
+				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));
+        }
+    }
+}

+ 117 - 0
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);
+    }
+}

+ 29 - 0
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;
+    }
+}

+ 151 - 0
src/main/java/com/thebeastshop/liteflow/util/LimitQueue.java

@@ -0,0 +1,151 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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<E> implements Queue<E> {
+
+	/**
+	 * 队列长度,实例化类的时候指定
+	 */
+	private int limit;
+
+	Queue<E> queue = new LinkedList<E>();
+
+	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<E> 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<? extends E> 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<E> 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> T[] toArray(T[] a) {
+		return queue.toArray(a);
+	}
+}

+ 32 - 0
src/test/java/com/thebeastshop/liteflow/test/TestMain.java

@@ -0,0 +1,32 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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();
+	}
+	
+}

+ 25 - 0
src/test/java/com/thebeastshop/liteflow/test/component/AComponent.java

@@ -0,0 +1,25 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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");
+	}
+	
+}

+ 31 - 0
src/test/java/com/thebeastshop/liteflow/test/component/BComponent.java

@@ -0,0 +1,31 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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!");
+		
+	}
+	
+}

+ 28 - 0
src/test/java/com/thebeastshop/liteflow/test/component/CComponent.java

@@ -0,0 +1,28 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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!");
+		
+	}
+	
+}

+ 36 - 0
src/test/java/com/thebeastshop/liteflow/test/component/DComponent.java

@@ -0,0 +1,36 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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!");
+		
+	}
+	
+}

+ 29 - 0
src/test/java/com/thebeastshop/liteflow/test/component/EComponent.java

@@ -0,0 +1,29 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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!");
+		
+	}
+	
+}

+ 28 - 0
src/test/java/com/thebeastshop/liteflow/test/component/FComponent.java

@@ -0,0 +1,28 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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!");
+		
+	}
+	
+}

+ 22 - 0
src/test/java/com/thebeastshop/liteflow/test/component/GComponent.java

@@ -0,0 +1,22 @@
+/**
+ * <p>Title: liteFlow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * <p>Copyright: Copyright (c) 2017</p>
+ * @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");
+	}
+	
+}

+ 24 - 0
src/test/resources/flow.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+	<nodes>
+		<node id="a" class="com.thebeastshop.liteflow.test.component.AComponent"/>
+		<node id="b" class="com.thebeastshop.liteflow.test.component.BComponent"/>
+		<node id="c" class="com.thebeastshop.liteflow.test.component.CComponent"/>
+		<node id="d" class="com.thebeastshop.liteflow.test.component.DComponent"/>
+		<node id="e" class="com.thebeastshop.liteflow.test.component.EComponent"/>
+		<node id="f" class="com.thebeastshop.liteflow.test.component.FComponent"/>
+		<node id="g" class="com.thebeastshop.liteflow.test.component.GComponent"/>
+	</nodes>
+	
+	<chain name="chain1">
+		<then value="a,c"/>
+		<when value="b,d"/>
+		<then value="e,f,g"/>
+	</chain>
+	
+	<chain name="chain2">
+		<then value="a,c,g"/>
+		<when value="b,e"/>
+		<then value="d,f"/>
+	</chain>
+</flow>

+ 28 - 0
src/test/resources/log4j.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration>
+    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%p] %m  >> %c:%L%n"/>
+        </layout>
+    </appender>
+    <appender name="fileout" class="org.apache.log4j.DailyRollingFileAppender">
+        <param name="File" value="./logs/commdata.log"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} [%p] %m  >> %c:%L%n"/>
+        </layout>
+    </appender>
+    <appender name="asyncStdout" class="org.apache.log4j.AsyncAppender">
+    	<param name="BufferSize" value="8192"/>
+        <appender-ref ref="stdout"/>
+    </appender>
+    <appender name="asyncFileout" class="org.apache.log4j.AsyncAppender">
+    	<param name="BufferSize" value="8192"/>
+        <appender-ref ref="fileout"/>
+    </appender>
+    <root>
+        <priority value="info" />
+        <appender-ref ref="asyncStdout"/>
+        <appender-ref ref="asyncFileout"/>
+    </root>
+</log4j:configuration>