Activiti 5.15 用户手册


Table of Contents

1. 简介
协议
下载
源码
必要的软件
JDK 6+
Eclipse Indigo 和 Juno
报告问题
试验性功能
内部实现类
2. 开始学习
一分钟入门
安装Activiti
安装Activiti数据库
引入Activiti jar和依赖
下一步
3. 配置
创建ProcessEngine
ProcessEngineConfiguration bean
数据库配置
支持的数据库
创建数据库表
理解数据库表的命名
数据库升级
启用Job执行器
配置邮件服务器
配置历史
为表达式和脚本暴露配置
配置部署缓存
日志
映射诊断上下文
事件处理
事件监听器实现
配置与安装
在运行阶段添加监听器
为流程定义添加监听器
通过API分发事件
支持的事件类型
附加信息
4. Activiti API
流程引擎的API和服务
异常策略
使用Activiti的服务
发布流程
启动一个流程实例
完成任务
挂起,激活一个流程
更多知识
查询API
表达式
单元测试
调试单元测试
web应用中的流程引擎
5. Spring集成
ProcessEngineFactoryBean
事务
表达式
资源的自动部署
单元测试
基于注解的配置
JPA 和 Hibernate 4.2.x
6. 部署
业务文档
编程式部署
通过Activiti Explorer控制台部署
外部资源
Java类
在流程中使用Spring beans
创建独立应用
流程定义的版本
提供流程图片
自动生成流程图片
类别
7. BPMN 2.0介绍
啥是BPMN?
定义一个流程
快速起步:10分钟教程
前提
目标
用例
流程图
XML内容
启动一个流程实例
任务列表
领取任务
完成任务
结束流程
代码总结
更多思考
8. BPMN 2.0结构
自定义扩展
事件(Event)
事件定义
定时器事件定义
错误事件定义
信号事件定义
消息事件定义
开始事件
空开始事件
定时开始事件
消息开始事件
信号开始事件
错误开始事件
结束事件
空结束事件
错误结束事件
取消结束事件
边界事件
定时边界事件
错误边界事件
信号边界事件
消息边界事件
取消边界事件
补偿边界事件
中间捕获事件
定时中间捕获事件
信号中间捕获事件
消息中间捕获事件
内部触发事件
中间触发空事件
信号中间触发事件
补偿中间触发事件
顺序流
描述
图形标记
XML内容
条件顺序流
默认顺序流
网关
排他网关
并行网关
包含网关
基于事件网关
任务
用户任务
脚本任务
Java服务任务
Web Service任务
业务规则任务
邮件任务
Mule任务
Camel任务
手工任务
Java接收任务
Shell任务
执行监听器
任务监听器
多实例(循环)
补偿处理器
子流程和调用节点
子流程
事件子流程
事务子流程
调用活动(子流程)
事务和并发
异步操作
排他任务
流程实例授权
数据对象
9. 表单
表单属性
外置表单的渲染
10. JPA
要求
配置
用法
简单例子
查询JPA流程变量
使用Spring beans和JPA结合的高级例子
11. 历史
查询历史
HistoricProcessInstanceQuery
HistoricVariableInstanceQuery
HistoricActivityInstanceQuery
HistoricDetailQuery
HistoricTaskInstanceQuery
历史配置
审计目的的历史
12. Eclipse Designer
Installation
Activiti Designer 编辑器的特性
Activiti Designer 的BPMN 特性
Activiti Designer 部署特性
扩展Activiti Designer
定制画板
校验图形和导出到自定义的输出格式
13. Activiti Explorer
流程图
任务
启动流程实例
我的流程实例
管理
报表
报告数据JSON
实例流程
报告开始表单
流程例子
修改数据库
14. Activiti Modeler
编辑模型
导入模型
把发布的流程定义转换成可编辑的模型
把模型导出成BPMN XML
把模型部署到Activiti引擎中
15. REST API
通用Activiti REST原则
安装与认证
使用Tomcat
方法和返回值
错误响应体
请求参数
部署
部署列表
获得一个部署
创建新部署
删除部署
列出部署内的资源
获取部署资源
获取部署资源的内容
流程定义
流程定义列表
获得一个流程定义
更新流程定义的分类
获得一个流程定义的资源内容
获得流程定义的BPMN模型
暂停流程定义
激活流程定义
获得流程定义的所有候选启动者
为流程定义添加一个候选启动者
删除流程定义的候选启动者
获得流程定义的一个候选启动者
模型
获得模型列表
获得一个模型
更新模型
新建模型
删除模型
获得模型的可编译源码
设置模型的可编辑源码
获得模型的附加可编辑源码
设置模型的附加可编辑源码
流程实例
获得流程实例
删除流程实例
激活或挂起流程实例
启动流程实例
显示流程实例列表
查询流程实例
获得流程实例的流程图
获得流程实例的参与者
为流程实例添加一个参与者
删除一个流程实例的参与者
列出流程实例的变量
获得流程实例的一个变量
创建(或更新)流程实例变量
更新一个流程实例变量
创建一个新的二进制流程变量
更新一个二进制的流程实例变量
分支
获取一个分支
对分支执行操作
获得一个分支的所有活动节点
获取分支列表
查询分支
获取分支的变量列表
获得分支的一个变量
新建(或更新)分支变量
更新分支变量
创建一个二进制变量
更新已经已存在的二进制分支变量
任务
获取任务
任务列表
查询任务
更新任务
操作任务
删除任务
获得任务的变量
获取任务的一个变量
获取变量的二进制数据
创建任务变量
创建二进制任务变量
更新任务的一个已有变量
更新一个二进制任务变量
删除任务变量
删除任务的所有局部变量
获得任务的所有IdentityLink
获得一个任务的所有组或用户的IdentityLink
获得一个任务的一个IdentityLink
为任务创建一个IdentityLink
删除任务的一个IdentityLink
为任务创建评论
获得任务的所有评论
获得任务的一个评论
删除任务的一条评论
获得任务的所有事件
获得任务的一个事件
为任务创建一个附件,包含外部资源的链接
为任务创建一个附件,包含附件文件
获得任务的所有附件
获得任务的一个附件
获取附件的内容
删除任务的一个附件
历史
获得历史流程实例
历史流程实例列表
查询历史流程实例
删除历史流程实例
获取历史流程实例的IdentityLink
获取历史流程实例变量的二进制数据
为历史流程实例创建一条新评论
获得一个历史流程实例的所有评论
获得历史流程实例的一条评论
删除历史流程实例的一条评论
获得单独历史任务实例
获取历史任务实例
查询历史任务实例
删除历史任务实例
获得历史任务实例的IdentityLink
获取历史任务实例变量的二进制值
获取历史活动实例
查询历史活动实例
列出历史变量实例
查询历史变量实例
获取历史任务实例变量的二进制值
获取历史细节
查询历史细节
获取历史细节变量的二进制数据
表单
获取表单数据
提交任务表单数据
数据库表
表列表
获得一张表
获得表的列信息
获得表的行数据
引擎
获得引擎属性
获得引擎信息
运行时
接收信号事件
作业
获取一个作业
删除作业
执行作业
获得作业的异常堆栈
获得作业列表
用户
获得一个用户
获取用户列表
更新用户
创建用户
删除用户
获取用户图片
更新用户图片
列出用户列表
获取用户信息
更新用户的信息
创建用户信息条目
删除用户的信息
群组
获得群组
获取群组列表
更新群组
创建群组
删除群组
获取群组的成员
为群组添加一个成员
删除群组的成员
传统REST - 通用方法
资源
上传发布
获取发布
获取发布资源
获取发布的一个资源
删除发布
删除发布
引擎
获取流程引擎
流程
流程定义列表
获得流程定义表单属性
获得流程定义表单资源
获取流程定义图
启动流程实例
流程实例列表
获得流程实例细节
获得流程实例图
获得流程实例的任务
继续特定流程实例的活动(receiveTask)
触发特定流程实例的信号
任务
获得任务简介
任务列表
获取任务
获取任务表单
执行任务操作
表单属性列表
为任务添加一个附件
获得任务附件
为任务添加一个url
身份
登录
获得用户
列出用户的群组
查询用户
创建用户
为群组添加用户
从群组删除用户
获得用户图片
获得群组
群组用户列表
查询群组
创建群组
为群组添加用户
为群组删除用户
管理
作业列表
获得作业
执行一个作业
执行多个作业
数据库表列表
获得表元数据
获得表数据
16. 集成CDI
设置activiti-cdi
查找流程引擎
配置Process Engine
发布流程
基于CDI环境的流程执行
与流程实例进行关联交互
声明式流程控制
在流程中引用bean
使用@BusinessProcessScoped beans
注入流程变量
接收流程事件
更多功能
已知的问题
17. 集成LDAP
用法
用例
配置
属性
为Explorer集成LDAP
18. 高级功能
监听流程解析
支持高并发的UUID id生成器
多租户
执行自定义SQL
使用ProcessEngineConfigurator实现高级流程引擎配置
启用安全的BPMN 2.0 xml

List of Tables

2.1. 示例用户
2.2. webapp工具
3.1. 支持的数据库
3.2. 支持的事件
6.1.
6.2.
6.3.
8.1. 邮件服务器配置
8.2. 邮件服务器配置
8.3. Mule服务器配置
8.4. 终端URL:
8.5. 已有的camel行为:
8.6. 已有的camel行为:
8.7. Shell任务参数配置
15.1. HTTP方法和对应操作
15.2. HTTP方法响应代码
15.3. URL查询参数类型
15.4. JSON参数类型
15.5. 查询JSON参数
15.6. 查询JSON参数
15.7. 默认查询JSON类型
15.8. 变量JSON属性
15.9. 变量类型
15.10. URL查询参数
15.11. REST响应码
15.12. 获得一个部署 - URL参数
15.13. 获得一个部署 - 响应码
15.14. 创建新部署 - 响应码
15.15. 删除部署 - URL参数
15.16. 删除部署 - 响应码
15.17. 列出部署内的资源 - URL参数
15.18. 列出部署内的资源 - 响应码
15.19. 获取部署资源 - URL参数
15.20. 获取部署资源 - 响应码
15.21. 获取部署资源的内容 - URL参数
15.22. 获取部署资源的内容 - 响应码
15.23. 流程定义列表 - URL参数
15.24. 流程定义列表 - 响应码
15.25. 获得一个流程定义 - URL参数
15.26. 获得一个流程定义 - 响应码
15.27. 更新流程定义的分类 - 响应码
15.28. 获得一个流程定义的资源内容 - URL参数
15.29. 获得流程定义的BPMN模型 - URL参数
15.30. 获得流程定义的BPMN模型 - 响应码
15.31. 暂停流程定义 - 请求的JSON参数
15.32. 暂停流程定义 - 响应码
15.33. 激活流程定义 - 响应码
15.34. 获得流程定义的所有候选启动者 - URL参数
15.35. 获得流程定义的所有候选启动者 - 响应码
15.36. 为流程定义添加一个候选启动者 - URL参数
15.37. 为流程定义添加一个候选启动者 - 响应码
15.38. 删除流程定义的候选启动者 - URL参数
15.39. 删除流程定义的候选启动者 - 响应码
15.40. 获得流程定义的一个候选启动者 - URL参数
15.41. 获得流程定义的一个候选启动者 - 响应码
15.42. 获得模型列表 - URL参数
15.43. 获得模型列表 - 响应码
15.44. 获得一个模型 - URL参数
15.45. 获得一个模型 - 响应码
15.46. 更新模型 - 响应码
15.47. 新建模型 - 响应码
15.48. 删除模型 - URL参数
15.49. 删除模型 - 响应码
15.50. 获得模型的可编译源码 - URL参数
15.51. 获得模型的可编译源码 - 响应码
15.52. 设置模型的可编辑源码 - URL参数
15.53. 设置模型的可编辑源码 - 响应码
15.54. 获得模型的附加可编辑源码 - URL参数
15.55. 获得模型的附加可编辑源码 - 响应码
15.56. 设置模型的附加可编辑源码 - URL参数
15.57. 设置模型的附加可编辑源码 - 响应码
15.58. 获得流程实例 - URL参数
15.59. 获得流程实例 - 响应码
15.60. 删除流程实例 - URL参数
15.61. 删除流程实例 - 响应码
15.62. 激活或挂起流程实例 - URL参数
15.63. 激活或挂起流程实例 - 响应码
15.64. 启动流程实例 - 响应码
15.65. 显示流程实例列表 - URL参数
15.66. 显示流程实例列表 - 响应码
15.67. 查询流程实例 - 响应码
15.68. 获得流程实例的流程图 - URL参数
15.69. 获得流程实例的流程图 - 响应码
15.70. 获得流程实例的参与者 - URL参数
15.71. 获得流程实例的参与者 - 响应码
15.72. 为流程实例添加一个参与者 - URL参数
15.73. 为流程实例添加一个参与者 - 响应码
15.74. 删除一个流程实例的参与者 - URL参数
15.75. 删除一个流程实例的参与者 - 响应码
15.76. 列出流程实例的变量 - URL参数
15.77. 列出流程实例的变量 - 响应码
15.78. 获得流程实例的一个变量 - URL参数
15.79. 获得流程实例的一个变量 - 响应码
15.80. 创建(或更新)流程实例变量 - URL参数
15.81. 创建(或更新)流程实例变量 - 响应码
15.82. 更新一个流程实例变量 - URL参数
15.83. 更新一个流程实例变量 - 响应码
15.84. 创建一个新的二进制流程变量 - URL参数
15.85. 创建一个新的二进制流程变量 - 响应码
15.86. 更新一个二进制的流程实例变量 - URL参数
15.87. 更新一个二进制的流程实例变量 - 响应码
15.88. 获取一个分支 - URL参数
15.89. 获取一个分支 - 响应码
15.90. 对分支执行操作 - URL参数
15.91. 对分支执行操作 - 响应码
15.92. 获得一个分支的所有活动节点 - URL参数
15.93. 获得一个分支的所有活动节点 - 响应码
15.94. 获取分支列表 - URL参数
15.95. 获取分支列表 - 响应码
15.96. 查询分支 - 响应码
15.97. 获取分支的变量列表 - URL参数
15.98. 获取分支的变量列表 - 响应码
15.99. 获得分支的一个变量 - URL参数
15.100. 获得分支的一个变量 - 响应码
15.101. 新建(或更新)分支变量 - URL参数
15.102. 新建(或更新)分支变量 - 响应码
15.103. 更新分支变量 - URL参数
15.104. 更新分支变量 - 响应码
15.105. 创建一个二进制变量 - URL参数
15.106. 创建一个二进制变量 - 响应码
15.107. 更新已经已存在的二进制分支变量 - URL参数
15.108. 更新已经已存在的二进制分支变量 - 响应码
15.109. 获取任务 - URL参数
15.110. 获取任务 - 响应码
15.111. 任务列表 - URL参数
15.112. 任务列表 - 响应码
15.113. 查询任务 - 响应码
15.114. 更新任务 - 响应码
15.115. 操作任务 - 响应码
15.116. >删除任务 - URL参数
15.117. >删除任务 - 响应码
15.118. 获得任务的变量 - URL参数
15.119. 获得任务的变量 - 响应码
15.120. 获取任务的一个变量 - URL参数
15.121. 获取任务的一个变量 - 响应码
15.122. 获取变量的二进制数据 - URL参数
15.123. 获取变量的二进制数据 - 响应码
15.124. 创建任务变量 - URL参数
15.125. 创建任务变量 - 响应码
15.126. 创建二进制任务变量 - URL参数
15.127. 创建二进制任务变量 - 响应码
15.128. 更新任务的一个已有变量 - URL参数
15.129. 更新任务的一个已有变量 - 响应码
15.130. 更新一个二进制任务变量 - URL参数
15.131. 更新一个二进制任务变量 - 响应码
15.132. 删除任务变量 - URL参数
15.133. 删除任务变量 - 响应码
15.134. 删除任务的所有局部变量 - URL参数
15.135. 删除任务的所有局部变量 - 响应码
15.136. 获得任务的所有IdentityLink - URL参数
15.137. 获得任务的所有IdentityLink - 响应码
15.138. 获得一个任务的所有组或用户的IdentityLink - URL参数
15.139. 获得一个任务的所有组或用户的IdentityLink - 响应码
15.140. 为任务创建一个IdentityLink - URL参数
15.141. 为任务创建一个IdentityLink - 响应码
15.142. 删除任务的一个IdentityLink - URL参数
15.143. 删除任务的一个IdentityLink - 响应码
15.144. 为任务创建评论 - URL参数
15.145. 为任务创建评论 - 响应码
15.146. 获得任务的所有评论 - URL参数
15.147. 获得任务的所有评论 - 响应码
15.148. 获得任务的一个评论 - URL参数
15.149. 获得任务的一个评论 - 响应码
15.150. 删除任务的一条评论 - URL参数
15.151. 删除任务的一条评论 - 响应码
15.152. 获得任务的所有事件 - URL参数
15.153. 获得任务的所有事件 - 响应码
15.154. 获得任务的一个事件 - URL参数
15.155. 获得任务的一个事件 - 响应码
15.156. 为任务创建一个附件,包含外部资源的链接 - URL参数
15.157. 为任务创建一个附件,包含外部资源的链接 - 响应码
15.158. 为任务创建一个附件,包含附件文件 - URL参数
15.159. 为任务创建一个附件,包含附件文件 - 响应码
15.160. 获得任务的所有附件 - URL参数
15.161. 获得任务的所有附件 - 响应码
15.162. 获得任务的一个附件 - URL参数
15.163. 获得任务的一个附件 - 响应码
15.164. 获取附件的内容 - URL参数
15.165. 获取附件的内容 - 响应码
15.166. 删除任务的一个附件 - URL参数
15.167. 删除任务的一个附件 - 响应码
15.168. 获得历史流程实例 - 响应码
15.169. 历史流程实例列表 - URL参数
15.170. 历史流程实例列表 - 响应码
15.171. 查询历史流程实例 - 响应码
15.172. 响应码
15.173. 响应码
15.174. 获取历史流程实例变量的二进制数据 - 响应码
15.175. 为历史流程实例创建一条新评论 - URL参数
15.176. 为历史流程实例创建一条新评论 - 响应码
15.177. 获得流程实例的所有评论 - URL参数
15.178. 获得流程实例的所有评论 - 响应码
15.179. 获得历史流程的一条评论 - URL参数
15.180. 获得历史流程的一条评论 - 响应码
15.181. 删除历史流程实例的一条评论 - URL参数
15.182. 删除历史流程实例的一条评论 - 响应码
15.183. 获得单独历史任务实例 - 响应码
15.184. 获取历史任务实例 - URL参数
15.185. 获取历史任务实例 - 响应码
15.186. 查询历史任务实例 - 响应码
15.187. 响应码
15.188. 响应码
15.189. 获取历史任务实例变量的二进制值 - 响应码
15.190. 获取历史活动实例 - URL参数
15.191. 获取历史活动实例 - 响应码
15.192. 查询历史活动实例 - 响应码
15.193. 列出历史变量实例 - URL参数
15.194. 列出历史变量实例 - 响应码
15.195. 查询历史变量实例 - 响应码
15.196. 获取历史任务实例变量的二进制值 - 响应码
15.197. 获取历史细节 - URL参数
15.198. 获取历史细节 - 响应码
15.199. 查询历史细节 - 响应码
15.200. 获取历史细节变量的二进制数据 - 响应码
15.201. 获取表单数据 - URL参数
15.202. 获取表单数据 - 响应码
15.203. 提交任务表单数据 - 响应码
15.204. 表列表 - 响应码
15.205. 获得一张表 - URL参数
15.206. 获得一张表 - 响应码
15.207. 获得表的列信息 - URL参数
15.208. 获得表的列信息 - 响应码
15.209. 获得表的行数据 - URL参数
15.210. 获得表的行数据 - URL参数
15.211. 获得表的行数据 - 响应码
15.212. 获得引擎属性 - 响应码
15.213. 获得引擎信息 - 响应码
15.214. 接收信号事件 - JSON体参数
15.215. 接收信号事件 - 响应码
15.216. 获取一个作业 - URL参数
15.217. 获取一个作业 - 响应码
15.218. 删除作业 - URL参数
15.219. 删除作业 - 响应码
15.220. 执行作业 - 请求的JSON参数
15.221. 执行作业 - 响应码
15.222. 获得作业的异常堆栈 - URL参数
15.223. 获得作业的异常堆栈 - 响应码
15.224. 获得作业列表 - URL参数
15.225. 获得作业列表 - 响应码
15.226. 获得一个用户 - URL参数
15.227. 获得一个用户 - 响应码
15.228. 获取用户列表 - URL参数
15.229. 获取用户列表 - 响应码
15.230. 更新用户 - 响应码
15.231. 创建用户 - 响应码
15.232. 删除用户 - URL参数
15.233. 删除用户 - 响应码
15.234. 获取用户图片 - URL参数
15.235. 获取用户图片 - 响应码
15.236. 更新用户图片 - URL参数
15.237. 更新用户图片 - 响应码
15.238. 列出用户列表 - URL参数
15.239. 列出用户列表 - 响应码
15.240. 获取用户信息 - URL参数
15.241. 获取用户信息 - 响应码
15.242. 更新用户的信息 - URL参数
15.243. 更新用户的信息 - 响应码
15.244. 创建用户信息条目 - URL参数
15.245. 创建用户信息条目 - 响应码
15.246. 删除用户的信息 - URL参数
15.247. 删除用户的信息 - 响应码
15.248. 获得群组 - URL参数
15.249. 获得群组 - 响应码
15.250. 获取群组列表 - URL参数
15.251. 获取群组列表 - 响应码
15.252. 更新群组 - 响应码
15.253. 创建群组 - 响应码
15.254. 删除群组 - URL参数
15.255. 删除群组 - 响应码
15.256. 为群组添加一个成员 - URL参数
15.257. 为群组添加一个成员 - 响应码
15.258. 删除群组的成员 - URL参数
15.259. 删除群组的成员 - 响应码
17.1. LDAP配置属性
17.2. 高级属性

Chapter 1. 简介

协议

Activiti是基于Apache V2协议发布的。

源码

发布包里包含大部分的已经打好jar包的源码。 如果想找到并构建完整的源码库,请参考 wiki “构建发布包”

必要的软件

JDK 6+

Activiti需要运行在JDK 6或以上版本上。 进入 Oracle Java SE 下载页面 点击 "下载 JDK"按钮。页面上也提供了安装的方法。 为了验证是否安装成功,可以在命令行中执行 java -version。 它将会打印出安装的JDK的版本。

Eclipse Indigo 和 Juno

(译者注:Eclipse 3.7 版本代号 Indigo 靛青, Eclipse 4.2 版本代号 Juno 朱诺)。 在Eclipse下载页面下载你选择的eclipse发布包。 解压下载文件,你就可以通过eclipse目录下的eclipse文件启动它。 此外,在该用户指南后面,专门有一章介绍安装eclipse设计器插件

报告问题

任何一个自觉的开发者都应该看看 如何聪明的提出问题

看完之后,你可以在用户论坛上进行提问和评论, 或者在JIRA问题跟踪系统中创建问题。

Note

虽然Activiti已经托管在GitHub上了,但是问题不应该提交到GitHub的问题跟踪系统上。如果你想报告一个问题, 不要创建一个GitHub的问题,而是应该使用JIRA

试验性功能

那些标记着 [EXPERIMENTAL] 的章节表示功能尚未稳定。

所有包名中包含 .impl. 的类都是内部实现类,都是不保证稳定的。 不过,如果用户指南把哪些类列为配置项,那么它们可以认为是稳定不变的。

内部实现类

在jar包中,所有包名中包含.impl.(比如:org.activiti.engine.impl.pvm.delegate)的类都是实现类, 它们应该被视为流程引擎内部的类。对于这些类和接口都不能够保证其稳定性。

Chapter 2. 开始学习

一分钟入门

Activiti网站下载Activiti Explorer的WAR文件后, 可以按照下列步骤以默认配置运行样例。 你需要一个Java 运行环境Apache Tomcat (其实,任何提供了servlet功能的web容器都可以正常运行。但是我们主要是使用tomcat进行的测试)。

  • 把下载的activiti-explorer.war复制到Tomcat的webapps目录下。

  • 执行Tomcat的bin目录下的startup.bat或startup.sh启动服务器。

  • Tomcat启动后,打开浏览器访问http://localhost:8080/activiti-explorer。 使用kermit/kermit登录。

这样就好了!Activiti Explorer默认使用H2内存数据库,如果你想使用其他数据库 请参考这里

安装Activiti

要安装Activiti你需要一个 Java运行环境Apache Tomcat。 还要确认设置好JAVA_HOME系统变量。 不同的操作系统下的设置方法是不同的。

要运行Activiti Explorer和REST web应用,你要从Activiti的下载页下载WAR文件, 复制到Tomcat安装目录下webapps目录下。 默认Explorer应用使用的内存数据库已经包含了示例流程,用户和群组信息。

下面是示例中可以使用的用户:

Table 2.1. 示例用户

账号密码角色
kermitkermitadmin
gonzogonzomanager
fozziefozzieuser

现在,你可以访问下列web应用:

Table 2.2. webapp工具

Webapp名称URL描述 
Activiti Explorerhttp://localhost:8080/activiti-explorer 流程引擎的用户控制台。使用它来启动新流程,分配任务, 查看并认领任务,等等。这个工具也可以用来管理Activiti引擎。  

注意Activiti Explorer演示实例只是一种简单快速展示Activiti的功能的方式。 但是并不是说只能使用这种方式使用Activiti。 Activiti只是一个jar, 可以内嵌到任何Java环境中:swing或者Tomcat, JBoss, WebSphere等等。 也可以把Activiti作为一个典型的单独运行的BPM服务器运行。 只要java可以做的,Activiti也可以。

安装Activiti数据库

就像在一分钟入门里说过的,Activiti Explorer默认使用H2内存数据库。 要让Activiti使用独立运行的H2数据库或者其他数据库, 可以修改Activiti Explorer web应用WEB-INF/classes目录下的db.properties。

另外,注意Activiti Explorer自动生成了演示用的默认用户和群组,流程定义,数据模型。 要想禁用这个功能,要修改WEB-INF目录下的activiti-standalone-context.xml。 可以使用下面的demoDataGenerator bean定义代码完全禁用安装默认数据。从代码中也可以看出,我们可以单独启用或禁用每一项功能。

      <bean id="demoDataGenerator" class="org.activiti.explorer.demo.DemoDataGenerator">
        <property name="processEngine" ref="processEngine" />
        <property name="createDemoUsersAndGroups" value="false" />
        <property name="createDemoProcessDefinitions" value="false" />
        <property name="createDemoModels" value="false" />
      </bean>
    

引入Activiti jar和依赖

为了引用Activiti jar和依赖,我们推荐使用 Maven(或Ivy), 它简化了我们之间的依赖管理。 参考http://www.activiti.org/community.html#maven.repository 来为你的项目引入必须的jar包。

如果不想用Maven,你也可以自己把这些jar引入到你的项目中。 Activiti下载zip包包含了一个libs目录, 包含了所有Activiti的jar包(和源代码jar包)。依赖没有用这种方式发布。 Activiti引擎必须的依赖如下所示(通过mvn dependency:tree生成):

org.activiti:activiti-engine:jar:5.12.1
+- org.apache.commons:commons-email:jar:1.2:compile
|  +- javax.mail:mail:jar:1.4.1:compile
|  \- javax.activation:activation:jar:1.1:compile
+- org.apache.commons:commons-lang3:jar:3.1:compile
+- org.mybatis:mybatis:jar:3.1.1:compile
+- org.springframework:spring-beans:jar:3.1.2.RELEASE:compile
|  \- org.springframework:spring-core:jar:3.1.2.RELEASE:compile
|     +- org.springframework:spring-asm:jar:3.1.2.RELEASE:compile
|     \- commons-logging:commons-logging:jar:1.1.1:compile
\- joda-time:joda-time:jar:2.1:compile
       

注意:只有使用了mail service task才必须引入mail依赖jar。

所有依赖可以在Activiti 源码的模块中, 通过mvn dependency:copy-dependencies下载。

下一步

使用Activiti Explorer web应用 是一个熟悉Activiti概念和功能的好办法。但是, Activiti的主要目标是为你自己的应用添加强大的BPM和工作流功能。 下面的章节会帮助你熟悉 如何在你的环境中使用Activiti进行编程:

  • 配置章节 会教你如何设置Activiti, 如何获得ProcessEngine类的实例, 它是所有Activiti引擎功能的中心入口。

  • API章节会带领你了解建立Activiti API的服务。 这些服务用简便的方法提供了Activiti引擎的强大功能, 它们可以使用在任何Java环境下。

  • 对深入了解BPMN 2.0,Activiti引擎中流程的编写结构感兴趣吗? 请继续浏览BPMN 2.0 章节

Chapter 3. 配置

创建ProcessEngine

Activiti流程引擎的配置文件是名为activiti.cfg.xml的XML文件。 注意这与使用Spring方式创建流程引擎一样的。

获得ProcessEngine最简单的办法是 使用org.activiti.engine.ProcessEngines类:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()

它会在classpath下搜索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:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
    <property name="jdbcDriver" value="org.h2.Driver" />
    <property name="jdbcUsername" value="sa" />
    <property name="jdbcPassword" value="" />
    <property name="databaseSchemaUpdate" value="true" />
    <property name="jobExecutorActivate" value="false" />
    <property name="mailServerHost" value="mail.my-corp.com" />
    <property name="mailServerPort" value="5025" />
  </bean>
</beans>

注意配置XML文件其实是一个spring的配置文件。 但不是说Activiti只能用在Spring环境中! 我们只是利用了Spring的解析和依赖注入功能 来构建引擎。

配置文件中使用的ProcessEngineConfiguration可以通过编程方式创建。 可以配置不同的bean id(比如,第三行)。

ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource);
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(String resource, String beanName);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream);
ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName);

也可以不使用配置文件,基于默认创建配置 (参考各种支持类

ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();

所有这些ProcessEngineConfiguration.createXXX()方法都返回 ProcessEngineConfiguration,后续可以调整成所需的对象。 在调用buildProcessEngine()后, 就会创建一个ProcessEngine

ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration()
  .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE)
  .setJdbcUrl("jdbc:h2:mem:my-own-db;DB_CLOSE_DELAY=1000")
  .setJobExecutorActivate(true)
  .buildProcessEngine();

ProcessEngineConfiguration bean

activiti.cfg.xml必须包含一个id为'processEngineConfiguration'的bean。

 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

这个bean会用来构建ProcessEngine。 有多个类可以用来定义processEngineConfiguration。 这些类对应不同的环境,并设置了对应的默认值。 最好选择(最)适用于你的环境的类, 这样可以少配置几个引擎的参数。 下面是目前可以使用的类(以后会包含更多):

  • org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration: 单独运行的流程引擎。Activiti会自己处理事务。 默认,数据库只在引擎启动时检测 (如果没有Activiti的表或者表结构不正确就会抛出异常)。

  • org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration: 单元测试时的辅助类。Activiti会自己控制事务。 默认使用H2内存数据库。数据库表会在引擎启动时创建,关闭时删除。 使用它时,不需要其他配置(除非使用job执行器或邮件功能)。

  • org.activiti.spring.SpringProcessEngineConfiguration: 在Spring环境下使用流程引擎。 参考Spring集成章节

  • org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration: 单独运行流程引擎,并使用JTA事务。

数据库配置

Activiti可能使用两种方式配置数据库。 第一种方式是定义数据库配置参数:

  • jdbcUrl: 数据库的JDBC URL。

  • jdbcDriver: 对应不同数据库类型的驱动。

  • jdbcUsername: 连接数据库的用户名。

  • jdbcPassword: 连接数据库的密码。

基于JDBC参数配置的数据库连接 会使用默认的MyBatis连接池。 下面的参数可以用来配置连接池(来自MyBatis参数):

  • jdbcMaxActiveConnections: 连接池中处于被使用状态的连接的最大值。默认为10。

  • jdbcMaxIdleConnections: 连接池中处于空闲状态的连接的最大值。

  • jdbcMaxCheckoutTime: 连接被取出使用的最长时间,超过时间会被强制回收。 默认为20000(20秒)。

  • jdbcMaxWaitTime: 这是一个底层配置,让连接池可以在长时间无法获得连接时, 打印一条日志,并重新尝试获取一个连接。(避免因为错误配置导致沉默的操作失败)。 默认为20000(20秒)。

示例数据库配置:

<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
      

也可以使用javax.sql.DataSource。 (比如,Apache Commons的DBCP):

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/activiti" />
  <property name="username" value="activiti" />
  <property name="password" value="activiti" />
  <property name="defaultAutoCommit" value="false" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <property name="dataSource" ref="dataSource" />
    ...
      

注意,Activiti的发布包中没有这些类。 你要自己把对应的类(比如,从DBCP里)放到你的classpath下。

无论你使用JDBC还是DataSource的方式,都可以设置下面的配置:

  • databaseType: 一般不用设置,因为可以自动通过数据库连接的元数据获取。 只有自动检测失败时才需要设置。 可能的值有:{h2, mysql, oracle, postgres, mssql, db2}。 如果没使用默认的H2数据库就必须设置这项。 这个配置会决定使用哪些创建/删除脚本和查询语句。 参考支持数据库章节 了解支持哪些类型。

  • databaseSchemaUpdate: 设置流程引擎启动和关闭时如何处理数据库表。

    • false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。

    • true: 构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建。

    • create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。

支持的数据库

下面列出Activiti使用的数据库类型(大小写敏感)。

Table 3.1. 支持的数据库

Activiti数据库类型JDBC URL实例备注
h2jdbc:h2:tcp://localhost/activiti默认配置的数据库
mysqljdbc:mysql://localhost:3306/activiti?autoReconnect=true使用mysql-connector-java驱动测试
oraclejdbc:oracle:thin:@localhost:1521:xe 
postgresjdbc:postgresql://localhost:5432/activiti 
db2jdbc:db2://localhost:50000/activiti 
mssqljdbc:sqlserver://localhost:1433/activiti 

创建数据库表

下面是创建数据库表最简单的办法:

  • 把activiti-engine的jar放到classpath下

  • 添加对应的数据库驱动

  • 把Activiti配置文件 (activiti.cfg.xml) 放到 classpath下, 指向你的数据库(参考数据库配置章节

  • 执行 DbSchemaCreate 类的main方法

然而,只有数据库管理员可以执行DDL语句。 在生产环境,也也是最明智的选择。 SQL DDL语句可以从Activiti下载页或Activiti发布目录里找到,在database子目录下。 脚本也包含在引擎的jar中(activiti-engine-x.jar), 在org/activiti/db/create包下(drop目录里是删除语句)。 SQL文件的命名方式如下

activiti.{db}.{create|drop}.{type}.sql

其中 db支持的数据库type

  • engine: 引擎执行的表。必须。

  • identity: 包含用户,群组,用户与组之间的关系的表。 这些表是可选的,只有使用引擎自带的默认身份管理时才需要。

  • history: 包含历史和审计信息的表。可选的:历史级别设为none时不会使用。 注意这也会引用一些需要把数据保存到历史表中的功能(比如任务的评论)。

MySQL用户需要注意: 版本低于5.6.4的MySQL不支持毫秒精度的timstamp或date类型。 更眼中的是,有些版本会在尝试创建这样一列时抛出异常,而有些版本则不会。 在执行自动创建/更新时,引擎会在执行过程中修改DDL。 当使用DDL时,可以选择通用版本和名为mysql55的文件。 (它适合所有版本低于5.6.4的情况)。 后一个文件会将列的类型设置为没有毫秒的情况。

总结一下,对于MySQL版本会执行如下操作

  • <5.6: 不支持毫秒精度。可以使用DDL文件(包含mysql55的文件)。可以实现自动创建/更新。

  • 5.6.0 - 5.6.3: 不支持毫秒精度。无法自动创建/更新。建议更新到新的数据库版本。如果真的需要的话,也可以使用mysql 5.5

  • 5.6.4+:支持毫秒精度。可以使用DDL文件(默认包含mysql的文件)。可以实现自动创建、更新。

注意对于已经更新了MySQL数据库,而且Activiti表已经创建/更新的情况, 必须手工修改列的类型。

理解数据库表的命名

Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。

  • ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。

  • ACT_RU_*: 'RU'表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。

  • ACT_ID_*: 'ID'表示identity。 这些表包含身份信息,比如用户,组等等。

  • ACT_HI_*: 'HI'表示history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。

  • ACT_GE_*: 通用数据, 用于不同场景下。

数据库升级

在执行更新之前要先备份数据库 (使用数据库的备份功能)

默认,每次构建流程引擎时都会还行版本检测。 这一版都在应用启动或Activiti webapp启动时发生。 如果Activiti发现数据库表的版本与依赖库的版本不同, 就会抛出异常。

要升级,你要把下面的配置 放到activiti.cfg.xml配置文件里:

<beans ... >
  <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <!-- ... -->
    <property name="databaseSchemaUpdate" value="true" />
    <!-- ... -->
  </bean>
</beans>

然后,把对应的数据库驱动放到classpath里。 升级应用的Activiti依赖。启动一个新版本的Activiti 指向包含旧版本的数据库。将databaseSchemaUpdate设置为true, Activiti会自动将数据库表升级到新版本, 当发现依赖和数据库表版本不通过时。

也可以执行更新升级DDL语句。 也可以执行数据库脚本,可以在Activiti下载页找到。

启用Job执行器

JobExecutor是管理一系列线程的组件,可以触发定时器(也包含后续的异步消息)。 在单元测试场景下,很难使用多线程。因此API允许查询(ManagementService.createJobQuery)和执行job (ManagementService.executeJob),所以job可以在单元测试中控制。 要避免与job执行器冲突,可以关闭它。

默认,JobExecutor在流程引擎启动时就会激活。 如果不想在流程引擎启动后自动激活JobExecutor,可以设置

<property name="jobExecutorActivate" value="false" />

配置邮件服务器

可以选择配置邮件服务器。Activiti支持在业务流程中发送邮件。 想真正的发送一个email,必须配置一个真实的SMTP邮件服务器。 参考e-mail任务

配置历史

可以选择定制历史存储的配置。你可以通过配置影响引擎的历史功能。 参考历史配置

<property name="history" value="audit" />

为表达式和脚本暴露配置

默认,activiti.cfg.xml和你自己的Spring配置文件中所有bean 都可以在表达式和脚本中使用。 如果你想限制配置文件中的bean的可见性, 可以配置流程引擎配置的beans配置。 ProcessEngineConfiguration的beans是一个map。当你指定了这个参数, 只有包含这个map中的bean可以在表达式和脚本中使用。 通过在map中指定的名称来决定暴露的bean。

配置部署缓存

所有流程定义都被缓存了(解析之后)避免每次使用前都要访问数据库, 因为流程定义数据是不会改变的。 默认,不会限制这个缓存。如果想限制流程定义缓存,可以添加如下配置

<property name="processDefinitionCacheLimit" value="10" />

这个配置会把默认的hashmap缓存替换成LRU缓存,来提供限制。 当然,这个配置的最佳值跟流程定义的总数有关, 实际使用中会具体使用多少流程定义也有关。

也你可以注入自己的缓存实现。这个bean必须实现 org.activiti.engine.impl.persistence.deploy.DeploymentCache接口:

<property name="processDefinitionCache">
  <bean class="org.activiti.MyCache" />
</property>

有一个类似的配置叫knowledgeBaseCacheLimitknowledgeBaseCache, 它们是配置规则缓存的。只有流程中使用规则任务时才会用到。

日志

从Activiti 5.12开始,SLF4J被用作日志框架,替换了之前使用java.util.logging。 所有日志(activiti, spring, mybatis等等)都转发给SLF4J 允许使用你选择的日志实现。

默认activiti-engine依赖中没有提供SLF4J绑定的jar, 需要根据你的实际需要使用日志框架。如果没有添加任何实现jar,SLF4J会使用NOP-logger,不使用任何日志,不会发出警告,而且什么日志都不会记录。 可以通过http://www.slf4j.org/codes.html#StaticLoggerBinder了解这些实现。

使用Maven,比如使用一个依赖(这里使用log4j),注意你还需要添加一个version:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
</dependency>

activiti-explorer和activiti-rest应用都使用了Log4j绑定。执行所有activiti-*模块的单元测试页使用了Log4j。

特别提醒如果容器classpath中存在commons-logging: 为了把spring日志转发给SLF4J,需要使用桥接(参考http://www.slf4j.org/legacy.html#jclOverSLF4J)。 如果你的容器提供了commons-logging实现,请参考下面网页:http://www.slf4j.org/codes.html#release来确保稳定性。

使用Maven的实例(忽略版本):

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
</dependency>

映射诊断上下文

在5.13中,activiti支持slf4j的MDC功能。 如下的基础信息会传递到日志中记录:

  • 流程定义Id标记为mdcProcessDefinitionID

  • 流程实例Id标记为mdcProcessInstanceID

  • 分支Id标记为mdcexecutionId

默认不会记录这些信息。可以配置日志使用期望的格式来显示它们,扩展通常的日志信息。 比如,下面的log4j配置定义会让日志显示上面提及的信息:

 log4j.appender.consoleAppender.layout.ConversionPattern =ProcessDefinitionId=%X{mdcProcessDefinitionID}
executionId=%X{mdcExecutionId} mdcProcessInstanceID=%X{mdcProcessInstanceID} mdcBusinessKey=%X{mdcBusinessKey} %m%n"
      

当系统进行高风险任务,日志必须严格检查时,这个功能就非常有用,比如要使用日志分析的情况。

事件处理

Activiti 5.15中实现了一种事件机制。它允许在引擎触发事件时获得提醒。 参考所有支持的事件类型了解有效的事件。

可以为对应的事件类型注册监听器,在这个类型的任何时间触发时都会收到提醒。 你可以添加引擎范围的事件监听器通过配置, 添加引擎范围的事件监听器在运行阶段使用API, 或添加event-listener到特定流程定义的BPMN XML中

所有分发的事件,都是org.activiti.engine.delegate.event.ActivitiEvent的子类。事件包含(如果有效)typeexecutionIdprocessInstanceIdprocessDefinitionId。 对应的事件会包含事件发生时对应上下文的额外信息, 这些额外的载荷可以在支持的所有事件类型中找到。

事件监听器实现

实现事件监听器的唯一要求是实现org.activiti.engine.delegate.event.ActivitiEventListener。 西面是一个实现监听器的例子,它会把所有监听到的事件打印到标准输出中,包括job执行的事件异常:

public class MyEventListener implements ActivitiEventListener {
  @Override
  public void onEvent(ActivitiEvent event) {
    switch (event.getType()) {
      case JOB_EXECUTION_SUCCESS:
        System.out.println("A job well done!");
        break;
      case JOB_EXECUTION_FAILURE:
        System.out.println("A job has failed...");
        break;
      default:
        System.out.println("Event received: " + event.getType());
    }
  }
  @Override
  public boolean isFailOnException() {
    // The logic in the onEvent method of this listener is not critical, exceptions
    // can be ignored if logging fails...
    return false;
  }
}

isFailOnException()方法决定了当事件分发时,onEvent(..)方法抛出异常时的行为。 这里返回的是false,会忽略异常。 当返回true时,异常不会忽略,继续向上传播,迅速导致当前命令失败。 当事件是一个API调用的一部分时(或其他事务性操作,比如job执行), 事务就会回滚。当事件监听器中的行为不是业务性时,建议返回false

activiti提供了一些基础的实现,实现了事件监听器的常用场景。可以用来作为基类或监听器实现的样例:

  • org.activiti.engine.delegate.event.BaseEntityEventListener: 这个事件监听器的基类可以用来监听实体相关的事件,可以针对某一类型实体,也可以是全部实体。 它隐藏了类型检测,并提供了三个需要重写的方法:onCreate(..), onUpdate(..)onDelete(..),当实体创建,更新,或删除时调用。对于其他实体相关的事件,会调用 onEntityEvent(..)

配置与安装

把事件监听器配置到流程引擎配置中时,会在流程引擎启动时激活,并在引擎启动启动中持续工作着。

eventListeners属性需要org.activiti.engine.delegate.event.ActivitiEventListener的队列。 通常,我们可以声明一个内部的bean定义,或使用ref引用已定义的bean。 下面的代码,向配置添加了一个事件监听器,任何事件触发时都会提醒它,无论事件是什么类型:

<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    ...
    <property name="eventListeners">
      <list>
         <bean class="org.activiti.engine.example.MyEventListener" />
      </list>
    </property>
</bean>

为了监听特定类型的事件,可以使用typedEventListeners属性,它需要一个map参数。 map的key是逗号分隔的事件名(或单独的事件名)。 map的value是org.activiti.engine.delegate.event.ActivitiEventListener队列。 下面的代码演示了向配置中添加一个事件监听器,可以监听job执行成功或失败:

<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    ...
    <property name="typedEventListeners">
      <map>
        <entry key="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" >
          <list>
            <bean class="org.activiti.engine.example.MyJobEventListener" />
          </list>
        </entry>
      </map>
    </property>
</bean>

分发事件的顺序是由监听器添加时的顺序决定的。首先,会调用所有普通的事件监听器(eventListeners属性),按照它们在list中的次序。 然后,会调用所有对应类型的监听器(typedEventListeners属性),如果对应类型的事件被触发了。

在运行阶段添加监听器

可以通过API(RuntimeService)在运行阶段添加或删除额外的事件监听器:

/**
 * Adds an event-listener which will be notified of ALL events by the dispatcher.
 * @param listenerToAdd the listener to add
 */
void addEventListener(ActivitiEventListener listenerToAdd);
/**
 * Adds an event-listener which will only be notified when an event occurs, which type is in the given types.
 * @param listenerToAdd the listener to add
 * @param types types of events the listener should be notified for
 */
void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types);
/**
 * Removes the given listener from this dispatcher. The listener will no longer be notified,
 * regardless of the type(s) it was registered for in the first place.
 * @param listenerToRemove listener to remove
 */
 void removeEventListener(ActivitiEventListener listenerToRemove);

注意运行期添加的监听器引擎重启后就消失了。

为流程定义添加监听器

可以为特定流程定义添加监听器。监听器只会监听与这个流程定义相关的事件,以及这个流程定义上发起的所有流程实例的事件。 监听器实现可以使用,全类名定义,引用实现了监听器接口的表达式,或配置为抛出一个message/signal/error的BPMN事件。

让监听器执行用户定义的逻辑

下面代码为一个流程定义添加了两个监听器。第一个监听器会接收所有类型的事件,它是通过全类名定义的。 第二个监听器只接收作业成功或失败的事件,它使用了定义在流程引擎配置中的beans属性中的一个bean。

<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener class="org.activiti.engine.test.MyEventListener" />
    <activiti:eventListener delegateExpression="${testEventListener}" events="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" />
  </extensionElements>
  ...
</process>

对于实体相关的事件,也可以设置为针对某个流程定义的监听器,实现只监听发生在某个流程定义上的某个类型实体事件。 下面的代码演示了如何实现这种功能。可以用于所有实体事件(第一个例子),也可以只监听特定类型的事件(第二个例子)。

<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener class="org.activiti.engine.test.MyEventListener" entityType="task" />
    <activiti:eventListener delegateExpression="${testEventListener}" events="ENTITY_CREATED" entityType="task" />
  </extensionElements>
  ...
</process>

entityType支持的值有:attachment, comment, execution,identity-link, job, process-instance, process-definition, task

监听抛出BPMN事件

[试验阶段]

另一种处理事件的方法是抛出一个BPMN事件。请注意它只针对与抛出一个activiti事件类型的BPMN事件。 比如,抛出一个BPMN事件,在流程实例删除时,会导致一个错误。 下面的代码演示了如何在流程实例中抛出一个signal,把signal抛出到外部流程(全局),在流程实例中抛出一个消息事件, 在流程实例中抛出一个错误事件。除了使用classdelegateExpression, 还使用了throwEvent属性,通过额外属性,指定了抛出事件的类型。

<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="signal" signalName="My signal" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="globalSignal" signalName="My signal" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="message" messageName="My message" events="TASK_ASSIGNED" />
  </extensionElements>
</process>
<process id="testEventListeners">
  <extensionElements>
    <activiti:eventListener throwEvent="error" errorCode="123" events="TASK_ASSIGNED" />
  </extensionElements>
</process>

如果需要声明额外的逻辑,是否抛出BPMN事件,可以扩展activiti提供的监听器类。在子类中重写isValidEvent(ActivitiEvent event), 可以防止抛出BPMN事件。对应的类是org.activiti.engine.test.api.event.SignalThrowingEventListenerTest, org.activiti.engine.impl.bpmn.helper.MessageThrowingEventListenerorg.activiti.engine.impl.bpmn.helper.ErrorThrowingEventListener.

流程定义中监听器的注意事项

  • 事件监听器只能声明在process元素中,作为extensionElements的子元素。 监听器不能定义在流程的单个activity下。

  • delegateExpression中的表达式无法访问execution上下文,这与其他表达式不同(比如gateway)。 它只能引用定义在流程引擎配置的beans属性中声明的bean,或者使用spring(未使用beans属性)中所有实现了监听器接口的spring-bean。

  • 在使用监听器的 class 属性时,只会创建一个实例。记住监听器实现不会依赖成员变量, 确认是多线程安全的。

  • 当一个非法的事件类型用在events属性或throwEvent中时,流程定义发布时就会抛出异常。(会导致部署失败)。如果classdelegateExecution由问题(类不存在,不存在的bean引用,或代理类没有实现监听器接口),会在流程启动时抛出异常(或在第一个有效的流程定义事件被监听器接收时)。所以要保证引用的类正确的放在classpath下,表达式也要引用一个有效的实例。

通过API分发事件

我们提供了通过API使用事件机制的方法,允许大家触发定义在引擎中的任何自定义事件。 建议(不强制)只触发类型为CUSTOMActivitiEvents。可以通过RuntimeService触发事件:

/**
 * Dispatches the given event to any listeners that are registered.
 * @param event event to dispatch.
 *
 * @throws ActivitiException if an exception occurs when dispatching the event or when the {@link ActivitiEventDispatcher}
 * is disabled.
 * @throws ActivitiIllegalArgumentException when the given event is not suitable for dispatching.
 */
 void dispatchEvent(ActivitiEvent event);

支持的事件类型

下面是引擎中可能出现的所有事件类型。每个类型都对应org.activiti.engine.delegate.event.ActivitiEventType中的一个枚举值。

Table 3.2. 支持的事件

事件名称描述事件类型
ENGINE_CREATED监听器监听的流程引擎已经创建完毕,并准备好接受API调用。org.activiti...ActivitiEvent
ENGINE_CLOSED监听器监听的流程引擎已经关闭,不再接受API调用。org.activiti...ActivitiEvent
ENTITY_CREATED创建了一个新实体。实体包含在事件中。org.activiti...ActivitiEntityEvent
ENTITY_INITIALIZED创建了一个新实体,初始化也完成了。如果这个实体的创建会包含子实体的创建,这个事件会在子实体都创建/初始化完成后被触发,这是与ENTITY_CREATED的区别。org.activiti...ActivitiEntityEvent
ENTITY_UPDATED更新了已存在的实体。实体包含在事件中。org.activiti...ActivitiEntityEvent
ENTITY_DELETED删除了已存在的实体。实体包含在事件中。org.activiti...ActivitiEntityEvent
ENTITY_SUSPENDED暂停了已存在的实体。实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。org.activiti...ActivitiEntityEvent
ENTITY_ACTIVATED激活了已存在的实体,实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。org.activiti...ActivitiEntityEvent
JOB_EXECUTION_SUCCESS作业执行成功。job包含在事件中。org.activiti...ActivitiEntityEvent
JOB_EXECUTION_FAILURE作业执行失败。作业和异常信息包含在事件中。org.activiti...ActivitiEntityEvent and org.activiti...ActivitiExceptionEvent
JOB_RETRIES_DECREMENTED因为作业执行失败,导致重试次数减少。作业包含在事件中。org.activiti...ActivitiEntityEvent
TIMER_FIRED触发了定时器。job包含在事件中。org.activiti...ActivitiEntityEvent
ACTIVITY_STARTED一个节点开始执行org.activiti...ActivitiActivityEvent
ACTIVITY_COMPLETED一个节点成功结束org.activiti...ActivitiActivityEvent
ACTIVITY_SIGNALED一个节点收到了一个信号org.activiti...ActivitiSignalEvent
ACTIVITY_MESSAGE_RECEIVED一个节点收到了一个消息。在节点收到消息之前触发。收到后,会触发ACTIVITY_SIGNALACTIVITY_STARTED,这会根据节点的类型(边界事件,事件子流程开始事件)org.activiti...ActivitiMessageEvent
ACTIVITY_ERROR_RECEIVED一个节点收到了一个错误事件。在节点实际处理错误之前触发。 事件的activityId对应着处理错误的节点。 这个事件后续会是ACTIVITY_SIGNALLEDACTIVITY_COMPLETE, 如果错误发送成功的话。org.activiti...ActivitiErrorEvent
UNCAUGHT_BPMN_ERROR抛出了未捕获的BPMN错误。流程没有提供针对这个错误的处理器。 事件的activityId为空。org.activiti...ActivitiErrorEvent
ACTIVITY_COMPENSATE一个节点将要被补偿。事件包含了将要执行补偿的节点id。org.activiti...ActivitiActivityEvent
VARIABLE_CREATED创建了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。org.activiti...ActivitiVariableEvent
VARIABLE_UPDATED更新了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。org.activiti...ActivitiVariableEvent
VARIABLE_DELETED删除了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。org.activiti...ActivitiVariableEvent
TASK_ASSIGNED任务被分配给了一个人员。事件包含任务。org.activiti...ActivitiEntityEvent
TASK_COMPLETED任务被完成了。它会在ENTITY_DELETE事件之前触发。当任务是流程一部分时,事件会在流程继续运行之前, 后续事件将是ACTIVITY_COMPLETE,对应着完成任务的节点。org.activiti...ActivitiEntityEvent
MEMBERSHIP_CREATED用户被添加到一个组里。事件包含了用户和组的id。org.activiti...ActivitiMembershipEvent
MEMBERSHIP_DELETED用户被从一个组中删除。事件包含了用户和组的id。org.activiti...ActivitiMembershipEvent
MEMBERSHIPS_DELETED所有成员被从一个组中删除。在成员删除之前触发这个事件,所以他们都是可以访问的。 因为性能方面的考虑,不会为每个成员触发单独的MEMBERSHIP_DELETED事件。org.activiti...ActivitiMembershipEvent

引擎内部所有ENTITY_*事件都是与实体相关的。下面的列表展示了实体事件与实体的对应关系:

  • ENTITY_CREATED, ENTITY_INITIALIZED, ENTITY_DELETED: Attachment, Comment, Deployment, Execution, Group, IdentityLink, Job, Model, ProcessDefinition, ProcessInstance, Task, User.

  • ENTITY_UPDATED: Attachment, Deployment, Execution, Group, IdentityLink, Job, Model, ProcessDefinition, ProcessInstance, Task, User.

  • ENTITY_SUSPENDED, ENTITY_ACTIVATED: ProcessDefinition, ProcessInstance/Execution, Task.

附加信息

只有同一个流程引擎中的事件会发送给对应的监听器。。的那个你有很多引擎 - 在同一个数据库运行 - 事件只会发送给注册到对应引擎的监听器。其他引擎发生的事件不会发送给这个监听器,无论实际上它们运行在同一个或不同的JVM中。

对应的事件类型(对应实体)都包含对应的实体。根据类型或事件,这些实体不能再进行更新(比如,当实例以被删除)。可能的话,使用事件提供的EngineServices来以安全的方式来操作引擎。即使如此,你需要小心的对事件对应的实体进行更新/操作。

没有对应历史的实体事件,因为它们都有运行阶段的对应实体。

Chapter 4. Activiti API

流程引擎的API和服务

引擎API是与Activiti打交道的最常用方式。 我们从ProcessEngine开始, 创建它的很多种方法都已经在 配置章节中有所涉及。 从ProcessEngine中,你可以获得很多囊括工作流/BPM方法的服务。 ProcessEngine和服务类都是线程安全的。 你可以在整个服务器中仅保持它们的一个引用就可以了。

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();

ProcessEngines.getDefaultProcessEngine()会在第一次调用时 初始化并创建一个流程引擎,以后再调用就会返回相同的流程引擎。 使用对应的方法可以创建和关闭所有流程引擎:ProcessEngines.init()ProcessEngines.destroy()

ProcessEngines会扫描所有activiti.cfg.xmlactiviti-context.xml 文件。 对于activiti.cfg.xml文件,流程引擎会使用Activiti的经典方式构建: ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine(). 对于activiti-context.xml文件,流程引擎会使用Spring方法构建:先创建一个Spring的环境, 然后通过环境获得流程引擎。

所有服务都是无状态的。这意味着可以在多节点集群环境下运行Activiti,每个节点都指向同一个数据库, 不用担心哪个机器实际执行前端的调用。 无论在哪里执行服务都没有问题。

RepositoryService可能是使用Activiti引擎时最先接触的服务。 它提供了管理和控制发布包流程定义的操作。 这里不涉及太多细节,流程定义是BPMN 2.0流程的java实现。 它包含了一个流程每个环节的结构和行为。 发布包是Activiti引擎的打包单位。一个发布包可以包含多个BPMN 2.0 xml文件和其他资源。 开发者可以自由选择把任意资源包含到发布包中。 既可以把一个单独的BPMN 2.0 xml文件放到发布包里,也可以把整个流程和相关资源都放在一起。 (比如,'hr-processes'实例可以包含hr流程相关的任何资源)。 可以通过RepositoryService部署这种发布包。 发布一个发布包,意味着把它上传到引擎中,所有流程都会在保存进数据库之前分析解析好。 从这点来说,系统知道这个发布包的存在,发布包中包含的流程就已经可以启动了。

除此之外,服务可以

  • 查询引擎中的发布包和流程定义。

  • 暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。

  • 获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。

  • 获得流程定义的pojo版本, 可以用来通过java解析流程,而不必通过xml。

正如RepositoryService负责静态信息(比如,不会改变的数据,至少是不怎么改变的), RuntimeService正好是完全相反的。它负责启动一个流程定义的新实例。 如上所述,流程定义定义了流程各个节点的结构和行为。 流程实例就是这样一个流程定义的实例。对每个流程定义来说,同一时间会有很多实例在执行。 RuntimeService也可以用来获取和保存流程变量。 这些数据是特定于某个流程实例的,并会被很多流程中的节点使用 (比如,一个排他网关常常使用流程变量来决定选择哪条路径继续流程)。 Runtimeservice也能查询流程实例和执行。 执行对应BPMN 2.0中的'token'。基本上执行指向流程实例当前在哪里。 最后,RuntimeService可以在流程实例等待外部触发时使用,这时可以用来继续流程实例。 流程实例可以有很多暂停状态,而服务提供了多种方法来'触发'实例, 接受外部触发后,流程实例就会继续向下执行。

任务是由系统中真实人员执行的,它是Activiti这类BPMN引擎的核心功能之一。 所有与任务有关的功能都包含在TaskService中:

  • 查询分配给用户或组的任务

  • 创建独立运行任务。这些任务与流程实例无关。

  • 手工设置任务的执行者,或者这些用户通过何种方式与任务关联。

  • 认领并完成一个任务。认领意味着一个人期望成为任务的执行者, 即这个用户会完成这个任务。完成意味着“做这个任务要求的事情”。 通常来说会有很多种处理形式。

IdentityService非常简单。它可以管理(创建,更新,删除,查询...)群组和用户。 请注意, Activiti执行时并没有对用户进行检查。 例如,任务可以分配给任何人,但是引擎不会校验系统中是否存在这个用户。 这是Activiti引擎也可以使用外部服务,比如ldap,活动目录,等等。

FormService是一个可选服务。即使不使用它,Activiti也可以完美运行, 不会损失任何功能。这个服务提供了启动表单任务表单两个概念。 启动表单会在流程实例启动之前展示给用户, 任务表单会在用户完成任务时展示。Activiti支持在BPMN 2.0流程定义中设置这些表单。 这个服务以一种简单的方式将数据暴露出来。再次重申,它时可选的, 表单也不一定要嵌入到流程定义中。

HistoryService提供了Activiti引擎手机的所有历史数据。 在执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据。

ManagementService在使用Activiti的定制环境中基本上不会用到。 它可以查询数据库的表和表的元数据。另外,它提供了查询和管理异步操作的功能。 Activiti的异步操作用途很多,比如定时器,异步操作, 延迟暂停、激活,等等。后续,会讨论这些功能的更多细节。

可以从javadocs中获得这些服务和引擎API的更多信息。

异常策略

Activiti中的基础异常为org.activiti.engine.ActivitiException,一个非检查异常。 这个异常可以在任何时候被API抛出,不过特定方法抛出的“特定”的异常都记录在 javadocs中。 例如,下面的TaskService

/**
 * Called when the task is successfully executed.
 * @param taskId the id of the task to complete, cannot be null.
 * @throws ActivitiObjectNotFoundException when no task exists with the given id.
 */
 void complete(String taskId);
    

在上面的例子中,当传入一个不存在的任务的id时,就会抛出异常。 同时,javadoc明确指出taskId不能为null,如果传入null, 就会抛出ActivitiIllegalArgumentException

我们希望避免过多的异常继承,下面的子类用于特定的场合。 流程引擎和API调用的其他场合不会使用下面的异常, 它们会抛出一个普通的ActivitiExceptions

  • ActivitiWrongDbException:当Activiti引擎发现数据库版本号和引擎版本号不一致时抛出。

  • ActivitiOptimisticLockingException:对同一数据进行并发方法并出现乐观锁时抛出。

  • ActivitiClassLoadingException:当无法找到需要加载的类或在加载类时出现了错误(比如,JavaDelegate,TaskListener等。

  • ActivitiObjectNotFoundException:当请求或操作的对应不存在时抛出。

  • ActivitiIllegalArgumentException:这个异常表示调用Activiti API时传入了一个非法的参数,可能是引擎配置中的非法值,或提供了一个非法制,或流程定义中使用的非法值。

  • ActivitiTaskAlreadyClaimedException:当任务已经被认领了,再调用taskService.claim(...)就会抛出。

使用Activiti的服务

像上面介绍的那样,要想操作Activiti引擎,需要通过 org.activiti.engine.ProcessEngine实例暴露的服务。 下面的代码假设你已经拥有了一个可以运行的Activiti环境。 你就可以操作一个org.activiti.engine.ProcessEngine。 如果只想简单尝试一下代码, 可以下载或者cloneActiviti单元测试模板, 导入到IDE中,把testUserguideCode()方法添加到 org.activiti.MyUnitTest中。

这个小例子的最终目标是做一个工作业务流程, 演示公司中简单的请假申请:

发布流程

任何与“静态”资源有关的数据(比如流程定义)都可以通过 RepositoryService访问。 从概念上讲,所以静态数据都是Activiti的资源内容。

src/test/resources/org/activiti/test目录下创建一个新的xml文件 VacationRequest.bpmn20.xml(如果不使用单元测试模板,你也可以在任何地方创建), 内容如下。注意这一章不会解释例子中使用的xml结构。 如果有需要可以先阅读bpmn 2.0章来了解这些。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">
  <process id="vacationRequest" name="Vacation request">
    <startEvent id="request" activiti:initiator="employeeName">
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" type="long" value="1" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" type="string" />
      </extensionElements>
    </startEvent>
    <sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" />
    <userTask id="handleRequest" name="Handle vacation request" >
      <documentation>
        ${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}).
      </documentation>
      <extensionElements>
         <activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true">
          <activiti:value id="true" name="Approve" />
          <activiti:value id="false" name="Reject" />
        </activiti:formProperty>
        <activiti:formProperty id="managerMotivation" name="Motivation" type="string" />
      </extensionElements>
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>management</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" />
    <exclusiveGateway id="requestApprovedDecision" name="Request approved?" />
    <sequenceFlow id="flow3" sourceRef="requestApprovedDecision" targetRef="sendApprovalMail">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'true'}</conditionExpression>
    </sequenceFlow>
    <task id="sendApprovalMail" name="Send confirmation e-mail" />
    <sequenceFlow id="flow4" sourceRef="sendApprovalMail" targetRef="theEnd1" />
    <endEvent id="theEnd1" />
    <sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'false'}</conditionExpression>
    </sequenceFlow>
    <userTask id="adjustVacationRequestTask" name="Adjust vacation request">
      <documentation>
        Your manager has disapproved your vacation request for ${numberOfDays} days.
        Reason: ${managerMotivation}
      </documentation>
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
        <activiti:formProperty id="resendRequest" name="Resend vacation request to manager?" type="enum" required="true">
          <activiti:value id="true" name="Yes" />
          <activiti:value id="false" name="No" />
        </activiti:formProperty>
      </extensionElements>
      <humanPerformer>
        <resourceAssignmentExpression>
          <formalExpression>${employeeName}</formalExpression>
        </resourceAssignmentExpression>
      </humanPerformer>
    </userTask>
    <sequenceFlow id="flow6" sourceRef="adjustVacationRequestTask" targetRef="resendRequestDecision" />
    <exclusiveGateway id="resendRequestDecision" name="Resend request?" />
    <sequenceFlow id="flow7" sourceRef="resendRequestDecision" targetRef="handleRequest">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'true'}</conditionExpression>
    </sequenceFlow>
     <sequenceFlow id="flow8" sourceRef="resendRequestDecision" targetRef="theEnd2">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'false'}</conditionExpression>
    </sequenceFlow>
    <endEvent id="theEnd2" />
  </process>
</definitions>
            

为了让Activiti引擎知道这个流程,我们必须先进行“发布”。 发布意味着引擎会把BPMN 2.0 xml解析成可以执行的东西, “发布包”中的所有流程定义都会添加到数据库中。 这样,当引擎重启时,它依然可以获得“已发布”的流程:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
  .addClasspathResource("org/activiti/test/VacationRequest.bpmn20.xml")
  .deploy();
Log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count());
            

可以阅读发布章来了解更多关于发布的信息。

启动一个流程实例

把流程定义发布到Activiti引擎后,我们可以基于它发起新流程实例。 对每个流程定义,都可以有很多流程实例。 流程定义是“蓝图”,流程实例是它的一个运行的执行。

所有与流程运行状态相关的东西都可以通过RuntimeService获得。 有很多方法可以启动一个新流程实例。在下面的代码中,我们使用定义在流程定义xml 中的key来启动流程实例。 我们也可以在流程实例启动时添加一些流程变量,因为第一个用户任务的表达式需要这些变量。 流程变量经常会被用到,因为它们赋予来自同一个流程定义的不同流程实例的特别含义。 简单来说,流程变量是区分流程实例的关键。

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);
// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
            

完成任务

流程启动后,第一步就是用户任务。这是必须由系统用户处理的一个环节。 通常,用户会有一个“任务列表”,展示了所有必须由整个用户处理的任务。 下面的代码展示了对应的查询可能是怎样的:

// Fetch all tasks for the management group
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  Log.info("Task available:""literal">complete任务。
			下面的代码展示了如何做这件事:
            

Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);
            

流程实例会进入到下一个环节。在这里例子中, 下一环节允许员工通过表单调整原始的请假申请。员工可以重新提交请假申请, 这会使流程重新进入到第一个任务。

挂起,激活一个流程

我们可以挂起一个流程定义。当挂起流程定时时, 就不能创建新流程了(会抛出一个异常)。 可以通过RepositoryService挂起一个流程:

repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
  runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
  e.printStackTrace();
}
            

要想重新激活一个流程定义,可以调用repositoryService.activateProcessDefinitionXXX方法。

也可以挂起一个流程实例。挂起时,流程不能继续执行(比如,完成任务会抛出异常), 异步操作(比如定时器)也不会执行。 骨气流程实例可以调用 runtimeService.suspendProcessInstance方法。 激活流程实例可以调用runtimeService.activateProcessInstanceXXX方法。

更多知识

上面章节中我们仅仅覆盖了Activiti功能的表层。 未来我们会继续扩展这些章节,以覆盖更多Activiti API。 当然,像其他开源项目一样,学习的最好方式 是研究代码,阅读javadoc。

查询API

有两种方法可以从引擎中查询数据:查询API和原生查询。查询API提供了完全类型安全的API。 你可以为自己的查询条件添加很多条件 (所以条件都以AND组合)和精确的排序条件。下面的代码展示了一个例子:

      List<Task> tasks = taskService.createTaskQuery()
         .taskAssignee("kermit")
         .processVariableValueEquals("orderId", "0815")
         .orderByDueDate().asc()
         .list();
      

有时,你需要更强大的查询,比如使用OR条件或不能使用查询API实现的条件。 这时,我们推荐原生查询,它让你可以编写自己的SQL查询。 返回类型由你使用的查询对象决定,数据会映射到正确的对象上。比如,任务,流程实例,,执行,等等。 因为查询会作用在数据库上,你必须使用数据库中定义的表名和列名;这要求了解内部数据结构, 因此使用原生查询时一定要注意。表名可以通过API获得,可以尽量减少对数据库的依赖。

      List<Task> tasks = taskService.createNativeTaskQuery()
        .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
        .parameter("taskName", "gonzoTask")
        .list();
      long count = taskService.createNativeTaskQuery()
        .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, "
               + managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
        .count();
      

表达式

Activiti使用UEL处理表达式。UEL即统一表达式语言,它时EE6规范的一部分(参考 EE6规范)。为了在所有运行环境都支持最新UEL的所有共嫩个,我们使用了一个JUEL的修改版本。

表达式可以用在很多场景下,比如Java服务任务执行监听器任务监听器条件流。 虽然有两重表达式,值表达式和方法表达式,Activiti进行了抽象,所以两者可以同样使用在需要表达式的场景中。

  • Value expression:解析为值。默认,所有流程变量都可以使用。所有spring bean(spring环境中)也可以使用在表达式中。 一些实例:

    ${myVar}
    ${myBean.myProperty}

  • Method expression:调用一个方法,使用或不使用参数。当调用一个无参数的方法时,记得在方法名后添加空的括号(以区分值表达式)。 传递的参数可以是字符串也可以是表达式,它们会被自动解析。例子:

    ${printer.print()}
    ${myBean.addNewOrder('orderName')}
    ${myBean.doSomething(myVar, execution)}
    

注意这些表达式支持解析原始类型(包括比较),bean,list,数组和map。

在所有流程实例中,表达式中还可以使用一些默认对象:

  • executionDelegateExecution提供外出执行的额外信息。

  • taskDelegateTask提供当前任务的额外信息。注意,只对任务监听器的表达式有效。

  • authenticatedUserId:当前登录的用户id。如果没有用户登录,这个变量就不可用。

想要更多具体的使用方式和例子,参考spring中的表达式Java服务任务执行监听器任务监听器条件流

单元测试

业务流程是软件项目的一部分,它也应该和普通的业务流程一样进行测试: 使用单元测试。 因为Activiti是一个嵌入式的java引擎, 为业务流程编写单元测试和写普通单元测试完全一样。

Activiti支持JUnit 3和4进行单元测试。使用JUnit 3时, 必须集成org.activiti.engine.test.ActivitiTestCase。 它通过保护的成员变量提供ProcessEngine和服务, 在测试的setup()中, 默认会使用classpath下的activiti.cfg.xml初始化流程引擎。 想使用不同的配置文件,可以重写getConfigurationResource()方法。 如果配置文件相同的话,对应的流程引擎会被静态缓存, 就可以用于多个单元测试。

继承了ActivitiTestCase你,可以在测试方法上使用 org.activiti.engine.test.Deployment注解。 测试执行前,与测试类在同一个包下的, 格式为testClassName.testMethod.bpmn20.xml的资源文件,会被部署。 测试结束后,发布包也会被删除,包括所有相关的流程实例,任务,等等。 Deployment注解也可以直接设置资源的位置。 参考Javadocs获得更多信息。

把这些放在一起,JUnit 3测试看起来像这样。

public class MyBusinessProcessTest extends ActivitiTestCase {
  @Deployment
  public void testSimpleProcess() {
    runtimeService.startProcessInstanceByKey("simpleProcess");
    Task task = taskService.createTaskQuery().singleResult();
    assertEquals("My Task", task.getName());
    taskService.complete(task.getId());
    assertEquals(0, runtimeService.createProcessInstanceQuery().count());
  }
}
      

要想在使用JUnit 4编写单元测试时获得同样的功能, 可以使用org.activiti.engine.test.ActivitiRule。 通过它,可以通过getter方法获得流程引擎和各种服务。 和 ActivitiTestCase一样(参考上面章节),使用这个Rule 也会启用org.activiti.engine.test.Deployment注解(参考上面章节使用和配置的介绍), 它会在classpath下查找默认的配置文件。 如果配置文件相同的话,对应的流程引擎会被静态缓存, 就可以用于多个单元测试。

下面的代码演示了JUnit 4单元测试并使用了ActivitiRule的例子。

public class MyBusinessProcessTest {
  @Rule
  public ActivitiRule activitiRule = new ActivitiRule();
  @Test
  @Deployment
  public void ruleUsageExample() {
    RuntimeService runtimeService = activitiRule.getRuntimeService();
    runtimeService.startProcessInstanceByKey("ruleUsage");
    TaskService taskService = activitiRule.getTaskService();
    Task task = taskService.createTaskQuery().singleResult();
    assertEquals("My Task", task.getName());
    taskService.complete(task.getId());
    assertEquals(0, runtimeService.createProcessInstanceQuery().count());
  }
}
      

调试单元测试

当使用内存数据库H2进行单元测试时,下面的教程会告诉我们 如何在调试环境下更容易的监视Activiti的数据库。 这里的截图都是基于eclipse,这种机制很容易复用到其他IDE下。 IDEs.

假设我们已经在单元测试里设置了一个断点。 Ecilpse里,在代码左侧双击:

现在用调试模式运行单元测试(右击单元测试, 选择“运行为”和“单元测试”),测试会停在我们的断点上, 然后我们就可以监视测试的变量,它们显示在右侧面板里。

要监视Activiti的数据,打开“显示”窗口 (如果找不到,打开“窗口”->“显示视图”->“其他”,选择显示。) 并点击(代码已完成)org.h2.tools.Server.createWebServer("-web").start()

选择你点击的行,右击。然后选择“显示”(或者直接快捷方式就不用右击了)

现在打开一个浏览器,打开http://localhost:8082, 输入内存数据库的JDBC URL(默认为jdbc:h2:mem:activiti), 点击连接按钮。

你仙子阿可以看到Activiti的数据,通过它们可以了解单元测试时如何以及为什么这样运行的。

web应用中的流程引擎

ProcessEngine是线程安全的, 可以在多线程下共享。在web应用中, 意味着可以在容器启动时创建流程引擎, 在容器关闭时关闭流程引擎。

下面代码演示了如何编写一个ServletContextListener 在普通的Servlet环境下初始化和销毁流程引擎:

public class ProcessEnginesServletContextListener implements ServletContextListener {
  public void contextInitialized(ServletContextEvent servletContextEvent) {
    ProcessEngines.init();
  }
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    ProcessEngines.destroy();
  }
}

contextInitialized方法会执行ProcessEngines.init()。 这会查找classpath下的activiti.cfg.xml文件, 根据配置文件创建一个ProcessEngine(比如,多个jar中都包含配置文件)。 如果classpath中包含多个配置文件,确认它们有不同的名字。 当需要使用流程引擎时,可以通过

ProcessEngines.getDefaultProcessEngine()

ProcessEngines.getProcessEngine("myName");

。 当然,也可以使用其他方式创建流程引擎, 可以参考配置章节中的描述。

ContextListener中的contextDestroyed方法会执行ProcessEngines.destroy(). 这会关闭所有初始化的流程引擎。

Chapter 5. Spring集成

虽然没有Spring你也可以使用Activiti,但是我们提供了一些非常不错的集成特性。这一章我们将介绍这些特性。

ProcessEngineFactoryBean

可以把流程引擎(ProcessEngine)作为一个普通的Spring bean进行配置。 类 org.activiti.spring.ProcessEngineFactoryBean是集成的切入点。 这个bean需要一个流程引擎配置来创建流程引擎。这也意味着在文档的配置这一章的介绍属性的创建和配置对于Spring来说也是一样的。对于Spring集成的配置和流程引擎bean看起来像这样:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
    ...
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
  <property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
  

注意现在使用的 processEngineConfiguration bean 是 org.activiti.spring.SpringProcessEngineConfiguration 类。

事务

我们将会一步一步地解释在Spring examples中公布的 SpringTransactionIntegrationTest 下面是我们使用这个例子的Spring配置文件(你可以在SpringTransactionIntegrationTest-context.xml找到它)以下展示的部分包括数据源(dataSource), 事务管理器(transactionManager),流程引擎(processEngine)和Activiti引擎服务。

当把数据源(DataSource)传递给 SpringProcessEngineConfiguration (使用"dataSource"属性)之后,Activiti内部使用了一个org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy代理来封装传递进来的数据源(DataSource)。 这样做是为了确保从数据源(DataSource)获取的SQL连接能够与Spring的事物结合在一起发挥得更出色。这意味它不再需要在你的Spring配置中代理数据源(dataSource)了。 然而它仍然允许你传递一个TransactionAwareDataSourceProxySpringProcessEngineConfiguration中。在这个例子中并不会发生多余的包装。

为了确保在你的Spring配置中申明的一个TransactionAwareDataSourceProxy,你不能把使用它的应用交给Spring事物控制的资源。(例如 DataSourceTransactionManager 和JPATransactionManager需要非代理的数据源 )

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                           http://www.springframework.org/schema/tx      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
  <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
    <property name="username" value="sa" />
    <property name="password" value="" />
  </bean>
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
    <property name="dataSource" ref="dataSource" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="databaseSchemaUpdate" value="true" />
    <property name="jobExecutorActivate" value="false" />
  </bean>
  <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
    <property name="processEngineConfiguration" ref="processEngineConfiguration" />
  </bean>
  <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
  <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
  <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
  <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
  <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
...

Spring配置文件的其余部分包含beans和我们将要在这个特有的例子中的配置:

<beans>
  ...
  <tx:annotation-driven transaction-manager="transactionManager"/>
  <bean id="userBean" class="org.activiti.spring.test.UserBean">
    <property name="runtimeService" ref="runtimeService" />
  </bean>
  <bean id="printer" class="org.activiti.spring.test.Printer" />
</beans>

首先使用任意的一种Spring创建应用上下文的方式创建其Spring应用上下文。在这个例子中你可以使用类路径下面的XML资源来配置我们的Spring应用上下文:

ClassPathXmlApplicationContext applicationContext =
    new ClassPathXmlApplicationContext("org/activiti/examples/spring/SpringTransactionIntegrationTest-context.xml");

或者, 如果它是一个测试的话:

@ContextConfiguration("classpath:org/activiti/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")

然后我们就可以得到Activiti的服务beans并且调用该服务上面的方法。ProcessEngineFactoryBean将会对该服务添加一些额外的拦截器,在Activiti服务上面的方法使用的是 Propagation.REQUIRED事物语义。所以,我们可以使用repositoryService去部署一个流程,如下所示:

RepositoryService repositoryService = (RepositoryService) applicationContext.getBean("repositoryService");
String deploymentId = repositoryService
  .createDeployment()
  .addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")
  .deploy()
  .getId();
    

其他相同的服务也是同样可以这么使用。在这个例子中,Spring的事物将会围绕在userBean.hello()上,并且调用Activiti服务的方法也会加入到这个事物中。

UserBean userBean = (UserBean) applicationContext.getBean("userBean");
userBean.hello();

这个UserBean看起来像这样。记得在上面Spring bean的配置中我们把repositoryService注入到userBean中。

public class UserBean {
  /** 由Spring注入 */
  private RuntimeService runtimeService;
  @Transactional
  public void hello() {
	//这里,你可以在你们的领域模型中做一些事物处理。
	//当在调用Activiti RuntimeService的startProcessInstanceByKey方法时,
	//它将会结合到同一个事物中。
    runtimeService.startProcessInstanceByKey("helloProcess");
  }
  public void setRuntimeService(RuntimeService runtimeService) {
    this.runtimeService = runtimeService;
  }
}

表达式

当使用ProcessEngineFactoryBean时候,默认情况下,在BPMN流程中的所有表达式都将会'看见'所有的Spring beans。 它可以限制你在表达式中暴露出的beans或者甚至可以在你的配置中使用一个Map不暴露任何beans。下面的例子暴露了一个单例bean(printer),可以把"printer"当作关键字使用. 想要不暴露任何beans,仅仅只需要在SpringProcessEngineConfiguration中传递一个空的list作为'beans'的属性。当不设置'beans'的属性时,在应用上下文中Spring beans都是可以使用的。

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
  ...
  <property name="beans">
    <map>
      <entry key="printer" value-ref="printer" />
    </map>
  </property>
</bean>
  <bean id="printer" class="org.activiti.examples.spring.Printer" />
    

现在暴露出来的beans就可以在表达式中使用:例如,在SpringTransactionIntegrationTest中的 hello.bpmn20.xml展示的是如何使用UEL方法表达式去调用Spring bean的方法:

<definitions id="definitions" ...>
  <process id="helloProcess">
    <startEvent id="start" />
    <sequenceFlow id="flow1" sourceRef="start" targetRef="print" />
    <serviceTask id="print" activiti:expression="#{printer.printMessage()}" />
    <sequenceFlow id="flow2" sourceRef="print" targetRef="end" />
    <endEvent id="end" />
  </process>
</definitions>

这里的 Printer 看起来像这样:

public class Printer {
  public void printMessage() {
    System.out.println("hello world");
  }
}

并且Spring bean的配置(如上文所示)看起来像这样:

<beans ...>
  ...
  <bean id="printer" class="org.activiti.examples.spring.Printer" />
</beans>

资源的自动部署

Spring的集成也有一个专门用于对资源部署的特性。在流程引擎的配置中,你可以指定一组资源。当流程引擎被创建的时候, 所有在这里的资源都将会被自动扫描与部署。在这里有过滤以防止资源重新部署,只有当这个资源真正发生改变的时候,它才会向Activiti使用的数据库创建新的部署。 这对于很多用例来说,当Spring容器经常重启的情况下(例如 测试),使用它是非常不错的选择。

这里有一个例子:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
  ...
  <property name="deploymentResources" value="classpath*:/org/activiti/spring/test/autodeployment/autodeploy.*.bpmn20.xml" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
  <property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

单元测试

当集成Spring时,使用标准的Activiti测试工具类是非常容易的对业务流程进行测试。 下面的例子展示了如何在一个典型的基于Spring单元测试测试业务流程:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:org/activiti/spring/test/junit4/springTypicalUsageTest-context.xml")
public class MyBusinessProcessTest {
  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private TaskService taskService;
  @Autowired
  @Rule
  public ActivitiRule activitiSpringRule;
  @Test
  @Deployment
  public void simpleProcessTest() {
    runtimeService.startProcessInstanceByKey("simpleProcess");
    Task task = taskService.createTaskQuery().singleResult();
    assertEquals("My Task", task.getName());
    taskService.complete(task.getId());
    assertEquals(0, runtimeService.createProcessInstanceQuery().count());
  }
}
      

注意对于这种方式,你需要在Spring配置中(在上文的例子中它是自动注入的)定义一个org.activiti.engine.test.ActivitiRulebean

<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
  <property name="processEngine" ref="processEngine" />
</bean>
      

基于注解的配置

[试验] @EnableActiviti注解相对较新,未来可能会有变更。

除了基于XML的配置以外,还可以选择基于注解的方式来配置Spring环境。 这与使用XML的方法非常相似,除了要使用@Bean注解, 而且配置是使用java编写的。 它已经可以直接用于Activiti-Spring的集成了:

首先介绍(需要Spring 3.0+)的是@EnableActiviti注解。 最简单的用法如下所示:

  @Configuration
  @EnableActiviti
  public static class SimplestConfiguration {
  }

它会创建一个Spring环境,并对Activiti流程引擎进行如下配置

  • 默认的内存H2数据库,启用数据库自动升级。

  • 一个简单的 DataSourceTransactionManager

  • 一个默认的 SpringJobExecutor

  • 自动扫描 processes/ 目录下的bpmn20.xml文件。

在这样一个环境里,可以直接通过注入操作Activiti引擎:

  @Autowired
  private ProcessEngine processEngine;
  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private TaskService taskService;
  @Autowired
  private HistoryService historyService;
  @Autowired
  private RepositoryService repositoryService;
  @Autowired
  private ManagementService managementService;
  @Autowired
  private FormService formService;

当然,默认值都可以自定义。比如,如果配置了DataSource,它就会代替默认创建的数据库配置。 事务管理器,job执行器和其他组件都与之相同。 比如如下配置:

  @Configuration
  @EnableActiviti
  public static class Config {
    @Bean
    public DataSource dataSource() {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setUsername("sa");
        basicDataSource.setUrl("jdbc:h2:mem:anotherDatabase");
        basicDataSource.setDefaultAutoCommit(false);
        basicDataSource.setDriverClassName(org.h2.Driver.class.getName());
        basicDataSource.setPassword("");
        return basicDataSource;
    }
  }

其他数据库会代替默认的。

下面介绍了更加复杂的配置。注意AbstractActivitiConfigurer用法, 它暴露了流程引擎的配置,可以用来对它的细节进行详细的配置。

@Configuration
@EnableActiviti
@EnableTransactionManagement(proxyTargetClass = true)
class JPAConfiguration {
    @Bean
    public OpenJpaVendorAdapter openJpaVendorAdapter() {
        OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
        openJpaVendorAdapter.setDatabasePlatform(H2Dictionary.class.getName());
        return openJpaVendorAdapter;
    }
    @Bean
    public DataSource dataSource() {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setUsername("sa");
        basicDataSource.setUrl("jdbc:h2:mem:activiti");
        basicDataSource.setDefaultAutoCommit(false);
        basicDataSource.setDriverClassName(org.h2.Driver.class.getName());
        basicDataSource.setPassword("");
        return basicDataSource;
    }
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(
        OpenJpaVendorAdapter openJpaVendorAdapter, DataSource ds) {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setPersistenceXmlLocation("classpath:/org/activiti/spring/test/jpa/custom-persistence.xml");
        emf.setJpaVendorAdapter(openJpaVendorAdapter);
        emf.setDataSource(ds);
        return emf;
    }
    @Bean
    public PlatformTransactionManager jpaTransactionManager(
        EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
    @Bean
    public AbstractActivitiConfigurer abstractActivitiConfigurer(
        final EntityManagerFactory emf,
        final PlatformTransactionManager transactionManager) {
        return new AbstractActivitiConfigurer() {
            @Override
            public void postProcessSpringProcessEngineConfiguration(SpringProcessEngineConfiguration engine) {
                engine.setTransactionManager(transactionManager);
                engine.setJpaEntityManagerFactory(emf);
                engine.setJpaHandleTransaction(false);
                engine.setJobExecutorActivate(false);
                engine.setJpaCloseEntityManager(false);
                engine.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
            }
        };
    }
    // A random bean
    @Bean
    public LoanRequestBean loanRequestBean() {
        return new LoanRequestBean();
    }
}

JPA 和 Hibernate 4.2.x

在Activiti引擎的serviceTask或listener中使用Hibernate 4.2.x JPA时,需要添加Spring ORM这个额外的依赖。 Hibernate 4.1.x及以下版本是不需要的。应该添加如下依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
      

Chapter 6. 部署

业务文档

为了部署流程,它们不得不包装在一个业务文档中。一个业务文档是Activiti引擎部署的单元。一个业务文档相当与一个压缩文件,它包含BPMN2.0流程,任务表单,规则和其他任意类型的文件。 大体上,业务文档是包含命名资源的容器。

当一个业务文档被部署,它将会自动扫描以 .bpmn20.xml 或者.bpmn作为扩展名的BPMN文件。每个那样的文件都将会被解析并且可能会包含多个流程定义。

Note

业务归档中的Java类将不能够添加到类路径下。为了能够让流程运行,必须把存在于业务归档程中的流程定义使用的所有自定义的类(例如:Java服务任务或者实现事件的监听器)放在activiti引擎的类路径下:

编程式部署

通过一个压缩文件(支持Zip和Bar)部署业务归档,它看起来像这样:

String barFileName = "path/to/process-one.bar";
ZipInputStream inputStream = new ZipInputStream(new FileInputStream(barFileName));
repositoryService.createDeployment()
    .name("process-one.bar")
    .addZipInputStream(inputStream)
    .deploy();
	

它也可以通过一个独立资源(例如bpmn,xml等)构建部署。 详细信息请查看javadocs。

通过Activiti Explorer控制台部署

Activiti web控制台允许你通过web界面的用户接口上传一个bar格式的压缩文件(或者一个bpmn20.xml格式的文件)。 选择Management 标签 和 点击 Deployment:

现在将会有一个弹出窗口允许你从电脑上面选择一个文件,或者你可以简单的拖拽到指定的区域(如果你的浏览器支持)。

外部资源

流程定义保存在Activiti所支持的数据库中。当使用服务任务、执行监听器或者从Activiti配置文件中配置的Spring beans时,流程定义能够引用这些委托类。 这些类或者Spring配置文件对于所有流程引擎中可能执行的流程定义必须是可用的。

Java类

当流程实例被启动的时候,在流程