├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── cuner │ │ └── flowframework │ │ ├── config │ │ ├── XmlParser.java │ │ └── definition │ │ │ ├── Definition.java │ │ │ ├── FlowDefinition.java │ │ │ ├── InvalidConfigException.java │ │ │ ├── StepDefinition.java │ │ │ └── transition │ │ │ ├── ConditionTransitionDefinition.java │ │ │ ├── OrderTransitionDefinition.java │ │ │ ├── TransitionDefinition.java │ │ │ └── condition │ │ │ ├── AndConditionDefinition.java │ │ │ ├── ConditionDefinition.java │ │ │ ├── DefaultConditionDefinition.java │ │ │ ├── GroovyExpressConditionDefinition.java │ │ │ └── OrConditionDefinition.java │ │ ├── core │ │ ├── Action.java │ │ ├── Flow.java │ │ ├── FlowContext.java │ │ ├── Step.java │ │ ├── manager │ │ │ └── FlowManager.java │ │ └── transition │ │ │ ├── ConditionTransition.java │ │ │ ├── OrderTransition.java │ │ │ ├── Transition.java │ │ │ └── condition │ │ │ ├── AndCondition.java │ │ │ ├── Condition.java │ │ │ ├── DefaultCondition.java │ │ │ ├── GroovyExpressCondition.java │ │ │ └── OrCondition.java │ │ └── support │ │ ├── Comparator.java │ │ ├── ThreadLocalDateFormat.java │ │ └── log │ │ ├── Execution.java │ │ └── ExecutionType.java └── resources │ └── flow-component.xsd └── test ├── java └── org │ └── cuner │ └── flowframework │ └── test │ ├── FlowManagerTest.java │ ├── action │ ├── Action1.java │ ├── Action2.java │ └── Action3.java │ └── domain │ ├── Data.java │ └── Result.java └── resources ├── flow.xml └── spring.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 基于xml配置的流程编排框架,将整个流程以xml配置文件的形式管理起来:定义流程、管理上下文、控制流程的流转(按条件或者顺序),各个流程节点的执行(同步或者异步)。同时对每个流程节点进行监控以及统计,完成对流程的整体把控。 3 | 4 | # 基本概念 5 | 6 | - flow:数据流程对象 7 | - step:流程的子步骤,也叫流程节点 8 | - action:流程中各个步骤执行的操作 9 | - subflow:子流程,一个流程节点可以执行操作;也可以执行某个子流程 10 | 11 | # 用法 12 | 13 | ## 1.定义流程的输入与输出 14 | 15 | - 定义输入为Data 16 | - 定义输出为Result 17 | 18 | ## 2.定义步骤(子节点) 19 | 20 | 所有的子节点需要实现接口org.cuner.flowframework.core.Action 21 | 22 | ``` 23 | public class Action1 implements Action { 24 | @Override 25 | public void execute(FlowContext context) { 26 | if (context.getResult() == null) { 27 | context.setResult(new Result()); 28 | } 29 | context.setParameter("param", 1); 30 | context.getResult().setResult(context.getData().getData()); 31 | System.out.println("----------flow: " + context.getFlowName() + " step: " + context.getStepName() + " action: " + this.getClass().getSimpleName() + "----------"); 32 | } 33 | } 34 | ``` 35 | 36 | 同时保证启动后,将所有action注入到spring容器中 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | ## 3.通过xml配置定义流程 45 | 46 | 详细的配置结构请查看xsd文件 47 | - `flows`标签为root标签,可包含多个`flow`标签 48 | - `flow`标签标识一个流程,可以是一个主流程也可以是一个子流程,拥有name属性代表流程名称(流程名字要求全局唯一),可包含多个`step`标签 49 | - `step`标签标识一个步骤(流程节点) 50 | - 包含零或一个`condition类`标签,标识该步骤的执行条件,若没有则默认执行 51 | - 包含零或多个`conditionTransition`标签,标识状态的流转,若没有则默认流转到配置文件中下一个步骤 52 | - name属性:标识步骤名称 53 | - asyn属性:标识是否异步执行 54 | - action属性:标识步骤执行的操作,关联Action接口某个实现类的bean id 55 | - subflow属性:标识步骤所代表的子流程(action属性和subflow属性只能存在一者) 56 | - `condition类标签` 57 | - `condition`标签: 58 | - 属性key:为输入对象Data的某个属性 59 | - value:与输入对象Data的某个属性值相比较的值 60 | - comparator:关系运算符(eq、ne、gt、lt、ge、le、ln) 61 | - `expCondition`标签,拥有express属性,值为判断条件的表达式,如 data == "test",其中data为输入对象Data的一个属性 62 | - `andCondtion`标签,包含多个condition类标签,对多个condition的结果做与操作 63 | - `orCondtion`标签,包含多个condition类标签,对多个condition的结果做或操作 64 | - `conditionTransition`标签 65 | - 包含一个`condition`标签,判断是否符合状态流程的条件 66 | - 包含一个to属性:标识状态流转的下一个节点 67 | 68 | ``` 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | ``` 106 | 107 | ## 4.流程注入 108 | 109 | 配置文件需要放在classpath下,案例中的配置文件就是在classpath下的flow.xml文件 110 | 111 | ``` 112 | 113 | 114 | 115 | flow.xml 116 | 117 | 118 | 119 | ``` 120 | 121 | ## 5.执行 122 | 123 | ``` 124 | Result result = flowManager.execute("mainFlow", data); 125 | ``` 126 | 由于各个步骤可异步执行,在主流程的执行中加入了闭锁,只有当所有步骤(包括异步的)执行完成,execute方法才能正确返回。 127 | 128 | ## 6.查看日志 129 | 可查看流程执行的完整堆栈,可以在logback.xml中如下配置 130 | ``` 131 | 132 | 133 | 134 | ``` 135 | 136 | 上述案例执行后日志如下 137 | ``` 138 | 21:03:23.431 [main] INFO flow-record - 2018-08-04 21:03:23,415 139 | [flow:mainFlow | start:2018-08-04 21:03:23,353 | end:2018-08-04 21:03:23,380 | cost:27] 140 | |----[step:step1 | start:2018-08-04 21:03:23,354 | end:2018-08-04 21:03:23,355 | cost:1] 141 | |----(step:step2 | start:2018-08-04 21:03:23,358 | end:2018-08-04 21:03:23,359 | cost:1) (asynchronously) 142 | |----[step:step3 | start:2018-08-04 21:03:23,359 | end:2018-08-04 21:03:23,360 | cost:1] 143 | |----[sub_flow:subflow | start:2018-08-04 21:03:23,359 | end:2018-08-04 21:03:23,360 | cost:1] 144 | |----(step:sub_step1 | start:2018-08-04 21:03:23,360 | end:2018-08-04 21:03:23,360 | cost:0) (asynchronously) 145 | |----[step:sub_step2 | start:2018-08-04 21:03:23,360 | end:2018-08-04 21:03:23,360 | cost:0] 146 | |----(step:step4 | start:2018-08-04 21:03:23,361 | end:2018-08-04 21:03:23,361 | cost:0) (asynchronously) 147 | |----[sub_flow:subflow | start:2018-08-04 21:03:23,361 | end:2018-08-04 21:03:23,361 | cost:0] 148 | |----[step:sub_step2 | start:2018-08-04 21:03:23,361 | end:2018-08-04 21:03:23,361 | cost:0] 149 | |----(step:sub_step1 | start:2018-08-04 21:03:23,361 | end:2018-08-04 21:03:23,361 | cost:0) (asynchronously) 150 | |----[step:step5 | start:2018-08-04 21:03:23,378 | end:2018-08-04 21:03:23,380 | cost:2] 151 | |----[step:step6 | start:2018-08-04 21:03:23,380 | end:2018-08-04 21:03:23,380 | cost:0] 152 | |----[step:step7 | start:2018-08-04 21:03:23,380 | end:2018-08-04 21:03:23,380 | cost:0] 153 | |----[step:step8 | start:2018-08-04 21:03:23,380 | end:2018-08-04 21:03:23,380 | cost:0] 154 | |----[step:step9 | start:2018-08-04 21:03:23,380 | end:2018-08-04 21:03:23,380 | cost:0] 155 | |----[step:step10 | start:2018-08-04 21:03:23,380 | end:2018-08-04 21:03:23,380 | cost:0] 156 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | flow-framework 8 | org.cuner.flowframework 9 | 1.0.0 10 | 11 | 12 | 13 | 14 | org.springframework 15 | spring-core 16 | 4.3.5.RELEASE 17 | 18 | 19 | org.springframework 20 | spring-context 21 | 4.3.5.RELEASE 22 | 23 | 24 | org.springframework 25 | spring-test 26 | 4.3.5.RELEASE 27 | 28 | 29 | org.springframework 30 | spring-beans 31 | 4.3.5.RELEASE 32 | 33 | 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | 1.6.2 40 | 41 | 42 | ch.qos.logback 43 | logback-core 44 | 1.1.2 45 | 46 | 47 | ch.qos.logback 48 | logback-classic 49 | 1.1.2 50 | 51 | 52 | 53 | org.codehaus.groovy 54 | groovy-all 55 | 2.4.6 56 | 57 | 58 | junit 59 | junit 60 | 4.12 61 | test 62 | 63 | 64 | 65 | dom4j 66 | dom4j 67 | 1.1 68 | 69 | 70 | 71 | commons-collections 72 | commons-collections 73 | 3.2.1 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/XmlParser.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config; 2 | 3 | import org.cuner.flowframework.config.definition.FlowDefinition; 4 | import org.cuner.flowframework.config.definition.InvalidConfigException; 5 | import org.cuner.flowframework.config.definition.StepDefinition; 6 | import org.cuner.flowframework.config.definition.transition.ConditionTransitionDefinition; 7 | import org.cuner.flowframework.config.definition.transition.OrderTransitionDefinition; 8 | import org.cuner.flowframework.config.definition.transition.TransitionDefinition; 9 | import org.cuner.flowframework.config.definition.transition.condition.*; 10 | import org.cuner.flowframework.core.Action; 11 | import org.cuner.flowframework.core.Flow; 12 | import org.dom4j.Attribute; 13 | import org.dom4j.Document; 14 | import org.dom4j.DocumentException; 15 | import org.dom4j.Element; 16 | import org.dom4j.io.SAXReader; 17 | import org.springframework.context.ApplicationContext; 18 | import org.springframework.util.StringUtils; 19 | 20 | import java.io.InputStream; 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | /** 27 | * Created by houan on 18/7/16. 28 | */ 29 | public class XmlParser { 30 | 31 | public static Map parse(List flowFiles, ApplicationContext applicationContext) throws InvalidConfigException, DocumentException { 32 | Map flowMap = new HashMap<>(); 33 | List flowDefinitionList = new ArrayList<>(); 34 | for (String file : flowFiles) { 35 | flowDefinitionList.addAll(parseFile(file)); 36 | } 37 | 38 | for (FlowDefinition flowDefinition : flowDefinitionList) { 39 | if (flowMap.containsKey(flowDefinition.getName())) { 40 | throw new InvalidConfigException("duplicate flow definition,name:" + flowDefinition.getName()); 41 | } 42 | Flow flow = flowDefinition.getInstance(); 43 | flowMap.put(flow.getName(), flow); 44 | } 45 | 46 | //填充step中的action和subflow 47 | for (FlowDefinition flowDefinition : flowDefinitionList) { 48 | for (StepDefinition stepDefinition : flowDefinition.getStepDefinitionList()) { 49 | if (!StringUtils.isEmpty(stepDefinition.getAction())) { 50 | stepDefinition.getInstance().setAction((Action) applicationContext.getBean(stepDefinition.getAction())); 51 | } 52 | if (!StringUtils.isEmpty(stepDefinition.getSubflow())) { 53 | stepDefinition.getInstance().setSubflow(flowMap.get(stepDefinition.getSubflow())); 54 | } 55 | } 56 | } 57 | 58 | return flowMap; 59 | } 60 | 61 | private static List parseFile(String file) throws DocumentException { 62 | List flowDefinitionList = new ArrayList<>(); 63 | // get document 64 | SAXReader saxReader = new SAXReader(); 65 | InputStream inputStream = ClassLoader.getSystemResourceAsStream(file); 66 | Document doc = saxReader.read(inputStream); 67 | List flowElements = doc.getRootElement().elements(); 68 | 69 | for (Object o : flowElements) { 70 | Element element = (Element) o; 71 | if (element.getName().equalsIgnoreCase("flow")) { 72 | flowDefinitionList.add(parseFlow(element)); 73 | } 74 | } 75 | 76 | return flowDefinitionList; 77 | } 78 | 79 | private static FlowDefinition parseFlow(Element element) { 80 | String flowName = getNodeAttribute(element, "name", true); 81 | List stepDefinitionList = new ArrayList<>(); 82 | for (Object o : element.elements()) { 83 | Element e = (Element) o; 84 | if (e.getName().equalsIgnoreCase("step")) { 85 | stepDefinitionList.add(parseStep(e)); 86 | } 87 | } 88 | 89 | return new FlowDefinition(flowName, stepDefinitionList); 90 | } 91 | 92 | private static StepDefinition parseStep(Element element) { 93 | String stepName = getNodeAttribute(element, "name", true); 94 | String asyn = getNodeAttribute(element, "asyn", false); 95 | String action = getNodeAttribute(element, "action", false); 96 | String subflow = getNodeAttribute(element, "subflow", false); 97 | 98 | ConditionDefinition conditionDefinition = null; 99 | List transitionDefinitionList = new ArrayList<>(); 100 | 101 | for (Object o : element.elements()) { 102 | Element e = (Element) o; 103 | switch (e.getName().toLowerCase()) { 104 | case "condition": 105 | case "expCondition": 106 | case "andCondition": 107 | case "orCondition": 108 | conditionDefinition = parseCondition(e); 109 | break; 110 | case "conditionTransition": 111 | case "orderTransition": 112 | transitionDefinitionList.add(parseTransition(e)); 113 | break; 114 | } 115 | } 116 | 117 | StepDefinition stepDefinition = new StepDefinition(); 118 | stepDefinition.setName(stepName); 119 | stepDefinition.setAsyn(asyn); 120 | stepDefinition.setAction(action); 121 | stepDefinition.setSubflow(subflow); 122 | stepDefinition.setConditionDefinition(conditionDefinition); 123 | stepDefinition.setTransitionDefinitionList(transitionDefinitionList); 124 | 125 | return stepDefinition; 126 | } 127 | 128 | private static TransitionDefinition parseTransition(Element element) { 129 | switch (element.getName().toLowerCase()) { 130 | case "conditionTransition": 131 | String to = getNodeAttribute(element, "to", true); 132 | ConditionDefinition conditionDefinition = null; 133 | for (Object o : element.elements()) { 134 | conditionDefinition = parseCondition((Element) o); 135 | break; 136 | } 137 | return new ConditionTransitionDefinition(conditionDefinition, to); 138 | case "orderTransition": 139 | return new OrderTransitionDefinition(getNodeAttribute(element, "to", true)); 140 | default: 141 | return null; 142 | } 143 | } 144 | 145 | private static ConditionDefinition parseCondition(Element element) { 146 | List conditionDefinitionList = new ArrayList<>(); 147 | switch (element.getName().toLowerCase()) { 148 | case "condition": 149 | String key = getNodeAttribute(element, "key", true); 150 | String value = getNodeAttribute(element, "value", true); 151 | String compareType = getNodeAttribute(element, "comparator", true); 152 | return new DefaultConditionDefinition(key, value, compareType); 153 | case "expCondition": 154 | String express = getNodeAttribute(element, "express", true); 155 | return new GroovyExpressConditionDefinition(express); 156 | case "andCondition": 157 | for (Object o : element.elements()) { 158 | conditionDefinitionList.add(parseCondition((Element) o)); 159 | } 160 | return new AndConditionDefinition(conditionDefinitionList); 161 | case "orCondition": 162 | for (Object o : element.elements()) { 163 | conditionDefinitionList.add(parseCondition((Element) o)); 164 | } 165 | return new OrConditionDefinition(conditionDefinitionList); 166 | default: 167 | return null; 168 | } 169 | } 170 | 171 | private static String getNodeAttribute(Element element, String key, boolean required) { 172 | Attribute attribute = element.attribute(key); 173 | if (required && (attribute == null || attribute.getValue() == null)) { 174 | throw new IllegalStateException("节点属性:" + attribute + " 不能为空!" + element.toString()); 175 | } 176 | return attribute == null || attribute.getValue() == null ? null : attribute.getValue(); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/Definition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition; 2 | 3 | /** 4 | * Created by houan on 18/7/24. 5 | */ 6 | public interface Definition { 7 | 8 | boolean isValid(); 9 | 10 | Object getInstance() throws InvalidConfigException; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/FlowDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.config.definition.transition.TransitionDefinition; 5 | import org.cuner.flowframework.core.Flow; 6 | import org.cuner.flowframework.core.Step; 7 | import org.cuner.flowframework.core.transition.OrderTransition; 8 | import org.cuner.flowframework.core.transition.Transition; 9 | import org.springframework.util.StringUtils; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * Created by houan on 18/7/24. 18 | */ 19 | public class FlowDefinition implements Definition { 20 | 21 | private String name; 22 | 23 | private List stepDefinitionList; 24 | 25 | private Flow instance; 26 | 27 | public FlowDefinition(String name, List stepDefinitionList) { 28 | this.name = name; 29 | this.stepDefinitionList = stepDefinitionList; 30 | } 31 | 32 | @Override 33 | public boolean isValid() { 34 | return !StringUtils.isEmpty(name) && CollectionUtils.isNotEmpty(stepDefinitionList); 35 | } 36 | 37 | @Override 38 | public Flow getInstance() throws InvalidConfigException { 39 | if (instance != null) { 40 | return instance; 41 | } 42 | 43 | if (!isValid()) { 44 | throw new InvalidConfigException(this.toString()); 45 | } 46 | 47 | instance = new Flow(); 48 | instance.setName(name); 49 | 50 | List stepList = new ArrayList<>(); 51 | Map stepMap = new HashMap<>(); 52 | for (StepDefinition stepDefinition : stepDefinitionList) { 53 | Step step = stepDefinition.getInstance(); 54 | stepList.add(step); 55 | stepMap.put(step.getName(), step); 56 | } 57 | instance.setSteps(stepList); 58 | 59 | //解析transition to 60 | for (StepDefinition sd : stepDefinitionList) { 61 | List tdl = sd.getTransitionDefinitionList(); 62 | for (TransitionDefinition td : tdl) { 63 | Step targetStep = stepMap.get(td.getTo()); 64 | if (targetStep == null) { 65 | throw new InvalidConfigException("transition to not found:" + td.getTo()); 66 | } 67 | td.getInstance().setTarget(targetStep); 68 | } 69 | } 70 | 71 | // 为没有定义的设置默认transition 72 | for (int i = 0; i < stepList.size() - 1; i++) { 73 | Step s = stepList.get(i); 74 | if (CollectionUtils.isEmpty(s.getTransitionList())) { 75 | OrderTransition st = new OrderTransition(stepList.get(i + 1)); 76 | List transitions = new ArrayList<>(); 77 | transitions.add(st); 78 | s.setTransitionList(transitions); 79 | } 80 | } 81 | return instance; 82 | } 83 | 84 | public String getName() { 85 | return name; 86 | } 87 | 88 | public void setName(String name) { 89 | this.name = name; 90 | } 91 | 92 | public List getStepDefinitionList() { 93 | return stepDefinitionList; 94 | } 95 | 96 | public void setStepDefinitionList(List stepDefinitionList) { 97 | this.stepDefinitionList = stepDefinitionList; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "FlowDefinition{" + 103 | "name='" + name + '\'' + 104 | ", stepDefinitionList=" + stepDefinitionList + 105 | '}'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/InvalidConfigException.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition; 2 | 3 | /** 4 | * Created by houan on 18/7/24. 5 | */ 6 | public class InvalidConfigException extends Exception { 7 | 8 | public InvalidConfigException() { 9 | 10 | } 11 | 12 | public InvalidConfigException(String msg) { 13 | super("invalid config: " + msg); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/StepDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.config.definition.transition.TransitionDefinition; 5 | import org.cuner.flowframework.config.definition.transition.condition.ConditionDefinition; 6 | import org.cuner.flowframework.core.Step; 7 | import org.cuner.flowframework.core.transition.Transition; 8 | import org.springframework.util.StringUtils; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by houan on 18/7/24. 15 | */ 16 | public class StepDefinition implements Definition { 17 | 18 | private String name; 19 | 20 | private String asyn = "false"; 21 | 22 | private ConditionDefinition conditionDefinition; 23 | 24 | private String action; 25 | 26 | private String subflow; 27 | 28 | private List transitionDefinitionList; 29 | 30 | private Step instance; 31 | 32 | @Override 33 | public boolean isValid() { 34 | if (StringUtils.isEmpty(name)) { 35 | return false; 36 | } 37 | 38 | if (StringUtils.isEmpty(action) && StringUtils.isEmpty(subflow)) { 39 | return false; 40 | } 41 | 42 | if (!StringUtils.isEmpty(action) && !StringUtils.isEmpty(subflow)) { 43 | return false; 44 | } 45 | 46 | return true; 47 | } 48 | 49 | @Override 50 | public Step getInstance() throws InvalidConfigException { 51 | if (instance != null) { 52 | return instance; 53 | } 54 | 55 | if (!isValid()) { 56 | throw new InvalidConfigException(this.toString()); 57 | } 58 | 59 | instance = new Step(); 60 | instance.setName(this.name); 61 | instance.setAsyn(Boolean.parseBoolean(this.asyn)); 62 | 63 | if (conditionDefinition != null) { 64 | instance.setCondition(conditionDefinition.getInstance()); 65 | } 66 | 67 | if (CollectionUtils.isNotEmpty(transitionDefinitionList)) { 68 | List transitionList = new ArrayList<>(); 69 | for (TransitionDefinition td : transitionDefinitionList) { 70 | transitionList.add(td.getInstance()); 71 | } 72 | instance.setTransitionList(transitionList); 73 | } 74 | 75 | //step的action和subflow后面注入 76 | return instance; 77 | 78 | } 79 | 80 | public String getName() { 81 | return name; 82 | } 83 | 84 | public void setName(String name) { 85 | this.name = name; 86 | } 87 | 88 | public String getAsyn() { 89 | return asyn; 90 | } 91 | 92 | public void setAsyn(String asyn) { 93 | this.asyn = asyn; 94 | } 95 | 96 | public ConditionDefinition getConditionDefinition() { 97 | return conditionDefinition; 98 | } 99 | 100 | public void setConditionDefinition(ConditionDefinition conditionDefinition) { 101 | this.conditionDefinition = conditionDefinition; 102 | } 103 | 104 | public String getAction() { 105 | return action; 106 | } 107 | 108 | public void setAction(String action) { 109 | this.action = action; 110 | } 111 | 112 | public String getSubflow() { 113 | return subflow; 114 | } 115 | 116 | public void setSubflow(String subflow) { 117 | this.subflow = subflow; 118 | } 119 | 120 | public List getTransitionDefinitionList() { 121 | return transitionDefinitionList; 122 | } 123 | 124 | public void setTransitionDefinitionList(List transitionDefinitionList) { 125 | this.transitionDefinitionList = transitionDefinitionList; 126 | } 127 | 128 | public void setInstance(Step instance) { 129 | this.instance = instance; 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return "StepDefinition{" + 135 | "name='" + name + '\'' + 136 | ", asyn='" + asyn + '\'' + 137 | ", conditionDefinition=" + conditionDefinition + 138 | ", action='" + action + '\'' + 139 | ", subflow='" + subflow + '\'' + 140 | ", transitionDefinitionList=" + transitionDefinitionList + 141 | '}'; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/ConditionTransitionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition; 2 | 3 | import org.cuner.flowframework.config.definition.InvalidConfigException; 4 | import org.cuner.flowframework.config.definition.transition.condition.ConditionDefinition; 5 | import org.cuner.flowframework.core.transition.ConditionTransition; 6 | import org.cuner.flowframework.core.transition.Transition; 7 | import org.springframework.util.StringUtils; 8 | 9 | /** 10 | * Created by houan on 18/7/24. 11 | */ 12 | public class ConditionTransitionDefinition extends TransitionDefinition { 13 | 14 | private ConditionDefinition conditionDefinition; 15 | 16 | private Transition instance; 17 | 18 | public ConditionTransitionDefinition(ConditionDefinition conditionDefinition, String to) { 19 | this.conditionDefinition = conditionDefinition; 20 | this.to = to; 21 | } 22 | 23 | @Override 24 | public boolean isValid() { 25 | return !StringUtils.isEmpty(to) && conditionDefinition != null; 26 | } 27 | 28 | @Override 29 | public Transition getInstance() throws InvalidConfigException { 30 | if (instance != null) { 31 | return instance; 32 | } 33 | 34 | if (!isValid()) { 35 | throw new InvalidConfigException(this.toString()); 36 | } 37 | 38 | //后面通过FlowDefinition注入to所代表的step 39 | instance = new ConditionTransition(conditionDefinition.getInstance(), null); 40 | return instance; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "ConditionTransitionDefinition{" + 46 | "conditionDefinition=" + conditionDefinition + 47 | ", to='" + to + '\'' + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/OrderTransitionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition; 2 | 3 | import org.cuner.flowframework.config.definition.InvalidConfigException; 4 | import org.cuner.flowframework.core.transition.OrderTransition; 5 | import org.cuner.flowframework.core.transition.Transition; 6 | import org.springframework.util.StringUtils; 7 | 8 | /** 9 | * Created by houan on 18/7/24. 10 | */ 11 | public class OrderTransitionDefinition extends TransitionDefinition { 12 | 13 | private Transition instance; 14 | 15 | public OrderTransitionDefinition(String to) { 16 | this.to = to; 17 | } 18 | 19 | @Override 20 | public boolean isValid() { 21 | return !StringUtils.isEmpty(to); 22 | } 23 | 24 | @Override 25 | public Transition getInstance() throws InvalidConfigException { 26 | if (instance != null) { 27 | return instance; 28 | } 29 | 30 | if (!isValid()) { 31 | throw new InvalidConfigException(this.toString()); 32 | } 33 | 34 | //后面通过FlowDefinition注入to所代表的step 35 | instance = new OrderTransition(null); 36 | return instance; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "OrderTransitionDefinition{" + 42 | "to='" + to + '\'' + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/TransitionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition; 2 | 3 | import org.cuner.flowframework.config.definition.Definition; 4 | import org.cuner.flowframework.config.definition.InvalidConfigException; 5 | import org.cuner.flowframework.core.transition.Transition; 6 | 7 | /** 8 | * Created by houan on 18/7/24. 9 | */ 10 | public abstract class TransitionDefinition implements Definition { 11 | 12 | protected String to; 13 | 14 | public String getTo() { 15 | return to; 16 | } 17 | 18 | public void setTo(String to) { 19 | this.to = to; 20 | } 21 | 22 | public abstract Transition getInstance() throws InvalidConfigException; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/condition/AndConditionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition.condition; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.config.definition.InvalidConfigException; 5 | import org.cuner.flowframework.core.transition.condition.AndCondition; 6 | import org.cuner.flowframework.core.transition.condition.Condition; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by houan on 18/7/24. 13 | */ 14 | public class AndConditionDefinition extends ConditionDefinition { 15 | 16 | private List conditionDefinitionList; 17 | 18 | private Condition instance; 19 | 20 | public AndConditionDefinition(List conditionDefinitionList) { 21 | this.conditionDefinitionList = conditionDefinitionList; 22 | } 23 | 24 | @Override 25 | public boolean isValid() { 26 | return CollectionUtils.isNotEmpty(conditionDefinitionList); 27 | } 28 | 29 | @Override 30 | public Condition getInstance() throws InvalidConfigException { 31 | if (instance != null) { 32 | return instance; 33 | } 34 | 35 | if (!isValid()) { 36 | throw new InvalidConfigException(this.toString()); 37 | } 38 | 39 | List conditionList = new ArrayList<>(); 40 | for (ConditionDefinition conditionDefinition : conditionDefinitionList) { 41 | conditionList.add(conditionDefinition.getInstance()); 42 | } 43 | 44 | instance = new AndCondition(conditionList); 45 | return instance; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "AndConditionDefinition{" + 51 | "conditionDefinitionList=" + conditionDefinitionList + 52 | '}'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/condition/ConditionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition.condition; 2 | 3 | import org.cuner.flowframework.config.definition.Definition; 4 | import org.cuner.flowframework.config.definition.InvalidConfigException; 5 | import org.cuner.flowframework.core.transition.condition.Condition; 6 | 7 | /** 8 | * Created by houan on 18/7/24. 9 | */ 10 | public abstract class ConditionDefinition implements Definition { 11 | public abstract Condition getInstance() throws InvalidConfigException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/condition/DefaultConditionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition.condition; 2 | 3 | import org.cuner.flowframework.config.definition.InvalidConfigException; 4 | import org.cuner.flowframework.core.transition.condition.Condition; 5 | import org.cuner.flowframework.core.transition.condition.DefaultCondition; 6 | import org.cuner.flowframework.support.Comparator; 7 | import org.springframework.util.StringUtils; 8 | 9 | /** 10 | * Created by houan on 18/7/24. 11 | */ 12 | public class DefaultConditionDefinition extends ConditionDefinition { 13 | 14 | private String key; 15 | 16 | private String value; 17 | 18 | private String compareType; 19 | 20 | private Condition instance; 21 | 22 | public DefaultConditionDefinition(String key, String value, String compareType) { 23 | this.key = key; 24 | this.value = value; 25 | this.compareType = compareType; 26 | } 27 | 28 | @Override 29 | public boolean isValid() { 30 | return !StringUtils.isEmpty(key) && !StringUtils.isEmpty(value) && !StringUtils.isEmpty(compareType) 31 | && Comparator.CompareType.getCompareType(this.compareType) != null; 32 | } 33 | 34 | @Override 35 | public Condition getInstance() throws InvalidConfigException { 36 | if (instance != null) { 37 | return instance; 38 | } 39 | 40 | if (!isValid()) { 41 | throw new InvalidConfigException(this.toString()); 42 | } 43 | instance = new DefaultCondition(key, value, Comparator.CompareType.getCompareType(this.compareType)); 44 | return instance; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "DefaultConditionDefinition{" + 50 | "key='" + key + '\'' + 51 | ", value='" + value + '\'' + 52 | ", compareType='" + compareType + '\'' + 53 | '}'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/condition/GroovyExpressConditionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition.condition; 2 | 3 | import org.cuner.flowframework.config.definition.InvalidConfigException; 4 | import org.cuner.flowframework.core.transition.condition.Condition; 5 | import org.cuner.flowframework.core.transition.condition.GroovyExpressCondition; 6 | import org.springframework.util.StringUtils; 7 | 8 | /** 9 | * Created by houan on 18/7/24. 10 | */ 11 | public class GroovyExpressConditionDefinition extends ConditionDefinition { 12 | 13 | private String express; 14 | 15 | private Condition instance; 16 | 17 | public GroovyExpressConditionDefinition(String express) { 18 | this.express = express; 19 | } 20 | 21 | @Override 22 | public boolean isValid() { 23 | return !StringUtils.isEmpty(express); 24 | } 25 | 26 | @Override 27 | public Condition getInstance() throws InvalidConfigException { 28 | if (instance != null) { 29 | return instance; 30 | } 31 | 32 | if (!isValid()) { 33 | throw new InvalidConfigException(this.toString()); 34 | } 35 | 36 | instance = new GroovyExpressCondition(express); 37 | return instance; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "GroovyExpressConditionDefinition{" + 43 | "express='" + express + '\'' + 44 | '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/config/definition/transition/condition/OrConditionDefinition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.config.definition.transition.condition; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.config.definition.InvalidConfigException; 5 | import org.cuner.flowframework.core.transition.condition.AndCondition; 6 | import org.cuner.flowframework.core.transition.condition.Condition; 7 | import org.cuner.flowframework.core.transition.condition.OrCondition; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by houan on 18/7/24. 14 | */ 15 | public class OrConditionDefinition extends ConditionDefinition { 16 | 17 | private List conditionDefinitionList; 18 | 19 | private Condition instance; 20 | 21 | public OrConditionDefinition(List conditionDefinitionList) { 22 | this.conditionDefinitionList = conditionDefinitionList; 23 | } 24 | 25 | @Override 26 | public boolean isValid() { 27 | return CollectionUtils.isNotEmpty(conditionDefinitionList); 28 | } 29 | 30 | @Override 31 | public Condition getInstance() throws InvalidConfigException { 32 | if (instance != null) { 33 | return instance; 34 | } 35 | 36 | if (!isValid()) { 37 | throw new InvalidConfigException(this.toString()); 38 | } 39 | 40 | List conditionList = new ArrayList<>(); 41 | for (ConditionDefinition conditionDefinition : conditionDefinitionList) { 42 | conditionList.add(conditionDefinition.getInstance()); 43 | } 44 | 45 | instance = new OrCondition(conditionList); 46 | return instance; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "OrConditionDefinition{" + 52 | "conditionDefinitionList=" + conditionDefinitionList + 53 | '}'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/Action.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core; 2 | 3 | /** 4 | * Created by houan on 18/7/18. 5 | */ 6 | public interface Action { 7 | 8 | void execute(FlowContext context); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/Flow.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core; 2 | 3 | import org.cuner.flowframework.support.log.Execution; 4 | import org.cuner.flowframework.support.log.ExecutionType; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.Executor; 9 | 10 | /** 11 | * Created by houan on 18/7/18. 12 | */ 13 | public class Flow { 14 | 15 | /** 16 | * 流程名称 17 | */ 18 | private String name; 19 | 20 | /** 21 | * 流程下所有的步骤 22 | */ 23 | private List steps; 24 | 25 | /** 26 | * 执行器 27 | */ 28 | private Executor executor; 29 | 30 | /** 31 | * 流程执行 32 | * @param context 上下问 33 | * @param execution 上游执行流程节点 34 | */ 35 | public void execute(FlowContext context, Execution execution) { 36 | if (null == steps) { 37 | return; 38 | } 39 | 40 | Execution flowExecution = new Execution(); 41 | flowExecution.setName(this.name); 42 | flowExecution.setStartTime(System.currentTimeMillis()); 43 | 44 | if (execution == null) { 45 | flowExecution.setExecutionType(ExecutionType.FLOW); 46 | context.setExecution(flowExecution); 47 | } else { 48 | flowExecution.setExecutionType(ExecutionType.SUBFLOW); 49 | execution.getChildren().add(flowExecution); 50 | } 51 | 52 | try { 53 | Step step = steps.get(0); 54 | do { 55 | if (!step.isAsyn()) { 56 | step.execute(context, flowExecution); 57 | } else { 58 | final Step nextStep = step; 59 | executor.execute(() -> nextStep.execute(context, flowExecution)); 60 | } 61 | 62 | step = step.next(context); 63 | } while (step != null); 64 | } finally { 65 | flowExecution.setEndTime(System.currentTimeMillis()); 66 | } 67 | 68 | } 69 | 70 | public String getName() { 71 | return name; 72 | } 73 | 74 | public void setName(String name) { 75 | this.name = name; 76 | } 77 | 78 | public List getSteps() { 79 | return steps; 80 | } 81 | 82 | public void setSteps(List steps) { 83 | this.steps = steps; 84 | } 85 | 86 | public Executor getExecutor() { 87 | return executor; 88 | } 89 | 90 | public void setExecutor(Executor executor) { 91 | this.executor = executor; 92 | } 93 | 94 | public List getSubflows() { 95 | if (null == steps){ 96 | return new ArrayList<>(); 97 | } 98 | List subflows = new ArrayList<>(); 99 | for (Step step : steps){ 100 | Flow subflow = step.getSubflow(); 101 | if (null != subflow){ 102 | subflows.add(subflow); 103 | } 104 | } 105 | return subflows; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/FlowContext.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core; 2 | 3 | import org.cuner.flowframework.support.log.Execution; 4 | 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | /** 9 | * Created by houan on 18/7/19. 10 | */ 11 | public class FlowContext { 12 | 13 | /** 14 | * 业务数据对象 15 | */ 16 | private T data; 17 | 18 | /** 19 | * 流程执行结果 20 | */ 21 | private R result; 22 | 23 | /** 24 | * 正在执行的流程 25 | */ 26 | private Flow flow; 27 | 28 | /** 29 | * 正在执行的步骤 30 | */ 31 | private Step step; 32 | 33 | /** 34 | * 自定义参数 35 | */ 36 | private ConcurrentHashMap parameters = new ConcurrentHashMap<>(); 37 | 38 | /** 39 | * 执行路径 40 | */ 41 | private Execution execution; 42 | 43 | /** 44 | * 闭锁 用于触发异步任务全部完成 45 | */ 46 | private CountDownLatch countDownLatch; 47 | 48 | /* 49 | * 构造一个业务方不能修改的流程上下文,防止业务方干预流程执行过程 50 | */ 51 | public FlowContext createFlowContext() { 52 | return new FlowContext() { 53 | public T getData() { 54 | return data; 55 | } 56 | 57 | public void setResult(R object) { 58 | result = object; 59 | } 60 | 61 | public R getResult() { 62 | return result; 63 | } 64 | 65 | public String getFlowName() { 66 | return flow.getName(); 67 | } 68 | 69 | public String getStepName() { 70 | return step.getName(); 71 | } 72 | 73 | public void setParameter(String key, Object value) { 74 | if (null == key){ 75 | throw new IllegalArgumentException("key is null!"); 76 | } 77 | parameters.put(key, value); 78 | } 79 | 80 | public Object getParameter(String key) { 81 | return parameters.get(key); 82 | } 83 | 84 | public ConcurrentHashMap getParameters() { 85 | return parameters; 86 | } 87 | }; 88 | } 89 | 90 | public FlowContext copy() { 91 | FlowContext flowContext = new FlowContext<>(); 92 | flowContext.setFlow(this.flow); 93 | flowContext.setStep(this.step); 94 | flowContext.setData(this.data); 95 | flowContext.setResult(null); 96 | flowContext.setParameters(this.parameters); 97 | flowContext.setCountDownLatch(countDownLatch); 98 | return flowContext; 99 | } 100 | 101 | 102 | public T getData() { 103 | return data; 104 | } 105 | 106 | public void setData(T data) { 107 | this.data = data; 108 | } 109 | 110 | public R getResult() { 111 | return result; 112 | } 113 | 114 | public void setResult(R result) { 115 | this.result = result; 116 | } 117 | 118 | public Flow getFlow() { 119 | return flow; 120 | } 121 | 122 | public void setFlow(Flow flow) { 123 | this.flow = flow; 124 | } 125 | 126 | public Step getStep() { 127 | return step; 128 | } 129 | 130 | public void setStep(Step step) { 131 | this.step = step; 132 | } 133 | 134 | public ConcurrentHashMap getParameters() { 135 | return parameters; 136 | } 137 | 138 | public void setParameters(ConcurrentHashMap parameters) { 139 | this.parameters = parameters; 140 | } 141 | 142 | public String getFlowName() { 143 | return flow.getName(); 144 | } 145 | 146 | public String getStepName() { 147 | return step.getName(); 148 | } 149 | 150 | public void setParameter(String key, Object value) { 151 | parameters.put(key, value); 152 | } 153 | 154 | public Object getParameter(String key) { 155 | return parameters.get(key); 156 | } 157 | 158 | public Execution getExecution() { 159 | return execution; 160 | } 161 | 162 | public void setExecution(Execution execution) { 163 | this.execution = execution; 164 | } 165 | 166 | public CountDownLatch getCountDownLatch() { 167 | return countDownLatch; 168 | } 169 | 170 | public void setCountDownLatch(CountDownLatch countDownLatch) { 171 | this.countDownLatch = countDownLatch; 172 | } 173 | 174 | @Override 175 | public String toString() { 176 | return "FlowContext{" + 177 | "data=" + data + 178 | ", result=" + result + 179 | ", flow=" + flow + 180 | ", step=" + step + 181 | ", parameters=" + parameters + 182 | '}'; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/Step.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.core.transition.Transition; 5 | import org.cuner.flowframework.core.transition.condition.Condition; 6 | import org.cuner.flowframework.support.log.Execution; 7 | import org.cuner.flowframework.support.log.ExecutionType; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by houan on 18/7/24. 13 | */ 14 | public class Step { 15 | 16 | /** 17 | * 步骤名称 18 | */ 19 | private String name; 20 | 21 | /** 22 | * 是否异步执行 23 | */ 24 | private boolean asyn = false; 25 | 26 | /** 27 | * 执行条件 28 | */ 29 | private Condition condition; 30 | 31 | /** 32 | * 步骤执行的操作 33 | */ 34 | private Action action; 35 | 36 | /** 37 | * 步骤代表的子流程 38 | */ 39 | private Flow subflow; 40 | 41 | /** 42 | * 该步骤执行结束后的符合所有条件的流转信息(因为什么条件流程到什么步骤) 43 | */ 44 | private List transitionList; 45 | 46 | /** 47 | * 通过上下文获取符合条件的流转信息 48 | * @param context 49 | * @return 50 | */ 51 | public Transition getMatchedTransition(FlowContext context) { 52 | if (CollectionUtils.isEmpty(transitionList)) { 53 | return null; 54 | } 55 | 56 | for (Transition transition : transitionList) { 57 | if (transition.match(context)) { 58 | return transition; 59 | } 60 | } 61 | 62 | return null; 63 | } 64 | 65 | /** 66 | * 获取下一个步骤 67 | * @param flowContext 68 | * @return 69 | */ 70 | public Step next(FlowContext flowContext) { 71 | if (CollectionUtils.isEmpty(transitionList)) { 72 | return null; 73 | } 74 | 75 | for (Transition transition : transitionList) { 76 | if (transition.match(flowContext)) { 77 | return transition.getTarget(flowContext); 78 | } 79 | } 80 | 81 | return null; 82 | } 83 | 84 | /** 85 | * 步骤执行 86 | * @param context 执行上下文 87 | * @param execution 上游执行流程节点 88 | */ 89 | @SuppressWarnings("all") 90 | public void execute(FlowContext context, Execution execution) { 91 | if (condition != null && !condition.match(context)) { 92 | return; 93 | } 94 | 95 | Execution stepExecution = new Execution(); 96 | stepExecution.setName(this.name); 97 | stepExecution.setStartTime(System.currentTimeMillis()); 98 | stepExecution.setAsyn(this.asyn); 99 | stepExecution.setExecutionType(ExecutionType.STEP); 100 | 101 | execution.getChildren().add(stepExecution); 102 | 103 | try { 104 | if (action != null) { 105 | context.setStep(this); 106 | action.execute(context.createFlowContext()); 107 | } else if (subflow != null) { 108 | FlowContext copiedContext = context.copy(); 109 | copiedContext.setFlow(subflow); 110 | copiedContext.setStep(null); 111 | subflow.execute(copiedContext, stepExecution); 112 | } 113 | 114 | if (this.asyn) { 115 | context.getCountDownLatch().countDown(); 116 | } 117 | } finally { 118 | stepExecution.setEndTime(System.currentTimeMillis()); 119 | } 120 | 121 | } 122 | 123 | 124 | public String getName() { 125 | return name; 126 | } 127 | 128 | public void setName(String name) { 129 | this.name = name; 130 | } 131 | 132 | public boolean isAsyn() { 133 | return asyn; 134 | } 135 | 136 | public void setAsyn(boolean asyn) { 137 | this.asyn = asyn; 138 | } 139 | 140 | public Condition getCondition() { 141 | return condition; 142 | } 143 | 144 | public void setCondition(Condition condition) { 145 | this.condition = condition; 146 | } 147 | 148 | public Action getAction() { 149 | return action; 150 | } 151 | 152 | public void setAction(Action action) { 153 | this.action = action; 154 | } 155 | 156 | public Flow getSubflow() { 157 | return subflow; 158 | } 159 | 160 | public void setSubflow(Flow subflow) { 161 | this.subflow = subflow; 162 | } 163 | 164 | public List getTransitionList() { 165 | return transitionList; 166 | } 167 | 168 | public void setTransitionList(List transitionList) { 169 | this.transitionList = transitionList; 170 | } 171 | 172 | @Override 173 | public String toString() { 174 | return "Step{" + 175 | "name='" + name + '\'' + 176 | ", asyn=" + asyn + 177 | ", condition=" + condition + 178 | ", action=" + action + 179 | ", subflow=" + subflow + 180 | ", transitionList=" + transitionList + 181 | '}'; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/manager/FlowManager.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.manager; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.config.XmlParser; 5 | import org.cuner.flowframework.core.Action; 6 | import org.cuner.flowframework.core.Flow; 7 | import org.cuner.flowframework.core.FlowContext; 8 | import org.cuner.flowframework.core.Step; 9 | import org.cuner.flowframework.support.log.Execution; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.BeansException; 13 | import org.springframework.context.ApplicationContext; 14 | import org.springframework.context.ApplicationContextAware; 15 | import org.springframework.context.ApplicationListener; 16 | import org.springframework.context.event.ApplicationContextEvent; 17 | import org.springframework.context.event.ContextRefreshedEvent; 18 | import org.springframework.util.StringUtils; 19 | 20 | import java.lang.reflect.Method; 21 | import java.lang.reflect.ParameterizedType; 22 | import java.lang.reflect.Type; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.concurrent.*; 27 | 28 | /** 29 | * Created by houan on 18/7/26. 30 | */ 31 | public class FlowManager implements ApplicationListener, ApplicationContextAware { 32 | 33 | private boolean init = false; 34 | 35 | private List flowFiles; 36 | 37 | private Map flowMap; 38 | 39 | private ExecutorService executorService; 40 | 41 | private ApplicationContext applicationContext; 42 | 43 | private Map flowDataTypeMap = new HashMap<>(); 44 | 45 | private Logger logger = LoggerFactory.getLogger("flow-record"); 46 | 47 | public FlowManager() { 48 | } 49 | 50 | public FlowManager(List flowFiles) { 51 | this.flowFiles = flowFiles; 52 | } 53 | 54 | @Override 55 | public void onApplicationEvent(ApplicationContextEvent event) { 56 | if (event instanceof ContextRefreshedEvent) { 57 | this.init = true; 58 | try { 59 | this.flowMap = XmlParser.parse(flowFiles, applicationContext); 60 | 61 | //校验子流程, 为了防止出现死循环 62 | this.flowMap.values().forEach(flow -> checkSubFlow(flow.getSubflows(), flow.getName())); 63 | this.flowMap.values().forEach(flow -> flowDataTypeMap.put(flow, validateFlow(flow))); 64 | 65 | executorService = new ThreadPoolExecutor(5, 66 | 10, 67 | 60, 68 | TimeUnit.SECONDS, 69 | new LinkedBlockingDeque<>(1000), 70 | new ThreadPoolExecutor.CallerRunsPolicy()); 71 | } catch (Exception e) { 72 | throw new RuntimeException("fail to load flows:", e); 73 | } 74 | } 75 | 76 | } 77 | 78 | public R execute(String flowName, T data) { 79 | if (!init) { 80 | throw new IllegalStateException("The flow engine has not been initialized!"); 81 | } 82 | if (null == flowName) { 83 | throw new IllegalArgumentException("flowName is null!"); 84 | } 85 | Flow flow = flowMap.get(flowName); 86 | if (null == flow) { 87 | throw new IllegalArgumentException("flow: " + flowName + "not exists!"); 88 | } 89 | 90 | flow.setExecutor(executorService); 91 | for (Flow subflow : flow.getSubflows()) { 92 | subflow.setExecutor(executorService); 93 | } 94 | 95 | FlowContext flowContext = new FlowContext<>(); 96 | 97 | //get all aync step count 98 | int ayncStepCount = getFlowAyncStepCount(flow); 99 | flowContext.setCountDownLatch(new CountDownLatch(ayncStepCount)); 100 | 101 | flowContext.setData(data); 102 | flowContext.setFlow(flow); 103 | 104 | Type genericDataType = flowDataTypeMap.get(flow); 105 | if (data.getClass() != genericDataType) { 106 | throw new IllegalArgumentException("input data type or result data is not compatible with action generic type! "); 107 | } 108 | 109 | try { 110 | flow.execute(flowContext, null); 111 | flowContext.getCountDownLatch().await(); 112 | } catch (InterruptedException e) { 113 | return flowContext.getResult(); 114 | } finally { 115 | logger.info(Execution.getExecutionString(flowContext.getExecution(), 0)); 116 | } 117 | 118 | return flowContext.getResult(); 119 | } 120 | 121 | private int getFlowAyncStepCount(Flow flow) { 122 | int count = 0; 123 | for (Step step : flow.getSteps()) { 124 | if (step.isAsyn()) { 125 | count++; 126 | } 127 | if (!StringUtils.isEmpty(step.getSubflow())) { 128 | count += getFlowAyncStepCount(step.getSubflow()); 129 | } 130 | } 131 | 132 | return count; 133 | } 134 | 135 | //流程包含子流程 校验子流程, 为了防止出现死循环:子流程不能为父流程 136 | private void checkSubFlow(List subflowList, String parentFlow) { 137 | if (CollectionUtils.isNotEmpty(subflowList)) { 138 | if (subflowList.stream().anyMatch(flow -> flow.getName().equals(parentFlow))) { 139 | throw new IllegalArgumentException("flow is in endless loop"); 140 | } else { 141 | subflowList.forEach(flow -> checkSubFlow(flow.getSubflows(), parentFlow)); 142 | } 143 | } 144 | } 145 | 146 | /* 147 | * 流程Action校验, 同一个流程中的所有step的Action必须继承相同的FlowAction接口 148 | */ 149 | private Type validateFlow(Flow flow) { 150 | Type type = null; 151 | 152 | List steps = flow.getSteps(); 153 | if (null == steps) { 154 | throw new IllegalStateException("flow: " + flow.getName() + " has no steps!"); 155 | } 156 | Type genericDataType = null; 157 | Type genericResultType = null; 158 | for (Step step : steps) { 159 | Action action = step.getAction(); 160 | if (null == action) { 161 | continue; 162 | } 163 | 164 | if (!Action.class.isAssignableFrom(action.getClass())) { 165 | throw new IllegalStateException("Action class:" + action.getClass().getName() + " must implement the FlowAction interface!"); 166 | } 167 | 168 | try { 169 | Method method = action.getClass().getMethod("execute", FlowContext.class); 170 | Type[] types = method.getGenericParameterTypes(); 171 | Type[] params = ((ParameterizedType) types[0]).getActualTypeArguments(); 172 | 173 | if (null == genericDataType) { 174 | genericDataType = params[0]; 175 | } else if (!params[0].equals(genericDataType)) { 176 | throw new IllegalStateException("flow:" + flow.getName() + " has different Action generic parameter types!"); 177 | } 178 | 179 | if (null == genericResultType) { 180 | genericResultType = params[1]; 181 | } else if (!params[1].equals(genericResultType)) { 182 | throw new IllegalStateException("flow:" + flow.getName() + " has different Action generic result types!"); 183 | } 184 | 185 | } catch (NoSuchMethodException e) { 186 | //不太可能出现这种异常 187 | } 188 | } 189 | if (null != genericDataType) { 190 | if (genericDataType instanceof ParameterizedType) { 191 | type = ((ParameterizedType) genericDataType).getRawType(); 192 | } else { 193 | type = genericDataType; 194 | } 195 | } 196 | 197 | return type; 198 | } 199 | 200 | public List getFlowFiles() { 201 | return flowFiles; 202 | } 203 | 204 | public void setFlowFiles(List flowFiles) { 205 | this.flowFiles = flowFiles; 206 | } 207 | 208 | @Override 209 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 210 | this.applicationContext = applicationContext; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/ConditionTransition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | import org.cuner.flowframework.core.Step; 5 | import org.cuner.flowframework.core.transition.condition.Condition; 6 | 7 | /** 8 | * 条件跳转 9 | * Created by houan on 18/7/24. 10 | */ 11 | public class ConditionTransition implements Transition { 12 | 13 | private Condition condition; 14 | 15 | private Step to; 16 | 17 | public ConditionTransition(Condition condition, Step to) { 18 | this.condition = condition; 19 | this.to = to; 20 | } 21 | 22 | @Override 23 | public boolean match(FlowContext context) { 24 | return condition.match(context); 25 | } 26 | 27 | @Override 28 | public Step getTarget(FlowContext context) { 29 | return to; 30 | } 31 | 32 | @Override 33 | public void setTarget(Step to) { 34 | this.to = to; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/OrderTransition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | import org.cuner.flowframework.core.Step; 5 | 6 | /** 7 | * 顺序跳转 8 | * Created by houan on 18/7/24. 9 | */ 10 | public class OrderTransition implements Transition { 11 | 12 | private Step to; 13 | 14 | public OrderTransition(Step to) { 15 | this.to = to; 16 | } 17 | 18 | @Override 19 | public boolean match(FlowContext context) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public Step getTarget(FlowContext context) { 25 | return to; 26 | } 27 | 28 | @Override 29 | public void setTarget(Step to) { 30 | this.to = to; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/Transition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | import org.cuner.flowframework.core.Step; 5 | 6 | /** 7 | * Created by houan on 18/7/19. 8 | */ 9 | public interface Transition { 10 | 11 | /** 12 | * 是否满足跳转的条件 13 | * @param context 14 | * @return 15 | */ 16 | boolean match(FlowContext context); 17 | /** 18 | * 获取下一个要执行的节点 19 | * @param context 20 | * @return 21 | */ 22 | Step getTarget(FlowContext context); 23 | 24 | void setTarget(Step to); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/condition/AndCondition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition.condition; 2 | 3 | import org.apache.commons.collections.CollectionUtils; 4 | import org.cuner.flowframework.core.FlowContext; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by houan on 18/7/23. 10 | */ 11 | public class AndCondition implements Condition { 12 | 13 | private List conditionList; 14 | 15 | public AndCondition(List conditionList) { 16 | this.conditionList = conditionList; 17 | } 18 | 19 | @Override 20 | public boolean match(FlowContext context) { 21 | if (CollectionUtils.isEmpty(conditionList)) { 22 | return false; 23 | } 24 | 25 | for (Condition condition : conditionList) { 26 | if (!condition.match(context)) { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/condition/Condition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition.condition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | 5 | /** 6 | * Created by houan on 18/7/23. 7 | */ 8 | public interface Condition { 9 | 10 | boolean match(FlowContext context); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/condition/DefaultCondition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition.condition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | import org.cuner.flowframework.support.Comparator; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | /** 9 | * Created by houan on 18/7/23. 10 | */ 11 | public class DefaultCondition implements Condition { 12 | 13 | private String key; 14 | 15 | private String value; 16 | 17 | private Comparator.CompareType compareType; 18 | 19 | public DefaultCondition(String key, String value, Comparator.CompareType compareType) { 20 | this.key = key; 21 | this.value = value; 22 | this.compareType = compareType; 23 | } 24 | 25 | @Override 26 | public boolean match(FlowContext context) { 27 | Object data = context.getData(); 28 | if (null == data){ 29 | return false; 30 | } 31 | Class clazz = data.getClass(); 32 | try{ 33 | Field field = clazz.getDeclaredField(this.key); 34 | field.setAccessible(true); 35 | return Comparator.validate(field.get(data), this.value, this.compareType); 36 | } catch (NoSuchFieldException | IllegalAccessException e){ 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/condition/GroovyExpressCondition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition.condition; 2 | 3 | import groovy.lang.Binding; 4 | import groovy.lang.GroovyShell; 5 | import org.cuner.flowframework.core.FlowContext; 6 | 7 | /** 8 | * Created by houan on 18/7/23. 9 | */ 10 | public class GroovyExpressCondition implements Condition { 11 | 12 | private String express; 13 | 14 | public GroovyExpressCondition(String express) { 15 | this.express = express; 16 | } 17 | 18 | @Override 19 | public boolean match(FlowContext context) { 20 | Binding binding = new Binding(); 21 | binding.setProperty("data", context.getData()); 22 | binding.setProperty("parameters", context.getParameters()); 23 | 24 | GroovyShell groovyShell = new GroovyShell(binding); 25 | return (boolean) groovyShell.evaluate("if (" + this.express + ") {\n\nreturn true; } \n\nreturn false;"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/core/transition/condition/OrCondition.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.core.transition.condition; 2 | 3 | import org.cuner.flowframework.core.FlowContext; 4 | import org.springframework.util.CollectionUtils; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by houan on 18/7/23. 10 | */ 11 | public class OrCondition implements Condition { 12 | 13 | private List conditionList; 14 | 15 | public OrCondition(List conditionList) { 16 | this.conditionList = conditionList; 17 | } 18 | 19 | @Override 20 | public boolean match(FlowContext context) { 21 | if (CollectionUtils.isEmpty(conditionList)) { 22 | return false; 23 | } 24 | 25 | for (Condition condition : conditionList) { 26 | if (condition.match(context)) { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/support/Comparator.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.support; 2 | 3 | /** 4 | * Created by houan on 18/7/23. 5 | */ 6 | public class Comparator { 7 | 8 | public static boolean validate(Object left, String right, CompareType compareType) { 9 | if (null == left || null == right) { 10 | return false; 11 | } 12 | switch (compareType) { 13 | case EQ: 14 | return left.toString().equals(right); 15 | case NE: 16 | return !left.toString().equals(right); 17 | case GT: 18 | return (Integer) left - Integer.parseInt(right) > 0; 19 | case GE: 20 | return (Integer) left - Integer.parseInt(right) >= 0; 21 | case LT: 22 | return (Integer) left - Integer.parseInt(right) < 0; 23 | case LE: 24 | return (Integer) left - Integer.parseInt(right) <= 0; 25 | case IN: 26 | //全部转成字符串比较 27 | String[] values = right.split(","); 28 | if (values.length == 0){ 29 | return false; 30 | } 31 | 32 | for (String value : values){ 33 | if (left.toString().trim().equals(value.trim())){ 34 | return true; 35 | } 36 | } 37 | return false; 38 | default: 39 | return false; 40 | } 41 | } 42 | 43 | 44 | public enum CompareType { 45 | EQ("等于"), 46 | NE("不等于"), 47 | LT("小于"), 48 | LE("小于或者等于"), 49 | GT("大于"), 50 | GE("大于或者等于"), 51 | IN("包含于"); 52 | 53 | private String desc; 54 | 55 | CompareType(String desc) { 56 | this.desc = desc; 57 | } 58 | 59 | public static CompareType getCompareType(String name) { 60 | for (CompareType compareType : CompareType.values()) { 61 | if (compareType.name().equalsIgnoreCase(name)) { 62 | return compareType; 63 | } 64 | } 65 | return null; 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/support/ThreadLocalDateFormat.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.support; 2 | 3 | import java.text.SimpleDateFormat; 4 | 5 | public class ThreadLocalDateFormat extends SimpleDateFormat { 6 | private static final ThreadLocal localDateFormat = ThreadLocal.withInitial(() -> new ThreadLocalDateFormat("yyyy-MM-dd HH:mm:ss,SSS")); 7 | 8 | ThreadLocalDateFormat(String pattern) { 9 | super(pattern); 10 | } 11 | 12 | public static ThreadLocalDateFormat current() { 13 | return localDateFormat.get(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/support/log/Execution.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.support.log; 2 | 3 | import org.cuner.flowframework.support.ThreadLocalDateFormat; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.Vector; 9 | 10 | public class Execution { 11 | /** 12 | * 执行对象类型 13 | */ 14 | private ExecutionType executionType; 15 | 16 | /** 17 | * 执行对象名称 18 | */ 19 | private String name; 20 | 21 | /** 22 | * 执行开始时间 23 | */ 24 | private long startTime; 25 | 26 | /** 27 | * 执行结束事件 28 | */ 29 | private long endTime; 30 | 31 | /** 32 | * 是否异步执行 33 | */ 34 | private boolean asyn; 35 | 36 | private Vector children = new Vector<>(); 37 | 38 | public ExecutionType getExecutionType() { 39 | return executionType; 40 | } 41 | 42 | public String getNodeName() { 43 | return name; 44 | } 45 | 46 | public long getStartTime() { 47 | return startTime; 48 | } 49 | 50 | public long getEndTime() { 51 | return endTime; 52 | } 53 | 54 | public Vector getChildren() { 55 | return children; 56 | } 57 | 58 | public void setExecutionType(ExecutionType executionType) { 59 | this.executionType = executionType; 60 | } 61 | 62 | public void setName(String name) { 63 | this.name = name; 64 | } 65 | 66 | public void setStartTime(long startTime) { 67 | this.startTime = startTime; 68 | } 69 | 70 | public void setEndTime(long endTime) { 71 | this.endTime = endTime; 72 | } 73 | 74 | public boolean isAsyn() { 75 | return asyn; 76 | } 77 | 78 | public void setAsyn(boolean asyn) { 79 | this.asyn = asyn; 80 | } 81 | 82 | public String getName() { 83 | return name; 84 | } 85 | 86 | /* 87 | 构造流程执行堆栈日志 88 | */ 89 | public static String getExecutionString(Execution execution, int level) { 90 | try { 91 | StringBuilder sb = new StringBuilder(); 92 | if (level == 0) { 93 | sb.append(ThreadLocalDateFormat.current().format(new Date())); 94 | sb.append("\n\r"); 95 | } 96 | if (null != execution) { 97 | 98 | if (level > 0) { 99 | for (int i = 0; i < level - 1; i++) { 100 | sb.append(" "); 101 | } 102 | sb.append("|----"); 103 | } 104 | String start = ThreadLocalDateFormat.current().format(new Date(execution.getStartTime())); 105 | String end = ThreadLocalDateFormat.current().format(new Date(execution.getEndTime())); 106 | 107 | if (execution.isAsyn()) { 108 | sb.append("(").append(execution.getExecutionType().getType()). 109 | append(":").append(execution.getNodeName()).append(" | "). 110 | append("start:").append(start).append(" | "). 111 | append("end:").append(end).append(" | "). 112 | append("cost:").append(execution.getEndTime() - execution.getStartTime()).append(") (asynchronously)"); 113 | } else { 114 | sb.append("[").append(execution.getExecutionType().getType()). 115 | append(":").append(execution.getNodeName()).append(" | "). 116 | append("start:").append(start).append(" | "). 117 | append("end:").append(end).append(" | "). 118 | append("cost:").append(execution.getEndTime() - execution.getStartTime()).append("]"); 119 | 120 | } 121 | 122 | if (execution.getChildren() != null) { 123 | for (Execution execution1 : execution.getChildren()) { 124 | sb.append("\n\r"); 125 | sb.append(getExecutionString(execution1, level + 1)); 126 | } 127 | } 128 | } 129 | return sb.toString(); 130 | } catch (Exception e) { 131 | return "parser execution stack failed!"; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/cuner/flowframework/support/log/ExecutionType.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.support.log; 2 | 3 | public enum ExecutionType { 4 | /** 5 | * 流程 6 | */ 7 | FLOW("flow"), 8 | 9 | /** 10 | * 节点 11 | */ 12 | STEP("step"), 13 | 14 | /** 15 | * 子流程 16 | */ 17 | SUBFLOW("sub_flow"); 18 | 19 | private String type; 20 | 21 | ExecutionType(String type) { 22 | this.type = type; 23 | } 24 | 25 | public String getType() { 26 | return type; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/flow-component.xsd: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/FlowManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test; 2 | 3 | import org.cuner.flowframework.core.manager.FlowManager; 4 | import org.cuner.flowframework.test.domain.Data; 5 | import org.cuner.flowframework.test.domain.Result; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | 12 | /** 13 | * Created by houan on 18/7/26. 14 | */ 15 | @RunWith(SpringJUnit4ClassRunner.class) 16 | @ContextConfiguration(locations = {"classpath:spring.xml"}) 17 | public class FlowManagerTest { 18 | 19 | @Autowired 20 | private FlowManager flowManager; 21 | 22 | @Test 23 | public void excute() { 24 | Data data = new Data(); 25 | data.setData("test"); 26 | Result result = flowManager.execute("mainFlow", data); 27 | System.out.println("data:" + data); 28 | System.out.println("result:" + result); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/action/Action1.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test.action; 2 | 3 | import org.cuner.flowframework.core.Action; 4 | import org.cuner.flowframework.core.FlowContext; 5 | import org.cuner.flowframework.core.Step; 6 | import org.cuner.flowframework.test.domain.Data; 7 | import org.cuner.flowframework.test.domain.Result; 8 | 9 | /** 10 | * Created by houan on 18/7/26. 11 | */ 12 | public class Action1 implements Action { 13 | @Override 14 | public void execute(FlowContext context) { 15 | if (context.getResult() == null) { 16 | context.setResult(new Result()); 17 | } 18 | context.setParameter("param", 1); 19 | context.getResult().setResult(context.getData().getData()); 20 | System.out.println("----------flow: " + context.getFlowName() + " step: " + context.getStepName() + " action: " + this.getClass().getSimpleName() + "----------"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/action/Action2.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test.action; 2 | 3 | import org.cuner.flowframework.core.Action; 4 | import org.cuner.flowframework.core.FlowContext; 5 | import org.cuner.flowframework.test.domain.Data; 6 | import org.cuner.flowframework.test.domain.Result; 7 | 8 | /** 9 | * Created by houan on 18/7/26. 10 | */ 11 | public class Action2 implements Action { 12 | @Override 13 | public void execute(FlowContext context) { 14 | if (context.getResult() == null) { 15 | context.setResult(new Result()); 16 | } 17 | context.getResult().setResult(context.getData().getData()); 18 | System.out.println("----------flow: " + context.getFlowName() + " step: " + context.getStepName() + " action: " + this.getClass().getSimpleName() + "----------"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/action/Action3.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test.action; 2 | 3 | import org.cuner.flowframework.core.Action; 4 | import org.cuner.flowframework.core.FlowContext; 5 | import org.cuner.flowframework.test.domain.Data; 6 | import org.cuner.flowframework.test.domain.Result; 7 | 8 | /** 9 | * Created by houan on 18/7/26. 10 | */ 11 | public class Action3 implements Action { 12 | @Override 13 | public void execute(FlowContext context) { 14 | if (context.getResult() == null) { 15 | context.setResult(new Result()); 16 | } 17 | context.getResult().setResult(context.getData().getData()); 18 | System.out.println("----------flow: " + context.getFlowName() + " step: " + context.getStepName() + " action: " + this.getClass().getSimpleName() + "----------"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/domain/Data.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test.domain; 2 | 3 | /** 4 | * Created by houan on 18/7/26. 5 | */ 6 | public class Data { 7 | 8 | private String data; 9 | 10 | public String getData() { 11 | return data; 12 | } 13 | 14 | public void setData(String data) { 15 | this.data = data; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "Data{" + 21 | "Data='" + data + '\'' + 22 | '}'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/cuner/flowframework/test/domain/Result.java: -------------------------------------------------------------------------------- 1 | package org.cuner.flowframework.test.domain; 2 | 3 | /** 4 | * Created by houan on 18/7/26. 5 | */ 6 | public class Result { 7 | 8 | private String result; 9 | 10 | public String getResult() { 11 | return result; 12 | } 13 | 14 | public void setResult(String result) { 15 | this.result = result; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "Result{" + 21 | "result='" + result + '\'' + 22 | '}'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/flow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/test/resources/spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | flow.xml 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------