一。activiti简介html
Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操做性和云架构,提供技术实现。
创始人Tom Baeyens是JBoss jBPM的项目架构师,以及另外一位架构师Joram Barrez,一块儿加入到建立Alfresco这项首次实现Apache开源许可的BPMN 2.0引擎开发中来。
web
Activiti是一个独立运做和经营的开源项目品牌,并将独立于Alfresco开源ECM系统运行。 Activiti将是一种轻量级,可嵌入的BPM引擎,并且还设计适用于可扩展的云架构。 Activiti将提供宽松的Apache许可2.0,以便这个项目能够普遍被使用,同时促进Activiti BPM引擎和BPMN 2.0的匹配,该项目现正由OMG经过标准审定。 加入Alfresco Activiti项目的是VMware的SpringSource分支,Alfresco的计划把该项目提交给Apache基础架构,但愿吸引更多方面的BPM专家和促进BPM的创新。spring
Activiti框架用于定义业务逻辑相关的流程 ,开启相关流程实例,流程变量控制流程走向,用户及组完成流程任务
数据库
Activiti流程定义使用bpmn2.0标准 该标准经过xml定义了全部业务流程相关的节点 好比 开始 结束 用户任务 排他路由 并行路由等 因为编写xml比较困难 activiti提供了eclipse相关插件 经过绘图方式来生成xmlexpress
相关网站:eclipse插件安装(参考用户向导https://www.activiti.org/userguide/#activitiDesigner)
eclipse点击菜单 Help → Install New Software
输入如下信息:
Name:Activiti BPMN 2.0 designer
Location:http://activiti.org/designer/update/
api
点击ok 后选择赞成协议后安装完成便可 新建任意项目 新建diagram文件 操做以下gif动态图
springboot
相关概念解释
架构
如下代码就是流程引擎以及各服务的相关代码
oracle
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); RepositoryService repositoryService = processEngine.getRepositoryService(); TaskService taskService = processEngine.getTaskService(); ManagementService managementService = processEngine.getManagementService(); IdentityService identityService = processEngine.getIdentityService(); HistoryService historyService = processEngine.getHistoryService(); FormService formService = processEngine.getFormService(); DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
开始节点 全部的流程图都须要有个开始节点
框架
结束节点 全部流程图都必须有个结束节点
任务节点 须要用户处理的任务 好比审批就是须要人工去完成的一个任务 就须要定义任务节点 该节点被广泛
使用顺序线 上一个节点完成后 须要走到的下一个节点能够经过 顺序线链接
排他节点 流程须要分支须要经过排他节点 进行判断 若是有多个分支 最后流程只会走向其中一个分支
好比请假 天数<3天 走组长审批 3-5天 走项目经理审批 5天以上boss审批 最后请假了多少天 只会是某我的审批
具备排他性
并行节点 好比有两个分支 有可能两个分支流程都须要走 可使用并行节点
二。activiti配置
ProcessEngines.getDefaultProcessEngine()会在第一次调用时 初始化并建立一个流程引擎,之后再调用就会返回相同的流程引擎。 使用对应的方法能够建立和关闭全部流程引擎:ProcessEngines.init() 和 ProcessEngines.destroy()。
ProcessEngines会扫描全部activiti.cfg.xml 和 activiti-context.xml 文件。 对于activiti.cfg.xml文件,流程引擎会使用Activiti的经典方式构建: ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine(). 对于activiti-context.xml文件,流程引擎会使用Spring方法构建:先建立一个Spring的环境, 而后经过环境得到流程引擎。
全部流程相关的数据都保存在数据库中 须要先经过流程引擎配置生成数据库的表 具体的实现参考官方用户向导
(https://www.activiti.org/userguide/#_configuration)
类路径下添加 activiti.cfg.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:orcl" /> <property name="jdbcDriver" value="oracle.jdbc.driver.OracleDriver" /> <property name="jdbcUsername" value="ac" /> <property name="jdbcPassword" value="ac" /> <property name="databaseSchemaUpdate" value="true" /> </bean> </beans>添加maven依赖
<dependencies> <!-- https://mvnrepository.com/artifact/org.activiti/activiti-engine --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>5.22.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.hynnet/oracle-driver-ojdbc --> <dependency> <groupId>com.hynnet</groupId> <artifactId>oracle-driver-ojdbc</artifactId> <version>12.1.0.2</version> </dependency> </dependencies>
获取流程实例 自动建立表
package activiti; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; public class TestCreateDb { public static void main(String[] args) { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); } }
运行main方法 查看数据库 发现产生了25张表
Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。
三。activiti 实例,bpmn及经常使用api操做
如下经过一个流程实例显示 activiti的全部相关api操做
模拟实例 好比威客网站中某个雇主发布了一个单人悬赏的任务 此时不一样的威客均可以发起投稿投标的流程 流程图以下
威客开启一个流程定义流程变量(威客用户id)威客交稿的任务当前威客经过威客id能够查询到任务 威客交稿任务
开始 用户须要传递投稿的任务编号 雇主信息 稿件信息 完成任务后 雇主选稿 选稿任务完成 须要进入排他路由 选中
和未选中经过一个流程变量来控制 若是未选中 直接发送落选邮件到威客帐号 选中继续后 使用并行路由 同时开启两个流程
(发送通知邮件 ,威客提交终稿)是同时执行 互不影响 只演示前半部分 后面部分类似
bpmn的源码
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="inviteProcess" name="inviteProcess" isExecutable="true"> <startEvent id="startevent1" name="Start"></startEvent> <userTask id="wkInvite" name="威客交稿" activiti:assignee="${userWk}"></userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="wkInvite"></sequenceFlow> <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway> <serviceTask id="mailtask1" name="发送落选邮件" activiti:type="mail"> <extensionElements> <activiti:field name="to"> <activiti:expression><![CDATA[${email_to}]]></activiti:expression> </activiti:field> <activiti:field name="from"> <activiti:expression><![CDATA[${email_from}]]></activiti:expression> </activiti:field> <activiti:field name="subject"> <activiti:expression><![CDATA[${email_title}]]></activiti:expression> </activiti:field> <activiti:field name="html"> <activiti:expression><![CDATA[${email_content}]]></activiti:expression> </activiti:field> <activiti:field name="charset"> <activiti:string><![CDATA[utf-8]]></activiti:string> </activiti:field> </extensionElements> </serviceTask> <endEvent id="endevent1" name="End"></endEvent> <sequenceFlow id="flow4" sourceRef="exclusivegateway1" targetRef="mailtask1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${shortlist==0}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow5" sourceRef="mailtask1" targetRef="endevent1"></sequenceFlow> <parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway> <serviceTask id="mailtask2" name="发送中标邮件" activiti:type="mail"> <extensionElements> <activiti:field name="to"> <activiti:expression><![CDATA[${email_to}]]></activiti:expression> </activiti:field> <activiti:field name="from"> <activiti:expression><![CDATA[${email_from}]]></activiti:expression> </activiti:field> <activiti:field name="subject"> <activiti:expression><![CDATA[${email_title}]]></activiti:expression> </activiti:field> <activiti:field name="html"> <activiti:expression><![CDATA[${email_content}]]></activiti:expression> </activiti:field> <activiti:field name="charset"> <activiti:string><![CDATA[utf-8]]></activiti:string> </activiti:field> </extensionElements> </serviceTask> <sequenceFlow id="flow6" sourceRef="exclusivegateway1" targetRef="parallelgateway1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${shortlist==1}]]></conditionExpression> </sequenceFlow> <userTask id="usertask1" name="提交最终稿件" activiti:assignee="${userWk}"></userTask> <sequenceFlow id="flow7" sourceRef="parallelgateway1" targetRef="mailtask2"></sequenceFlow> <sequenceFlow id="flow8" sourceRef="parallelgateway1" targetRef="usertask1"></sequenceFlow> <userTask id="usertask2" name="雇主确认" activiti:assignee="${employer}"></userTask> <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway> <sequenceFlow id="flow9" sourceRef="exclusivegateway2" targetRef="usertask1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${ifaccept==0}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flow10" sourceRef="usertask2" targetRef="exclusivegateway2"></sequenceFlow> <sequenceFlow id="flow11" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> <userTask id="usertask3" name="威客收款" activiti:assignee="${userWk}"></userTask> <sequenceFlow id="flow12" sourceRef="exclusivegateway2" targetRef="usertask3"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${ifaccept==1}]]></conditionExpression> </sequenceFlow> <endEvent id="endevent2" name="End"></endEvent> <sequenceFlow id="flow13" sourceRef="usertask3" targetRef="endevent2"></sequenceFlow> <endEvent id="endevent3" name="End"></endEvent> <sequenceFlow id="flow14" sourceRef="mailtask2" targetRef="endevent3"></sequenceFlow> <userTask id="usertask4" name="雇主选稿" activiti:assignee="${employer}"></userTask> <sequenceFlow id="flow15" sourceRef="wkInvite" targetRef="usertask4"></sequenceFlow> <sequenceFlow id="flow16" sourceRef="usertask4" targetRef="exclusivegateway1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_inviteProcess"> <bpmndi:BPMNPlane bpmnElement="inviteProcess" id="BPMNPlane_inviteProcess"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="0.0" y="172.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="wkInvite" id="BPMNShape_wkInvite"> <omgdc:Bounds height="55.0" width="105.0" x="60.0" y="162.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1"> <omgdc:Bounds height="40.0" width="40.0" x="300.0" y="169.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="mailtask1" id="BPMNShape_mailtask1"> <omgdc:Bounds height="55.0" width="105.0" x="380.0" y="216.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="415.0" y="380.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="parallelgateway1" id="BPMNShape_parallelgateway1"> <omgdc:Bounds height="40.0" width="40.0" x="380.0" y="110.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="mailtask2" id="BPMNShape_mailtask2"> <omgdc:Bounds height="55.0" width="105.0" x="470.0" y="56.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> <omgdc:Bounds height="55.0" width="105.0" x="470.0" y="135.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="640.0" y="135.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2"> <omgdc:Bounds height="40.0" width="40.0" x="672.0" y="223.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> <omgdc:Bounds height="55.0" width="105.0" x="640.0" y="300.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent2" id="BPMNShape_endevent2"> <omgdc:Bounds height="35.0" width="35.0" x="675.0" y="414.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent3" id="BPMNShape_endevent3"> <omgdc:Bounds height="35.0" width="35.0" x="670.0" y="66.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4"> <omgdc:Bounds height="55.0" width="105.0" x="180.0" y="164.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="35.0" y="189.0"></omgdi:waypoint> <omgdi:waypoint x="60.0" y="189.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="320.0" y="209.0"></omgdi:waypoint> <omgdi:waypoint x="320.0" y="243.0"></omgdi:waypoint> <omgdi:waypoint x="380.0" y="243.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"> <omgdi:waypoint x="432.0" y="271.0"></omgdi:waypoint> <omgdi:waypoint x="432.0" y="380.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6"> <omgdi:waypoint x="320.0" y="169.0"></omgdi:waypoint> <omgdi:waypoint x="320.0" y="138.0"></omgdi:waypoint> <omgdi:waypoint x="353.0" y="138.0"></omgdi:waypoint> <omgdi:waypoint x="388.0" y="138.0"></omgdi:waypoint> <omgdi:waypoint x="388.0" y="138.0"></omgdi:waypoint> <omgdi:waypoint x="420.0" y="130.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"> <omgdi:waypoint x="400.0" y="110.0"></omgdi:waypoint> <omgdi:waypoint x="400.0" y="83.0"></omgdi:waypoint> <omgdi:waypoint x="470.0" y="83.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8"> <omgdi:waypoint x="400.0" y="150.0"></omgdi:waypoint> <omgdi:waypoint x="400.0" y="162.0"></omgdi:waypoint> <omgdi:waypoint x="470.0" y="162.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9"> <omgdi:waypoint x="692.0" y="223.0"></omgdi:waypoint> <omgdi:waypoint x="522.0" y="190.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10"> <omgdi:waypoint x="692.0" y="190.0"></omgdi:waypoint> <omgdi:waypoint x="692.0" y="223.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11"> <omgdi:waypoint x="575.0" y="162.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="162.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12"> <omgdi:waypoint x="692.0" y="263.0"></omgdi:waypoint> <omgdi:waypoint x="692.0" y="300.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow13" id="BPMNEdge_flow13"> <omgdi:waypoint x="692.0" y="355.0"></omgdi:waypoint> <omgdi:waypoint x="692.0" y="414.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow14" id="BPMNEdge_flow14"> <omgdi:waypoint x="575.0" y="83.0"></omgdi:waypoint> <omgdi:waypoint x="670.0" y="83.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow15" id="BPMNEdge_flow15"> <omgdi:waypoint x="165.0" y="189.0"></omgdi:waypoint> <omgdi:waypoint x="180.0" y="191.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow16" id="BPMNEdge_flow16"> <omgdi:waypoint x="285.0" y="191.0"></omgdi:waypoint> <omgdi:waypoint x="300.0" y="189.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>演示代码以下
//建立流程引擎对象 private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
步骤1 发布流程定义
/** * 发布流程 添加发布bpmn和图片 也能够打包成 zip发布 * */ @Test public void deployProcess() { //用于发布流程资源 以及流程定义 RepositoryService repositoryService = processEngine.getRepositoryService(); Deployment deploy = repositoryService.createDeployment() .addClasspathResource("diagrams/MyProcess.bpmn") .addClasspathResource("diagrams/MyProcess.png") //.addZipInputStream(zipInputStream) 能够添加压缩包 web定义流程就可使用这个方法上传 .name("威客单人投标流程") .deploy(); //资源文件在act_ge_bytearray表中 //流程定义在act_re_procdef表中 System.out.println("流程定义id:"+deploy.getId()); System.out.println("流程定义名称:"+deploy.getName()); System.out.println("流程定义发布时间:"+deploy.getDeploymentTime().toGMTString()); //查看发布的流程信息 查看发布的流程总和 long count = repositoryService.createDeploymentQuery().count(); System.out.println("总流程定义数:"+count); }步骤2 zs查看本身的任务编号 完成投稿任务
/** * 运行流程 * 相同的流程被发布(id不一样 _key相同)多, 每发布 一次 ACT_RE_PROCDEF 多一条数据, Version都会加1, * 根据key来启动 会启动最新版本号的流程定义 * 若是想执行旧的流程定义就可使用id来启动 * 模拟场景 某个威客 开始向某个任务投稿 任务就开始了 */ @Test public void startProcess() { //运行流程使用runtimesErvice RuntimeService runtimeService = processEngine.getRuntimeService(); //流程变量 传入是谁启动的流程 Map map=new HashMap(); map.put("userWk", "zs"); //key就是流程图的 id (注意不是数据库里面的id 数据库里面的key是流程里开始定义的id) //流程实例数据在 ACT_RU_EXECUTION表中 //流程变量 在 ACT_RU_VARIABLE中 //用户任务在 ACT_RU_TASK中 ProcessInstance instance= runtimeService.startProcessInstanceByKey("inviteProcess",map); System.out.println("用户"+map.get("userWk")+"开启了一个投稿流程"); System.out.println("流程实例编号是:"+instance.getProcessInstanceId()); long count = runtimeService.createExecutionQuery().count(); System.out.println("总流程实例数:"+count); }
完成任务
/** * 张三完成任务 就须要交稿 定义流程变量 */ @Test public void submitTask() { //用户任务的完成 使用TaskService TaskService taskService = processEngine.getTaskService(); String taskId="20005"; Map map=new HashMap(); /** * 张三交稿数据 * **/ map.put("employer", "ls");//设置任务的雇主 map.put("taskNo", "雇主任务编号"); map.put("taskContent", "用字符串模拟"); taskService.complete(taskId, map); }
步骤3 雇主ls查看本身的任务编号 完成选稿稿任务 这里模拟shortlist==0表示没选中 进入发送邮件结束流程的步骤
须要配置邮件的smtp和用户密码信息 修改activiti.cfg.xml 添加
<property name="mailServerHost" value="smtp.126.com" /> <property name="mailServerPort" value="25" /> <property name="mailServerUsername" value="lixin1112003@126.com"></property> <property name="mailServerPassword" value="XXXXXXXXXX您的邮箱密码"></property>未选中代码
/** * 李四完成任务 就须要选稿 */ @Test public void submitTask() { //用户任务的完成 使用TaskService TaskService taskService = processEngine.getTaskService(); String taskId="20005"; Map map=new HashMap(); map.put("shortlist", 0); map.put("email_to", "973465719@qq.com"); map.put("email_from", "lixin1112003@126.com"); map.put("email_title", "973465719@qq.com"); map.put("email_content", "您对雇主任务编号"+taskService.getVariable(taskId, "taskNo")+"的投稿稿件 已经落选。。"); taskService.complete(taskId, map); }选中代码
/** * 李四完成任务 就须要选稿 */ @Test public void submitTask() { //用户任务的完成 使用TaskService TaskService taskService = processEngine.getTaskService(); String taskId="20005"; Map map=new HashMap(); map.put("shortlist", 1); map.put("email_to", "973465719@qq.com"); map.put("email_from", "lixin1112003@126.com"); map.put("email_title", "973465719@qq.com"); map.put("email_content", "您对雇主任务编号"+taskService.getVariable(taskId, "taskNo")+"的投稿稿件 已经被选中 请完成终稿。。"); taskService.complete(taskId, map); }
其余代码就不演示 具体参考https://www.activiti.org/userguide/#chapterApi
四。springboot集成activiti
集成web参考 https://www.activiti.org/userguide/#springintegration