实 战
Activiti实战
闫洪磊 著
图书在版编目(CIP)数据
Activiti实战/闫洪磊著. —北京:机械工业出版社,2014.12
(实战)
ISBN 978-7-111-48595-7
I. A… II.闫… III. JAVA语言-程序设计 IV. TP312
中国版本图书馆CIP数据核字(2014)第270580号
本书版权登记号:图字:01-2013-1390
Authorized translation from the English language edition, entitled Learning Objective-C 2.0: A Hands-on Guide to Objective-C for Mac and iOS Developers,2nd Edition, 9780321832085 by Robert Clair, published by Pearson Education, Inc., Copyright ?2013.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.
Chinese simplified language edition published by Pearson Education Asia Ltd., and China Machine Press Copyright ?2014.
本书中文简体字版由Pearson Education(培生教育出版集团)授权机械工业出版社在中华人民共和国境内(不包括中国台湾地区和中国香港、澳门特别行政区)独家出版发行。未经出版者书面许可,不得以任何方式抄袭、复制或节录本书中的任何部分。
本书封底贴有Pearson Education(培生教育出版集团)激光防伪标签,无标签者不得销售。
Activiti实战
出版发行:机械工业出版社(北京市西城区百万庄大街22号 邮政编码:100037)
责任编辑:姜影 责任校对:殷虹
印 刷: 版 次:2015年1月第1版第1次印刷
开 本:186mm×240mm 1/16 印 张:31.75
书 号:ISBN 978-7-111-48595-7 定 价:89.00元
凡购本书,如有缺页、倒页、脱页,由本社发行部调换
客服热线:(010)88378991 88361066 投稿热线:(010)88379604
购书热线:(010)68326294 88379649 68995259 读者信箱:hzjsj@hzbook.com
版权所有·侵权必究
封底无防伪标均为盗版
本书法律顾问:北京大成律师事务所 韩光/邹晓东
Foreword 本 书 推 荐
洪磊与我同事近8年,最早我在原来公司担任CTO,在一次处理系统性能问题时发现洪磊是个疯狂的技术控,那时的他虽然刚工作不久,但是一直在学习一些前言技术,因此对他印象比较深刻。
后来我自己创业,正巧合他也有意向,我们一起一干就是4年。因为是创业公司,我们对框架和平台的选择主要聚焦在开源项目上。工作流平台的选择是个艰难的决定,在对比jBPM5与Tom发起的Activiti后,我决定接受洪磊的建议选择Activiti。
我们公司对这本书的写作极其重视。书中引用的很多案例,都源自实际的项目中的使用经验。在使用过程中也遇到了一些不能满足需求的地方,因此我们自己做了一些改动并通过Github贡献了源代码。目前为止,我们公司是中国地区对Activiti贡献代码次数最多的团队,洪磊是团队的技术核心。这本书也算是对Activiti项目以及对开源代码社区饮水思源的一种表达吧。
今年我们做了一个汽车电商项目—小马购车,初期拿到千万级天使投资,依然选择Activiti作为后台业务系统的流程引擎。事实证明Activiti都能轻松“Hold”住各种应用场景。
希望本书能给大家在Activiti实战中带来更多的帮助。
小马购车CTO
郑国强
咖啡兔同学的《Activiti实战》终于出炉,欣喜之情溢于言表。虽然国内的工作流产品繁多,但是开源一直为Activiti和jBPM垄断。相对来说Activiti延续了一贯方便灵活的特性,又不会在功能上有半分折损,在国内拥有大量的粉丝。咖啡兔同学此前一直致力于Activiti在国内的推广与传播,先后开辟了专栏博客、Activiti论坛网站、QQ群组,并且积极参与Activiti的官方开发,可以说Activiti在国内能达到当前的认知程度,他是功不可没的。可惜,国内尚缺一本可以为Activiti新手答疑解惑,带初阶者更上一层楼的实体书籍。我觉得,这个任务由一直积极活跃于Activiti开源社区,既拥有实际流程项目设计研发经验,又为Activiti官方内核提交过代码的洪磊是再合适不过了。
全书由浅入深地引导读者进入工作流的殿堂,不仅覆盖常见的流程功能与实现方法,还专门提供了作者在实践中总结的经验方法,因此本书必将成为学习流程道路上的得力助手。
小米公司高级软件研发工程师 《深入浅出Ext JS 》作者 jBPM、Activiti国内推广者
徐会生
在开源BPM领域,你或许不知道“闫洪磊”是谁,但你必须听说过“咖啡兔”,否则,你不能说你曾经玩过开源BPM!
咖啡兔是我从事BPM工作几年来,少有的一位对BPM领域有较深认识的人,同时他对开源界热情的、积极的、无私的奉献精神,让我感动。我们信舟科技SuperBPM平台的诞生,离不开咖啡兔提供的巨大帮助,在此表示感谢。Activiti是一个优秀的项目,让我们能够很容易地将BPM引入我们的企业级应用中,但Activiti毕竟是国外的开源产品,它与国内很多BPM应用还是有些差异的,我们还需要对其做一些必要的个性化扩展和补充,才能用于实际的企业应用。目前国内关于Activiti的专业资料,几乎没有,而《Activiti实战》无疑是目前最佳的入门宝典,书中介绍了很多案例,都是从实际BPM应用中总结的宝贵经验。相信你们将和我一样从中受益,并快速将Activiti集成到自己的企业应用中,让Activiti绽放光芒。
北京信舟科技创始人
袁启勋
自2010年Tom Bayen离开jBoss加入Afresco公司开发出Activit5以来,在短短几年时间内,国内有大批企事业单位和大型金融机构基于Activiti5来构建各类业务流程系统,那是因为 Activiti功能稳定、性能良好,对BPMN 2规范完全支持,在API应用友好性和扩展性方面都有着卓越的表现。我从2010年接触工作流至今已15年,认为Activti5是完全可以与Ultimus 、K2等大型商用工作流引擎相媲美的大型开源产品,加上国内有着咖啡兔等众多开源奉献者和非常活跃地社区支持,相信Activiti在国内BPM界会发挥更大地作用和价值。希望这本汇集各种实战经验的书能帮助读者深入了解Activiti。
edoc2 CTO Robinson
咖啡兔先生,作为Activiti官方源代码的贡献者,也是Activiti国内传道先锋,对Activiti的背景理念、架构设计、开发运维和使用调优等方方面面的认识、理解和感受,无论从深度还是广度上,都是顶级的。有幸与先生合作,创新地提出分布式平台模式,极大地挖掘和扩展了Activiti的功能和使用范围,更为其谦虚大度的为人、高屋建瓴的思路、团队协作的精神、全面扎实的技术等所震惊,如沐春风,受益匪浅。《Activiti实战》作为先生力作,历时一年有余,全面而富有深度地带领大家认识Activiti的世界,强烈推荐!
通联支付研发中心技术部总经理
王垒
前言 Preface
为什么要写这本书
2011年年末,公司承接了一个保险类的业务系统,包含处理核心业务的ERP系统以及日常办公的OA系统,很明显这两种类型的系统都离不开工作流引擎的支持。我用一周时间对比了几个开源的工作流引擎,最后决定使用Activiti作为整套系统的工作流引擎。
现在回想起来,当初的学习过程是多么的“痛苦”啊!当时Activiti才刚满周岁,除了官方提供的尚能看得过去的用户手册之外,再无其他资料可供参考,这对于国内开发者来说尤为痛苦。仅有的用户手册全部都是英文的,为了学习Activiti只能打开翻译软件硬着头皮把手册看了一遍,当然也离不开打入引擎内部的利器—Javadocs。幸运的是,我的第一份工作(3年时间)是为政府单位开发OA系统,这有助于理解在学习Activiti过程中遇到的一些概念性的内容,在此基础上前后花了一周时间写出了第一个在本书中被讲“烂”掉的请假流程。
国内很多技术爱好者都会使用IM软件或论坛建立技术交流社区,也有一些人开设博客撰写相关技术文章。在开始学习Activiti时,很多人都尝试着去寻找这样的社区,结果由于社区规模小、热度不高,常听到学习资料匮乏以及没有成熟的Demo可供参考这样的声音。我喜欢研究技术也乐于分享,从08年就开始以博客的形式分享一些技术学习心得,在基本掌握Activiti的使用方法后就响应社区的号召在GitHub上公开了Activiti入门Demo项目—kft-activiti-demo,并在个人博客上发布了几篇与Activiti有关的博文。随着国内使用Activiti的企业越来越多,使得Activiti中文社区活跃度大大增加。最初我要花不少时间回答社区中提出的有关Activiti的问题,长此以往,同一个问题每天要回答多次,而且kft-activiti-demo也太过简单,只能作为入门参考,为了能系统地介绍Activiti,就萌发了撰写一本关于Activiti的书籍的想法。
在2012年6月,机械工业出版社华章公司的首席策划杨福川联系我,表示有意向出版一本Activiti方面的书籍。我们一拍即合,于是就有了这本书,这也让我相信机会是留给有准备的人的。这本书原本预计一年完成,不过由于工作与家庭的原因,在2013年一度中断了大半年,导致这本书的难产。在此也对期待已久的读者说声对不起,同时也是因为你们给予的支持与压力促使我最终完成这本书。
在学习和使用Activiti的过程中也遇到了一些Bug或功能缺陷,例如基本上每个初学者都会遇到的流程图中文乱码问题。对于这些问题最初会通过Bug跟踪系统向官方提交问题,在Activiti的源码从SVN切换到GitHub后就可以很方便地让全球的开发者参与进来。笔者也借助GitHub这个平台为Activiti贡献了一些代码,借此机会也呼吁技术爱好者多多参与开源。
读者对象
本书以“理论+实战”的方式引导读者学习,不仅介绍如何使用Activiti,还详细介绍了其遵循的BPMN 2.0规范,所以无论读者是以技术为主还是以业务需求为主,都适合阅读本书。虽然本书中大部分示例都是B/S架构,但不表示Activiti不能在C/S架构中使用。另外,Activiti也不是只针对Java语言的,被其官方定义为BPM平台,借助REST也可以让非Java语言的系统使用Activiti。
适合阅读本书的读者有以下几类:
Activiti用户和爱好者
Activiti代码贡献者
流程引擎相关的项目经理或者需求人员
Activiti开发者,或运维人员
使用Activiti开发流程平台的公司
如何阅读本书
本书分为四大部分:
第一部分(第1、2章)为准备篇,介绍整个体系结构及其特点,并为后面的内容配置开发环境。
第二部分(第3、4章)为基础篇,介绍两种流程设计器的使用,以及BPMN 2.0规范。
第三部分(第5~14章)为实战篇,本书中内容最多的部分,该部分以实战为主,包括流程定义、流程实例、任务、子流程、多实例、事件以及监听器等。
第四部分(第15~21章)为高级篇,通过集成各种服务、中间件来阐述Activiti不仅是引擎,更是一个BPM平台,最后还深入源码内部剖析Activiti的设计模式及PVM。
勘误和支持
由于笔者的水平有限,加之编写时间仓促,书中难免会出现一些错误或不准确的地方,恳请读者批评指正。为此,特意创建一个在线支持与应急方案的站点http://www.kafeitu.me/activiti-in-action.html。大家可以将书中的错误发布在Bug勘误表页面中,同时,在遇到任何问题时,你可以访问其Q&A页面,笔者将尽量在线上为你提供最满意的解答。书中的全部源文件除可以从华章网站下载外,还可以从笔者提供的这个网址下载,笔者也会将相应的功能更新及时更正出来。如果你有更多的宝贵意见,也欢迎发送邮件至邮箱yanhonglei@gmail.com,期待能够得到你们的真挚反馈。
致谢
首先要感谢Tom Beayens发起了这么优秀的开源项目,以及Alfresco公司Tijs Rademakers带领的Activiti团队对此项目的无偿贡献与支持。
感谢Activiti中文社区中每一位充满激情的朋友—袁启勋、临远、翔子、姜维、王垒、于广岳、第二少、小股东、陈浩、jackyrong、卖字母为生,以及名单之外的更多朋友,感谢他们长期以来对社区的支持和贡献。
感谢机械工业出版社华章公司的编辑杨福川和姜影,我始终坚信专业的事找专业的人做,两位在这两年的时间中从各个方面支持我的写作,他们的鼓励和帮助引导我顺利完成全部书稿。
感谢我的驴友们,和他们一起登山探险让我精神得以放松,希望以后的空余时间我们继续用双脚丈量大地。
最后感谢我的家人,感谢他们对我的支持和鼓励。
谨以此书献给我最亲爱的家人,以及众多热爱Activiti的朋友们!
闫洪磊(咖啡兔)
于上海
目录
本书推荐
前言
第一部分 准?备?篇
第1章?认识Activiti2
1.1?什么是Activiti2
1.2?工作流基础3
1.2.1?什么是BPM3
1.2.2?工作流生命周期4
1.2.3?什么是BPMN5
1.3?Activiti的特点5
1.4?Activiti的应用6
1.5?Activiti架构与组件7
1.6?Activiti与jBPM5比较8
1.7?本章小结9
第2章?搭建Activiti开发环境10
2.1 下载Activiti10
2.1.1 目录结构10
2.1.2?Javadocs12
2.2?环境配置检查14
2.2.1?检查并安装JDK14
2.2.2?检查并安装Ant14
2.2.3?检查并安装Maven15
2.3?配置文件介绍16
2.3.1?Activiti配置文件16
2.3.2?Maven配置文件17
2.4?Hello World18
2.4.1?最简单的流程定义18
2.4.2?创建单元测试类20
2.4.3?运行Hello World21
2.4.4?添加业务节点21
2.5?Activiti Explorer25
2.5.1?配置并运行Activiti Explorer25
2.5.2?使用Activiti Explorer26
2.6?本章小结29
第二部分?基?础?篇
第3章 流程设计工具32
3.1?基于B/S架构的流程设计器Activiti Modeler32
3.1.1?Activiti Modeler特点32
3.1.2?下载signavio-core-components33
3.1.3?配置打包与运行33
3.1.4?Windows用户打包36
3.1.5?设计请假流程37
3.1.6?导出bpmn2.0.xml41
3.2?在Activiti Explorer中使用Activiti Modeler41
3.3?基于Eclipse插件的流程设计器Activiti Designer44
3.3.1?Activiti Designer特点44
3.3.2?安装Activiti Designer45
3.3.3?设计流程46
3.3.4?自动化51
3.3.5?升级历史遗留的流程设计55
3.3.6?导入Activiti Modeler设计56
3.3.7?泳池与泳道57
3.3.8?在Activiti Designer中使用泳道与泳池58
3.4?本章小结60
第4章?Activiti与BPMN 2.0规范61
4.1?启动事件与结束事件62
4.1.1?启动事件62
4.1.2?结束事件66
4.2?顺序流69
4.2.1?标准顺序流69
4.2.2?条件顺序流69
4.3?任务70
4.3.1?用户任务70
4.3.2?脚本任务73
4.3.3?Java Service任务74
4.3.4Web Service任务75
4.3.5业务规则任务76
4.3.6邮件任务77
4.3.7Camel任务79
4.3.8Mule任务80
4.3.9手动任务81
4.3.10接收任务81
4.3.11Shell任务81
4.3.12多实例82
4.4网关85
4.4.1排他网关85
4.4.2并行网关87
4.4.3包容网关89
4.4.4事件网关90
4.5子流程与调用活动92
4.5.1子流程92
4.5.2 调用活动94
4.5.3 事件子流程95
4.5.4事务子流程97
4.6边界与中间事件98
4.6.1边界事件99
4.6.2中间捕获事件103
4.6.3中间抛出事件107
4.7监听器109
4.7.1执行监听器109
4.7.2任务监听器111
4.8本章小结112
第三部分实战篇
第5章用户与组及部署管理116
5.1用户与组116
5.1.1用户117
5.1.2组118
5.1.3用户与组的关系119
5.1.4用户任务中的用户与组120
5.2部署流程资源126
5.2.1classpath方式126
5.2.2InputStream方式128
5.2.3字符串方式128
5.2.4zip/bar格式压缩包方式129
5.3流程部署及资源读取131
5.3.1读取已部署流程定义133
5.3.2从客户端部署流程134
5.3.3读取流程定义的XML137
5.3.4读取流程定义的图片及图片中的中文乱码138
5.3.5删除部署143
5.4本章小结144
第6章 任务表单145
6.1动态表单145
6.1.1流程定义146
6.1.2单元测试151
6.1.3在Activiti Explorer中运行流程154
6.2实现自己的Activiti Explorer160
6.2.1完善身份验证功能160
6.2.2流程启动表单162
6.2.3任务签收与办理165
6.2.4自定义表单的字段类型169
6.3 外置表单171
6.3.1流程定义172
6.3.2单元测试173
6.3.3自定义表单引擎174
6.3.4 读取流程启动表单175
6.3.5任务签收与办理179
6.4本章小结181
第7章 Activiti与容器集成182
7.1 流程引擎工厂182
7.1.1 创建引擎的方式182
7.1.2 配置 ProcessEngineFactory-Bean183
7.2 自动部署流程定义187
7.3 表达式189
7.3.1 表达式基础189
7.3.2 表达式示例190
7.3.3 使用Spring管理变量193
7.4 监听器195
7.5 Spring容器集成应用实例199
7.5.1 业务建模200
7.5.2 启动流程200
7.5.3 任务读取203
7.5.4 任务办理204
7.6 使用Spring注解初始化引擎208
7.6.1 使用@EnableActiviti注解208
7.6.2 使用Spring Boot初始化引擎210
7.7 CDI模块212
7.7.1 启动示例212
7.7.2 引擎配置与流程定义213
7.7.3 流程定义与启动214
7.7.4 任务办理与完成218
7.7.5 事件监听220
7.8 本章小结221
第8章 邮件服务222
8.1 配置与测试222
8.1.1 搭建邮件系统222
8.1.2 通过Activiti的邮件任务发送邮件226
8.1.3 使用Gmail发送邮件227
8.2 与业务集成229
8.2.1 即时发送邮件229
8.2.2 定时发送邮件231
8.3 本章小结233
第9章 多实例234
9.1 非用户任务234
9.2 用户任务多实例236
9.2.1 顺序方式办理236
9.2.2 并行方式办理237
9.2.3 设置结束条件238
9.3 应用实例—请假会签240
9.3.1 流程定义240
9.3.2 任务办理243
9.4 审批意见245
9.5 本章小结247
第10章 子流程与调用活动248
10.1 子流程249
10.1.1 流程定义250
10.1.2 流程办理253
10.1.3 分析流程数据255
10.2 调用活动256
10.2.1 流程定义256
10.2.2 单元测试258
10.2.3 流程办理259
10.2.4 分析流程数据260
10.3 事件子流程261
10.3.1 流程定义262
10.3.2 单元测试263
10.4 多实例支持264
10.5 本章小结266
第11章 事件267
11.1 启动事件267
11.1.1 定时启动事件267
11.1.2 消息启动事件271
11.2 结束事件274
11.3 边界事件276
11.3.1 异常边界事件276
11.3.2 消息边界事件278
11.3.3 信号边界事件282
11.4 中间事件284
11.5 本章小结286
第12章 用户任务与附件287
12.1 用户任务288
12.1.1 改进任务列表288
12.1.2 改进任务表单289
12.1.3 任务相关人员291
12.1.4 反签收任务295
12.1.5 候选人与候选组296
12.1.6 改进任务查询298
12.2 子任务299
12.3 手动任务302
12.4 附件303
12.5 改进意见列表305
12.6 任务委派307
12.6.1 单元测试308
12.6.2 任务表单中的委派309
12.7 本章小结310
第13章 流程数据查询与跟踪312
13.1 Query API简介312
13.2 运行时数据查询314
13.2.1 任务查询314
13.2.2 查询参与的流程317
13.3 流程图跟踪320
13.4 历史数据查询325
13.4.1 查询历史活动及表单325
13.4.2 查询已归档流程326
13.5 基于MyBatis的CustomSql查询327
13.5.1 定义Mapper接口329
13.5.2 展示数据330
13.6 本章小结330
第14章 管理员特性331
14.1 流程状态331
14.1.1 流程定义状态331
14.1.2 作业查询334
14.1.3 流程实例状态336
14.2 作业管理338
14.2.1 作业执行原理339
14.2.2 作业执行异常339
14.2.3 独占与异步341
14.3 删除流程实例345
14.4 流程定义权限控制347
14.4.1 权限拦截347
14.4.2 设置候选启动人和候选启动组349
14.4.3 读取候选启动数据351
14.5 读取引擎属性353
14.6 数据库查询353
14.7 用户与组355
14.8 本章小结356
第四部分 高级篇
第15章 集成WebService358
15.1发布WebService服务359
15.2在流程中定义WebService任务362
15.3 在流程中调用WebService364
15.4本章小结365
第16章 集成规则引擎367
16.1 定义流程与规则367
16.1.1部署规则文件369
16.1.2单元测试369
16.2本章小结371
第17章 集成JPA372
17.1配置JPA372
17.1.1Standalone模式373
17.1.2Spring模式373
17.2JPA版本的请假流程374
17.2.1启动流程时持久化JPA实体375
17.2.2更改JPA实体属性378
17.2.3清理历史表单数据381
17.3本章小结382
第18章 集成ESB383
18.1Camel简介与快速入门383
18.2在流程中调用Camel385
18.2.1Camel依赖及配置386
18.2.2定义Camel路由387
18.2.3执行单元测试388
18.2.4URI输入参数388
18.2.5URI输出参数390
18.3异步Camel任务391
18.4在Camel中启动流程391
18.5集成Mule392
18.5.1Mule快速入门393
18.5.2Mule、Spring与Activiti集成394
18.5.3在流程中调用Mule396
18.6本章小结398
第19章 统一身份管理399
19.1一套典型的身份系统399
19.2引擎身份接口方式401
19.3用视图代替物理表406
19.4集成LDAP406
19.5本章小结409
第20章 REST服务410
20.1通信协议简介410
20.2REST API概述411
20.3发布REST API413
20.3.1通过浏览器访问414
20.3.2通过HttpClient访问415
20.3.3通过Restlet访问416
20.3.4通过Apache CXF访问417
20.4集成REST API417
20.4.1依赖及配置文件418
20.4.2通过Ajax访问419
20.5完整示例420
20.5.1部署流程421
20.5.2查询Deployment422
20.5.3查询流程定义423
20.5.4启动流程424
20.5.5读取流程变量428
20.5.6查询任务429
20.5.7签收任务431
20.5.8完成任务431
20.5.9查询历史数据433
20.6集成流程图跟踪组件Diagram Viewer434
20.6.1准备资源文件434
20.6.2准备配置文件434
20.6.3访问Diagram Viewer跟踪流程435
20.7基于REST服务搭建流程中心437
20.7.1基础架构438
20.7.2表单模式选型439
20.7.3统一的组件440
20.7.4事务管理440
20.8集成流程设计器Activiti Modeler440
20.8.1准备资源文件441
20.8.2准备配置文件442
20.8.3更改默认配置443
20.8.4创建模型443
20.8.5导出模型的流程XML445
20.8.6把模型转换为流程定义446
20.8.7把流程定义转换为模型447
20.8.8删除模型449
20.9本章小结449
第21章 入侵Activiti450
21.1解析BPMN文件450
21.1.1BpmnModel对象与XML之间的转换451
21.1.2动态创建流程454
21.1.3BPMN解析处理器457
21.2全局事件处理器461
21.2.1定义事件处理器463
21.2.2处理捕获的事件467
21.2.3事件处理器的异常处理469
21.2.4动态注册事件处理器470
21.2.5任务自动转办471
21.2.6事件日志474
21.3命令与拦截器477
21.3.1命令与拦截器运行机制478
21.3.2自定义命令480
21.3.3命令拦截器482
21.4流程虚拟机—PVM484
21.4.1简述PVM484
21.4.2Hello PVM484
21.4.3PVM进阶487
21.5本章小结489
第一部分
准备篇
工作流(Work Flow)引擎被广泛应用于各种信息化系统中,将原本散乱甚至混乱的业务梳理后制定成业务规范流程,进而约束业务的规范化处理和运转。需求人员、开发人员共同协作制定了符合BPMN 2.0规范的流程定义,之后将其部署到工作流引擎中,由它自动驱动业务流程的进行。
本部分作为准备篇,第1章先介绍了什么是Activiti及其历史背景,然后介绍工作流、BPM、BPMN等概念,使初次接触工作流的读者能快速认识相关概念及各种规范。第2章内容从搭建开发环境开始,之后又介绍了Activiti的Hello World,帮助读者快速入门。
第1章
认识Activiti
很多人对工作流(Workflow)应该不陌生。生活中到处都是活生生的“流”:在单位要请假,首先要找领导审批,在领导审批通过之后申请才获准;从网上购物,下单的那一刻就已经触发了一条工作流,此时可以跟踪购物流程,什么时间下单、什么时间付款、什么时候发货、什么时候收到货,在快递单上签字的时候才等于一条工作流程结束了。
工作流应用广泛,在由任务驱动的各种系统中都能见到它的身影,例如,CRM、ERP、ECM、BI、OA等。在企业应用中还有很多产品或平台集成工作流引擎,用来处理系统运行过程中发起的业务流程。
工作流总是以任务(Task)的形式驱动人处理业务或者驱动业务系统自动完成作业。有了工作流引擎之后,我们不必一直等待其他人的工作进度,直白地说,我们只需要关心系统首页的待办任务数即可,由系统提醒当前有多少待办任务需要处理。
1.1什么是Activiti
大家第一次接触Activiti的时候不理解它为什么要叫这个名字,从词典中也没有找到对它的解释。可能有人会想到另外一个单词Activity(活动),与Activiti仅一个字母之差。在工作流方面有些基础的读者或许能很快理解,业务流程由多个环节串联起来并且每个环节被赋予任务,而每个任务又可以分为多个活动。举个日常的例子—网上购物的下单环节,首先需要搜索到要购买的商品,然后将其加入到购物车,最后下单填写邮寄地址并付款。这个例子中的每一动作都可以称为活动(Activity),也就是业务流程中最小的组成部分。多个活动在英文中肯定要用复数形式,即Activities;最后以复数化简的方式标示活动的集合,以此来诠释Activiti与工作流的目的与设计。
此项目是Tom Bayen(jBPM创始人)自2010年离开jBoss加入Alfresco公司后的又一力作:第一版在2010年5月发布,当时仅支持最简单的流程处理,之后的版本陆续完善了对BPMN 2.0规范的支持。
值得一提的是,参与项目开发的除了Tom Bayen和十几位核心开发人员之外,还有其他公司的员工参与,例如,SpringSource、MuleSoft、Salves、Signavio、FuseSource、NextLevel等。
Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快速、稳定的BPMN2.0流程引擎。Activiti是在ApacheV2许可下发布的,可以运行在任何类型的Java程序中,例如服务器、集群、云服务等。Activiti可以完美地与Spring集成。同时,基于简约思想的设计使Activiti非常轻量级。
Activiti有着活跃的社区,而且越来越多的企业都选择Activiti作为自己的流程引擎或者将其嵌入到自己的系统平台中(例如ESB)。
接下来简单了解一下工作流及其相关规范的历史。
1.2工作流基础
1.2.1什么是BPM
BPM是Business Process Management的缩写,中文含义是业务流程管理,是一套达成企业各种业务环节整合的全面管理模式。
BPM是为了实现一定的经营目的而执行的一系列逻辑相关的活动的集合。业务流程的输出是满足市场需要的产品或服务。根据功能、管理范围等的不同,企业流程管理一般分为生产流程层、运作层、计划层和战略层四个层次。BMP是根据业务环境的变化,推进人与人之间、人与系统之间,以及系统与系统之间的整合及调整的经营方法与解决方案的IT工具。
BPM最早是由工作流和企业应用集成(Enterprise Application Intergration)逐步融合而发展起来的,当时是为了满足无纸化办公需求(这也是最早的需求之一)。笔者早期参与OA系统开发时曾经见到过“原始的”工作流—没有工作流引擎,整个流程均使用一系列单独为不同任务节点设计的页面串联起来,完成一个节点后在数据库标记当前任务的名称,以此做到“流程驱动”。
随着时间的推移,BPM的定义范围逐步扩展,不仅用来满足无纸化办公需求,现在BPM是一种企业集成技术,作为对面向服务系统架构SOA(Service-Oriented Architecture)、企业应用集成EAI (Enterprise Application Integration)、企业服务总线ESB(Enterprise Service Bus)的补充。
从概念上来说,BPM包含两个不同方面的意思:管理规范和软件工程。各大BPM供应商长期以来试图抽象这两个不同的方面,但是依然混乱。
作为管理规范,BPM是每一个战略管理者的责任。BPM是组织必须执行的核心业务流程,包含了企业价值和如何提供其实现。作为日常工作的一部分,业务系统可以借助模型和流程规范地定义业务流程。BPM流程图表达的是执行流程的步骤,已完成特定目标。特别说明的是这些模型用于人与人的沟通。这些都是诠释未决的,这意味着它们可以包含更高级别有价值的信息而不包括不必要的细节。这种诠释未决的过程模型也被称为抽象业务流程(Abstract Business Processes.)。
BPM作为软件工程时可以由BPM系统(BPMS)执行可执行的业务流程。可执行的业务流程是在一个流程基础上表示不同的流程顺序。流程图完全可以看做一个抽象的业务流程。可执行流程不同于抽象业务流程,因为它总是以最简单的方式运行。这部分内容也是被大多数厂商认同并接受的。
1.2.2工作流生命周期
一个完整的工作流生命周期会经过5步,并且迭代循环,如图1-1所示。
定义:工作流生命周期总是从流程定义开始。此阶段的任务主要是收集业务需求并转化为流程定义。一般由业务需求人员进行,然后交由开发人员加工转化为计算机可以识别的流程定义。
发布:由开发人员打包各种资源,然后在系统管理(平台)中发布流程定义。在具体的流程引擎中包括流程定义文件(bpmn20.xml结尾)、自定义表单、任务监听类。
执行:具体的流程引擎(例如,Activiti)按照事先定义的流程处理路线以任务驱动的方式执行业务流程。
监控:此阶段是依赖执行阶段。业务人员在办理任务的同时收集每个任务(Task)的结果,然后根据结果做出相应处理,例如,在采购办公用品流程中,在通过领导审批之后,采购人员就要根据申请单外出采购。
优化:在此阶段,一个完整的流程已经结束,或许能满足业务需求,或许需要优化,而糟糕的情况是需要重新设计(流程没结束就异常终止),优化与设计正是此阶段需要处理的。根据整个流程的运行过程结果分析问题的根源,然后在此基础上进一步改进,并再次开始一个新的周期。
1.2.3什么是BPMN
Business Process Modeling Notation,简称BPMN,中文译为业务流程建模标注,是由BPMN标准组织发布的,其第一版BPMN 1.0规范于2004年5月发布。经过多年的改进新的规范BPMN 2.0于2011年发布。之后各大厂商、开源社区均基于2.0规范设计自己的流程引擎,结束了各个厂商“各自为政”的局面,相应地统一了标准,从而利于以后的产品迁移。
BPMN定义了业务流程图,其基于流程图技术,同时对创建业务流程操作的图形化模型进行了裁减。业务流程的模型就是图形化对象的网图,包括活动(也可以说工作)和定义操作顺序的流控制。
在BPMN 1.x版本中的一些概念,如人工任务、可以执行脚本、自动决策等,都是独立于供应商的可视化标准化的方式。在BPMN 2.0规范中重点聚焦在如何执行语义和一个被业界认可的通用交换格式。这意味着基于BPMN 2.0的流程建模不仅在流程设计器上可以通用,还可以在任何符合BPMN 2.0规范的流程引擎上执行。
关于BPMN的细节内容有很多,官方文档足足有500多页,本书会在第4章介绍Activiti支持的BPMN 2.0规范以及Activiti在BPMN 2.0规范基础上的扩展。关于BPMN的其他内容本书就不一一列举了,有兴趣的读者可以仔细阅读官方文档。
1.3Activiti的特点
1. 数据持久化
Activiti的设计思想是简洁、快速。有过应用开发经验的开发人员都知道应用的瓶颈体现在和数据库交换数据的过程中,针对这一点Activiti选择了使用MyBatis,从而可以通过最优的SQL语句执行Command,仅凭如此就能让引擎在速度上保持最高的性能。
2. 引擎Service接口
Activiti引擎提供了七大Service接口,均通过ProcessEngine获取,并且支持链式API编程风格。表1-1简单列出七个Service接口及其作用,具体使用会在后面的章节陆续介绍。
表1-1Activiti引擎的七大Service接口
Service接口 作 用
RepositoryService 流程仓库Service,用于管理流程仓库,例如,部署、删除、读取流程资源
IdentifyService 身份Service,可以管理和查询用户、组之间的关系
RuntimeService 运行时Service,可以处理所有正在运行状态的流程实例、任务等
TaskService 任务Service,用于管理、查询任务,例如,签收、办理、指派等
FormService 表单Service,用于读取和流程、任务相关的表单数据
HistoryService 历史Service,可以查询所有历史数据,例如,流程实例、任务、活动、变量、附件等
ManagementService 引擎管理Service,和具体业务无关,主要是可以查询引擎配置、数据库、作业等
3. 流程设计器
在jBPM4时代有专门的Eclipse插件可以用来设计jPDL,同样Activiti团队也专门设计了用来设计BPMN 2.0规范的流程设计器—Eclipse Designer。此外还有Signavio公司为Activiti定制的基于Web的Activiti Modeler流程设计器。
4. 原生支持Spring
Activiti原生支持Spring,这一点对企业应用来说尤为重要:可以很轻松地进行Spring集成,非常方便管理事务和解析表达式(Expression)。
5. 分离运行时与历史数据
Activiti继承自jBPM4,在表结构设计方面也遵循运行时与历史数据的分离,这样的设计可以快速读取运行时数据,仅当需要查询历史数据时再从专门的历史数据表中读取。这种设计方式可以大幅提高数据的存取效率,尤其是当数据日积月累时依然能够快速反应。
1.4Activiti的应用
目前Activiti在国外已被很多厂商所使用,甚至有人专门成立了公司来培训Activiti的使用。Activiti在国内的发展正在呈直线趋势上升,已经成立了由很多热心的技术爱好者参与的技术社区。目前,很多新项目、新产品都开始采用Activiti作为新一代工作流引擎。
1. 在系统集成方面应用
与ESB(Enterprise Service Bus,企业服务总线)整合,例如Mule。
与规则引擎(Rule Engine)整合,例如JBoss Drools。
嵌入已有系统平台,例如,很多公司都开发了自己的系统平台,在其中嵌入Activiti作为平台的一部分。
2. 在其他产品中应用
Alfresco公司的ECM(Enterprise Content Management)产品Alfresco在企业中应用广泛,主要涉及文档管理、协作、记录管理、知识库管理、Web内容管理等。
如果企业或客户正在使用Alfresco管理文档,那么针对文档管理流程设计的流程定义可以直接部署在Alfresco上使用;如果之前没有接触过jBPM而现在学会了使用Activiti,那么不用再去学习其他的流程引擎。关于Activiti与jBPM的区别在1.6节会提到。
在Activiti没有发布之前一直使用jBPM作为流程引擎,在Activiti成熟以后Alfresco同时支持两者,当然在以后的某个时间可能会取消对jBPM的支持。
1.5Activiti架构与组件
Activiti架构中最重要的肯定是引擎,当然还有刚刚提到的外部工具和组件,如图1-2所示。
图 1-2Activiti架构图
下面依次介绍Activiti架构图中的各个组件。
Activiti Engine:作为最核心的模块,提供针对BPMN 2.0规范的解析、执行、创建、管理(任务、流程实例)、查询历史记录并根据结果生成报表。
Activiti Modeler:是模型设计器,其并非由Activiti公司所开发,而是由业界认可的Signavio公司赠送的(Signavio原本是收费的产品,现在被免费授权给Activiti用户使用)。适用于业务人员把需求转换为规范流程定义。
Activiti Designer:功能和Activiti Modeler类似,同样提供了基于BPMN 2.0规范的可视化设计功能,但是目前还没有完全支持BPMN规范的定义。适用于开发人员,可以把业务需求人员用Signavio设计的流程定义(XML格式)导入到Designer中,从而让开发人员将其进一步加工成为可以运行的流程定义。
Activiti Explorer:可以用来管理仓库、用户、组,启动流程、任务办理等。此组件使用REST风格API(目的在于让开发人员快速入门),提供一个基础的设计模型。如果业务简单,也可以直接使用无需开发。还可以作为后台管理员的流程、任务管理系统使用。
Activiti REST:提供Restful风格的服务,允许客户端以JSON的方式与引擎的REST API交互,通用的协议具有跨平台、跨语言的特性。
1.6Activiti与jBPM5比较
目前流行的工作流引擎有Activiti和jBPM5,而在jBPM5发布以前大多数项目、平台都是基于jBPM3、jBPM4开发的。本节内容从技术和实际应用上对Activiti和jBPM5进行比较。表1-2从技术层面比较了两者的区别。
表1-2Activiti与jBPM5的技术层面对比
技 术 组 成 Activiti jBPM
ORM框架 MyBatis3 Hibernate3
持久化标准 无 EJB JPA规范
事务管理 MyBatis自带/Spring集成事务 Bitronix,基于JTA事务管理
数据库连接方式 Jdbc/DataSource Jdbc/DataSource
Spring支持 原生支持Spring,在流程中可以使用Spring代理的Bean作为表达式的一部分,并且支持JPA及事务管理 默认没有提供对Spring的支持
支持的数据库 Oracle、SQL Server、MySQL、H2、内存数据库等 Oracle、SQL Server、MySQL、内存数据库等
设计模式 命令模式、观察者模式等
内部服务通信 Service间通过API调用 基于Apache Mina异步通信
集成接口 SOAP、Mule、RESTful 消息通信
支持的流程格式 BPMN2、xPDL、jPDL等(由PVM实现) 目前仅只支持BPMN2 xml
引擎核心 PVM(流程虚拟机) Drools
技术前身 jBPM3、jBPM4 Drools Flow
团队成员 除了Alfresco公司的雇员之外还有Spring-Source、MuleSoft、Salves、Signavio、FuseSource、NextLevel等公司的员工加入 有一个专门的团队,此外还有一些个人参与者
附加工具 提供了基于Eclipse插件的流程设计器—Eclipse Designer,提供基于REST风格的Activiti Explorer,可以用来管理仓库、用户、组、启动流程、任务办理等 同样提供Eclipse插件和一个Web应用管理流程
发布周期 固定每两个月发布一版,其中包括:引擎、Eclipse Designer、Activiti Explorer、REST应用 jBPM的发布周期相对来说不太固定,发布内容包括引擎及基于Eclipse的设计器
Activiti是基于jBPM4设计的衍生版本,如果选择Activiti可以继续沿用jBPM的思想理念设计、整合Activiti到项目或平台中,这也是相对于jBPM5来说的一个优势;相反,对于jBPM5来说要花点时间重新接受开发者的设计思想。
在各个流程引擎社区中有很多关于该如何选择Activiti和jBPM5的讨论,这两者有着很多相似的地方,争论主要是对规则引擎的支持:jBPM5是基于Drool Flow所有自然深度继承而来的规则引擎Drools;早期的Activiti功能比较简单,后来陆续添加的新特性也支持规则引擎Drools,开发人员只要简单配置规则接口即可达到与jBPM5一样的效果。
1.7本章小结
本章内容主要是以初识工作流和Activiti的角度去讲解什么是工作流、什么是Activiti。从Activiti项目的发起、特点、应用、架构,以及与其他同类产品比较的角度在概念层给读者一个引导和认识。
作为一个开发人员,能够知道为什么需要学习Activiti,Activiti能帮助企业解决什么问题,为什么要选择Activiti而不是其他的工作流引擎。
看到这里你或许会不耐烦了,会想起一句话:“Talk is cheap. Show me the code”。下一章将带领你体验Activiti的Hello World。
第2章
搭建Activiti开发环境
第1章介绍了关于工作流的概念及Activiti的发起背景,带领大家大体了解Activiti能帮助企业解决什么问题,作为一个开发人员为什么需要学习Activiti,还简单介绍了一下Activiti的架构以及与其他工作流引擎的区别。本章将带领你一步一步搭建开发环境,并运行期待已久的Hello World程序。
2.1 下载Activiti
通过浏览器访问页面:http://activiti.org/download.html,其中列出自Activiti发布以来历次版本的压缩包和相关文档(10分钟入门、用户手册以及JavaDoc)。
在“Latest Release”处下载下面的压缩包,在笔者写作本书时最新版本为Activiti 5.9。细心的读者可能注意到:在“Older releases”的发布列表中5.6版本之前基本上是一个月发布一版,从5.6版本之后基本固定为2~3个月发布一版。
初学者很难理解的地方是:为什么Activiti的版本从5.0开始?在第1章的内容中提到过Activiti是基于jBPM4开发的,所以就直接版本号累加,最终从5.0版本开始。
2.1.1 目录结构
在最初撰写本章内容时最新的版本为5.9,目录结构和下一版本(5.10)一致;但是从5.11版本开始发生了比较大的变化,所以分两个小部分说明两者在目录结构上的不同。本书完稿时最新版本为5.16.3。
1. 5.10及之前的版本
对于Activiti 5.10及之前的版本,解压压缩包之后目录结构如图2-1所示。
下面我们来详细介绍图2-1中的目录结构。
docs,该目录包含了三种文档:javadocs、userguide、xsd。
m javadocs:包名按照功能模块划分,org.activiti.engine.*,接下来在2.1.2节中详细介绍。
m userguide:用户手册,包含环境配置、10分钟快速入门,以及各个功能模块的使用教程。
m xsd:包含BPMN 2.0规范的XSD文件,以及Activiti扩展的自定义标签XSD。
setup:用来构建、启动Activiti Explorer演示程序。通过ant demo.start命令即可自动下载tomcat,配置数据库,最后打开浏览器访问应用。具体操作将在2.4节详细介绍。
workspace:该目录包含了各种应用的实例程序,都以单元测试的形式展示功能的使用方式。读者可以直接将项目导入Eclipse查看其源代码,从而通过断点调试来学习。
2. 5.11及之后的版本
对于Activiti 5.11及之后的版本,解压压缩包之后目录结构如图2-2所示。
下面我们来详细介绍图2-2中的目录结构。
Database,该目录包含了针对Activiti引擎表的创建(create)、删除(drop)及版本升级(upgrade)三种类型的脚本:创建、删除类型的脚本以“activiti.oracle.[create|drop].[identity|engine|history].sql”这样的结构命名SQL脚本文件;升级脚本以“activiti.oracle.upgradestep.[5x].to.[5(x+1)].history”的结构命名SQL脚本文件,其中“x”表示小版本号。
docs,该目录也包含了三种文档:javadocs、userguide、xsd。
m javadocs:包名按照功能模块划分,org.activiti.engine.*,接下来在2.1.2节中详细介绍。
m userguide:用户手册,包含环境配置、10分钟快速入门,以及各个功能模块的使用教程。
m xsd:包含与流程定义相关的scheme,例如,BPMN 2.0、BPMNDI及Activiti在BPMN 2.0规范上扩展的元素。
libs:相比5.10之前的版本移除了第三方的依赖,仅仅包含了Activiti引擎的各个模块(engine、bpmn-converter、bpmn-model、camel、cdi、cxf、json-converter、modeler、mule、osgi、rest、spring)的class文件以及源码。这么做的目的是通过Maven管理依赖(在2.3.2节介绍了如何使用Maven管理依赖)避免因Jar版本不同导致冲突(大部分开源项目及国外的项目都使用Maven管理依赖,所以强烈建议没接触过Maven的读者花点时间学习使用它)。
wars:从5.11版本开始对explorer模块和rest模块进行了拆分,使得rest模块可以独立运行;运行explorer的方式也随之发生了变化,不再使用ant脚本(2.5.1节)运行,而是提供一个独立的war包,自行部署tomcat或jboss等Web服务器中。
2.1.2Javadocs
Javadocs是开发人员与Activiti交流的“语言”,在开始编写第一个Hello World之前先来大致了解一下每个包(也就是module)的功能及各个接口的作用。当然不太想了解这些内容的读者也可以暂时跳过这里,直接开始搭建环境并编写第一个Hello World程序,最后再回过头来了解。
Javadocs一共包含11个package,下面依次介绍它们的作用范围。
1)org.activiti.engine:包含上一章提到的七大类Service接口、异常类定义和流程引擎(Process Engine)及流程引擎配置(Process Engine Configuration),另外还定义了一些运行时(Runtime)异常类。
2)org.activiti.engine.delegate:该包定义了处理流程的行为、监听事件的规范。在实际应用中可以在流程定义中配置实现了监听接口的类处理业务逻辑,例如可以在流程结束时由系统自动归档。在第1章中提到过Activiti使用的是监听者(Observer)模式,在流程运行过程中(任务完成)引擎会遍历注册的监听并依次调用。
3)org.activiti.engine.form:该包应用在内置表单的场景下,在一些企业或客户要求自定义表单的需求中使用。定义表单有两种方式:第一种是直接在流程定义中设置每个节点的表单内容,可以设置每个字段(Field)的类型、是否可以编辑等属性;另外一种就是通过外置表单的形式,通过formkey指定外置表单文件的名称,类型可以是.xml或.form。表单的读取、提交均可以通过FormService接口完成。
4)org.activiti.engine.history:该包包含了历史记录查询对象及查询结果的历史数据对象接口。可以查询历史流程实例(HistoricProcessInstance)、历史任务(HistoricTask)、历史活动(HistoricActivity)、历史详细(HistoricDetail)等。在13.3节中,流程跟踪功能就是通过HistoryService接口进行查询来实现的。
5)org.activiti.engine.identity:该包可以用来管理身份和认证,功能依托IdentityService接口。在这里需要说明一下,Activiti有自己的Identify模块,因为流程中的用户任务(userTask)需要指派给某个人或某个角色。关于任务指派在后面12.1.3节会介绍。讲到这里顺便提一下,关于身份认证管理功能和企业中已有身份认证管理模块的冲突解决办法将在第19章中详细讨论。
6)org.activiti.engine.management:该包主要是用来实现针对流程引擎的管理功能,通过调用接口ManagementService可以监控引擎状态、任务调度、数据库数据读取。
7)org.activiti.engine.query:该包没有具体的功能,定义了查询的共有特性。细心的读者在查看API的时候会发现XxxQuery接口均继承自Query,并且使用了JDK 5提供的泛型。
8)org.activiti.engine.repository:该包包含了针对流程资源的管理与查询。依托RepositoryService接口可以部署流程定义、自定义表单、规则等文件,还可以读取流程图片、流程定义(bpmn20.xml)文件。
9)org.activiti.engine.runtime:在第1章中提到了Activiti把流程数据划分为两种:运行时数据和历史数据。刚刚介绍了org.activiti.engine.history包依托HistoryService查询历史数据,相应地,也可以通过RuntimeService接口查询运行时数据,例如可以查询出当前用户的待签收任务、待处理任务,以及正在处理中的流程实例对象。当然除了查询数据之外还要启动流程,然后得到实现了ProcessInstance接口的实例,从而在业务中根据流程实例继续处理业务信息。另外在Activiti 5.9版本中添加了对流程定义状态的控制功能:挂起和恢复。
10)org.activiti.engine.task:该包包含任务对象的定义,依托TaskService接口可以对任务(Task)全面管理,例如,任务创建、删除、任务指派、批注管理、附件管理以及变量查询。在12章将会逐一介绍TaskService操控Task。
11)org.activiti.engine.test:顾名思义,该包针对快速创建测试用例提供基类和注解(Annotation)。现在已经有很多公司或开发人员使用测试驱动(Test Driven Development,TDD)方式编码,Activiti提供的这一功能大大方便了验证流程定义中业务逻辑正确与否的效率。此包不仅提供了测试基类,还支持注解(Annotation)方式自动部署流程定义。本书的很多示例也将先以TDD方式引导读者学习。
2.2环境配置检查
在准备搭建开发环境之前需要读者检查自己的系统环境是否已安装JDK、Ant和Maven,如果已经安装,那么需要检查版本是否满足Activiti的最低要求。
2.2.1检查并安装JDK
在Activiti 5.10版本之前要求JDK的最低版本为JDK 1.5(即5.0),从Activiti 5.11版本开始要求最低JDK为1.6(即6.0);如果本地配置低于不同版本的最低要求,那么需要到Oracle官方下载(http://www.oracle.com/technetwork/java/javase/downloads/index.html)并安装。
在检查JDK前先确认JAVA_HOME已正确设置。
1)Windows用户:
C:\Documents and Settings\henryyan>> echo %JAVA_HOME%
C:\Documents and Settings\henryyan>> java -version
2) Linux、Mac OS用户:
henryyan@hy-hp ~ echo $JAVA_HOME
henryyan@hy-hp ~ java -version
笔者的Java环境如图2-3所示。
图2-3在Linux系统中安装配置Java环境
2.2.2检查并安装Ant
Activiti Explorer在1.5节已经提到了,可以快速运行示例程序。示例程序需要使用Ant构建运行。
官方要求Ant的版本为1.8.1或更高,如果本地没有Ant安装软件,那么先从Apache网站下载(http://ant.apache.org/bindownload.cgi),然后将其解压并安装到本地目录。
检查Ant前先确认ANT_HOME是否已正确设置。
1)Windows用户:
C:\Documents and Settings\henryyan> echo %ANT_HOME%
C:\Documents and Settings\henryyan> ant -version
2)Linux、MacOS用户:
henryyan@hy-hp ~ echo $ANT_HOME
henryyan@hy-hp ~ ant -version
笔者的Ant环境如图2-4所示。
图2-4在Linux系统中安装配置Ant环境
2.2.3检查并安装Maven
Maven作为一款优秀的构建工具(不仅仅是构建)已经被全球很多的开发者、开源组织使用,近年来也被国内越来越多的公司和组织使用。未接触Maven或不太熟悉相关内容的读者可以参考国内Maven专家许晓斌著的《Maven实战》。
Activiti引擎的源码也是使用Maven构建的,并且公开Maven仓库方便开发者下载引擎。本书所有示例程序都将使用Maven管理依赖,所以读者一定要检查Maven的配置是否正确。
如果还未安装Maven,那么先从Apache网站下载(http://maven.apache.org/download.html),然后将其解压到本地目录。
检查Maven前要先确认MVN_HOME是否设置。
1)Windows用户:
C:\Documents and Settings\henryyan> echo %MVN_HOME%
C:\Documents and Settings\henryyan>mvn -v
2)Linux、Mac OS用户:
henryyan@hy-hp~echo $MVN_HOME
henryyan@hy-hp ~ mvn-v
笔者的Maven环境如图2-5所示。
图2-5在Linux系统中安装配置Maven环境
2.3配置文件介绍
本节来介绍两个配置文件,一个是Maven的pom.xml文件,另外一个就是Activiti的默认配置文件activiti.cfg.xml。
2.3.1Activiti配置文件
在bpmn20-example工程的src/test/resources中有一个activiti.cfg.xml文件,此文件就是Activiti的配置文件,用来定义引擎初始化参数、bean、邮件服务器及各种监听器。
代码清单2-1展示了一个标准的Activiti配置文件。
代码清单2-1标准的Activiti配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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" #1
class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration"> #2
<property name="databaseType" value="h2" /> #3-S
<property name="databaseSchemaUpdate" value="true"/>
<property name="jobExecutorActivate" value="false" />
<property name="history" value="full" /> #3-E
</bean>
</beans>
在第1章中提到过Activiti和Spring无缝集成,熟悉Spring的读者一眼就看出来这就是Spring的配置文件。是的,在XML的命名空间中定义了spring-bean,即http://www.springframework.org/schema/beans。
#1处定义了一个id为processEngineConfiguration的bean对象,其中processEngine-Configuration即为Activiti默认的引擎配置管理器名称。
#2处指定了一个具体的Java类,由Spring负责实例化引擎配置管理器并注入#3处的一系列配置参数。此处的引擎配置管理器是基于内存数据库的,因为其速度快,常用于测试环境。
#3处的配置参数含义如表2-1所示。
表2-1Activiti引擎配置管理器的参数及其含义
属 性 名 称 属 性 说 明
databaseType 数据库类型,默认为H2。当使用非H2数据库时需要特别声明,例如在生产环境中一般不会使用H2作为数据库。目前Activiti支持的数据库有:H2、MySQl、Oracle、Postgres、 MSSQl、DB2
databaseSchemaUpdate 用来声明数据库脚本更新策略,和hibernate的机制类似。取值如下:
false,什么都不做
true,当Activiti的表不存在时自动创建;当Activiti的jar文件中定义的版本号与数据库中记录的版本号不一致时会自动执行相应的升级脚本,并且会记录升级过程
create-drop,创建引擎时执行初始化脚本,引擎销毁时执行删除数据库脚本
jobExecutorActivate 用来设置是否启用作业执行功能,默认为false。在将该值设置为true之后,引擎会不间断地刷新数据库的作业表,检查是否存在需要执行的作业,有则触发作业的执行。作业的来源有多种,例如各种时间事件或异步任务执行
history 用来设置记录历史的级别,默认为audit。支持以下几种类型:
none,不保存任何历史记录,可以提高系统性能
activity,保存所有的流程实例、任务、活动信息
audit,也是Activiti的默认级别,保存所有的流程实例、任务、活动、表单属性
full: 最完整的历史记录,除了包含audit级别的信息之外还保存详细,例如流程变量、表单属性
2.3.2Maven配置文件
讲解Maven配置文件主要是针对没有接触过Maven或对Maven不太熟悉的读者。
打开bpmn20-example中的pom.xml查看文件内容。下面以代码片段的形式说明Maven配置文件的作用。
要在Maven项目中使用一个依赖只需要声明groupId、artifactId、version三个属性即可,具体的XML如下:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
其中:
groupId,一般以公司或组织的域名倒序定义;
artifactId,一个组件的ID标识;
version,依赖的版本,此处用一个占位符代替实际的版本号。
version标签中的${activiti.version}表达式可以在properties标签中定义属性的值,如下:
<properties>
<activiti.version>5.10</activiti.version>
</properties>
通过指定activiti.version为5.10,Maven就知道需要从仓库下载版本为5.10的Activiti对应的jar文件,还可以下载依赖组件的javadocs或sources等。
Maven在需要下载依赖的时候默认从中央仓库搜索,当匹配到存在的组件时将其从远程下载到本地仓库。但是,当中央仓库不存在某个组件的时候就需要定义第三方仓库供Maven查询、下载。目前Activiti还未被收录到中央仓库,所以我们需要指定一个第三方仓库,即Activiti提供的仓库,告诉Maven当找不到依赖时从此仓库查询。以下代码定义了Activiti的公开仓库(从5.14版本开始,可以从Maven中央仓库直接下载):
<!-- Maven仓库定义 -->
<repositories>
<!-- Activiti的仓库 -->
<repository>
<id>Activiti</id>
<url>http://maven.alfresco.com/nexus/content/repositories/activiti</url>
</repository>
</ repositories>
以此类推,当日常开发中遇到某些依赖组件不存在的情况时,可以先查看官方网站是否提供了Maven仓库,如果有则加入到repositories中,否则只能用私服的方式(私服如何使用不在本书讨论范围内,读者可以参考《Maven实战》一书)。
2.4Hello World
Hello World如此经典,几乎每学习一门新技术都从它开始,它是一把打开技术之门的万能钥匙,因为通过Hello World能快速了解一门技术如何配置、运行,以及得到什么样的结果。
好吧,让我们一起开启探索Activiti的大门。
2.4.1最简单的流程定义
代码清单2-2是一个最简单的请假流程定义文件,简单到仅有开始节点和结束节点。
代码清单2-2最简单的请假流程定义文件leave.bpmn
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" #1-S
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.kafeitu.me/activiti-in-action"> #1-E
<process id="leave" name="Leave"> #2
<startEvent id="startevent1" name="Start"></startEvent> #3
<endEvent id="endevent1" name="End"></endEvent> #4
<sequenceFlow id="flow1" name="" sourceRef="startevent1" #5
targetRef="endevent1"></sequenceFlow>
</process>
在解释这个文件的内容之前我们先来了解一下为了更好地理解代码含义本书所做的一些约定。
#号后面加数字用来表示代码清单的第几处(不是行号),其中1-S和1-E分别代表第1处标记的开始(Start)和结束(End)。
在详细讲解流程定义文件之前还要来看看对应的图片形式的流程定义,如图2-6所示。
在代码清单2-2中,1-S处的definitions标签表示BPMN 2.0规范中定义的开始,可以包含多个process标签;紧跟着是XMLSchema的定义,用来验证XML内容是否符合规范;最后一个属性targetNamespace是必需的,用来声明命名空间。为什么必须定义targetNamespace呢?命名空间是BPMN 2.0规范为了易于区分、归类流程定义所设,其值可以是任意文字,当然一般由公司或组织的名称定义,也可以更具体到一个项目,在本例中使用的是http://www.kafeitu.me/activiti-in-action,本书所有的targetNamespace也均使用来声明命名空间。
代码清单2-2中的#2处,定义了id属性为leave的process标签,以此来标记这是一个请假流程定义的开始。
代码清单2-2中的#3处,定义了流程的入口,即空启动事件startEvent,当流程启动时总是以startEvent开始。
代码清单2-2中的#4处,定义了流程的唯一结束出口,即空结束事件endEvent,流程线(flow)执行到endEvent时表示流程结束。可以定义多个结束事件。
代码清单2-2中的#5处,定义了一个sequenceFlow标签,用来描述各个流程节点之间的关系。图2-6中的箭头线就是sequenceFlow所定义的部分。sequenceFlow用sourceRef表示从哪里开始“流”到哪里。
2.4.2创建单元测试类
先来看看我们的Java类代码,如代码清单2-3所示,让程序运行起来,然后再解释代码的含义。
代码清单2-3 最简单的请假流程的Java类
public class VerySimpleLeaveProcessTest {
@Test
public void testStartProcess() throws Exception {
// 创建流程引擎,使用内存数据库
ProcessEngineprocessEngine = ProcessEngineConfiguration #1-S
.createStandaloneInMemProcessEngineConfiguration()
.buildProcessEngine(); #1-E
// 部署流程定义文件
RepositoryService repositoryService = processEngine.getRepositoryService(); #2
repositoryService.createDeployment() #3-S
.addClasspathResource(
"me/kafeitu/activiti/helloworld/sayhelloleave.bpmn.xml").deploy(); #3-E
// 验证已部署流程定义
ProcessDefinitionprocessDefinition = repositoryService #4-S
.createProcessDefinitionQuery().singleResult();
assertEquals("leavesayhello", processDefinition.getKey()); #4-E
// 启动流程并返回流程实例
RuntimeServiceruntimeService = processEngine.getRuntimeService(); #5
ProcessInstanceprocessInstance = runtimeService #6-S
.startProcessInstanceByKey("leavesayhello");
assertNotNull(processInstance);
System.out.println("pid=" + processInstance.getId() + ", pdid="
+ processInstance.getProcessDefinitionId()); #6-E
}
}
下面对代码清单2-3中所有标记处的作用依次进行解释。
#1-S至#1-E是通过编程方式创建一个流程引擎实例,即通过ProcessEngineConfiguration工具类的createStandaloneInMemProcessEngineConfiguration()方法创建一个使用H2内存数据库的流程引擎实例,默认的JdbcUrl为jdbc:h2:mem:activiti。当然,除了此方法之外还有其他创建引擎实例的方法,例如调用ProcessEngineConfiguration.createXXX(). buildProcessEngine(),在调用过程中还可以通过编程方式配置引擎的参数ProcessEngineConfiguration.createXXX().. setFoo(argument).buildProcessEngine()。
#2处紧接着使用刚刚创建的引擎实例获取RepositoryService,第1章列出的Activiti的七大Service接口都可以由ProcessEngine通过getXxxService()方法获取。
#3处使用RepositoryService部署位于classpath中的流程定义文件sayhelloleave.bpmn。
#4-S至#4-E处用来验证刚刚部署的流程是否成功。这里需要说明一下,在通过七大Service接口查询对象时均使用xxxService.createXxxQuery()方式创建查询对象。
#5处和#2处一样,通过引擎实例获取RuntimeService对象。
#6-S处使用runtimeService启动一个流程并返回流程实例。此外runtimeService和创建流程引擎的方式类似,也提供了多种启动流程的方式,可以使用runtimeService.startProcessInstanceXxx()启动流程实例。在启动的同时还可以设置流程变量,具体使用方法可以先参考API文档中的方法说明,以后的章节会陆续讲到根据不同的需求使用不同的启动方式。接下来验证流程是否启动成功,这仅是简单的null验证。读者可以自行扩展验证,以验证自己的猜测结果,这也是一种很好的学习方式。最后输出已启动的流程实例的ID和流程定义的ID。
2.4.3运行Hello World
可以将项目bpmn20-example导入至IDE,笔者使用的是Eclipse IDE for Java EE Developers(Indigo),选择此版本是考虑到后续章节中有基于Web的应用。本书的例子均使用Maven管理,在实例中已配置好依赖需要使用的仓库(Repository),通过http://www.eclipse.org/m2e/download/安装M2Eclipse插件,然后单击菜单:File->Import,选择对话框中的Maven->Exsiting Maven Project选项,最后选择本示例程序所在目录即可。
使用JUnit运行VerySimpleLeaveProcessTest之后得到的结果是:
pid=5,pdid=leavesayhello:1:4
pid即流程实例在数据中的id;pdid的值有些特殊,由一些列参数组合而成并以冒号分割,其中leavesayhello就是流程定义的key,1表示版本号,4表示流程定义在数据库中的id。
除了使用Eclipse运行测试用例外,还可以在命令行中输入mvn test进入本实例目录运行。
2.4.4添加业务节点
前面运行了最简单的例子来说明流程执行过程(严格来说不是一个流程,因为根本没有做任何事情),下面为这个例子添加一点实际业务使其可以正常工作起来。既然是请假流程就应该知道是哪个员工请了几天假(先处理不复杂的业务信息)。
首先需要为流程添加一个用户任务(userTask)来处理申请,根据申请内容决定运行申请还是驳回申请。下面先用如图2-7所示的流程图来表示需求。
图2-7带审批的请假流程
然后再来看看流程定义是如何设计的,以及用户任务(userTask)和脚本任务是如何定义的。支持领导审批的请假流程定义如代码清单2-4所示。
代码清单2-4支持领导审批的请假流程定义
<?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: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.kafeitu.me/activiti-in-action">
<process id="SayHelloToLeave" name="SayHelloToLeave">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="领导审批"> #1-S
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>deptLeader</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask> #1-E
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="outputAuditResult"
targetRef="endevent1"></sequenceFlow>
<scriptTask id="outputAuditResult" name="输出审批结果" #2-S
scriptFormat="groovy">
<script><![CDATA[out:println "applyUser:" + applyUser + " ,days:" + days + ", approval:" + approved;]]></script>
</scriptTask> #2-E
<sequenceFlow id="flow3" name="" sourceRef="usertask1"
targetRef="outputAuditResult"></sequenceFlow>
</process>
</definitions>
此流程在2.4.1的基础上添加了新的userTask,并且增加了一个sequenceFlow定义。照例还是来解释一下此流程的定义。
#1处使用userTask来定义图2-7中的“领导审批”节点,其id为deptLeaderAudit,在其内部使用BPMN 2.0标准的用户任务分配定义元素设置了此任务由角色为deptLeader的人员处理,即有deptLeader角色的人员都可以处理此任务。
#2处定义了通过scriptTask来输出“领导审批”节点的处理结果。目前Activiti支持的scriptTask类型有Javascript和Groovy两种。本例使用可以运行在JVM上的脚本语言Groovy输出结果,语法简洁明了,读者比较容易理解。在scriptTask标签内部定义了脚本要处理的脚本内容,由script标签包裹并定义为CDATA类型数据。在流程运行的过程中Activiti会把脚本及流程变量转交给Groovy处理(需要添加Groovy的jar或依赖)。
这一步迈得好像有点大了,从一个空流程突然多出了两个不同的任务,不过这两个任务都比较简单。下面结合代码清单2-5的单元测试代码来讲解一下流程,相信读者能很快了解流程的运行过程以及任务的作用。
代码清单2-5SayHelloToLeaveTest.java
public class SayHelloToLeaveTest {
@Test
public void testStartProcess() throws Exception {
ProcessEngineprocessEngine = ProcessEngineConfiguration
.createStandaloneInMemProcessEngineConfiguration().buildProcessEngine();
RepositoryServicerepositoryService = processEngine.getRepositoryService();
String bpmnFileName = "me/kafeitu/activiti/helloworld/SayHelloToLeave.bpmn"; #1-S
repositoryService.createDeployment()
.addInputStream("SayHelloToLeave.bpmn ",this.getClass().getClassLoader()
.getResourceAsStream(bpmnFileName)).deploy(); #1-E
ProcessDefinitionprocessDefinition = repositoryService
.createProcessDefinitionQuery().singleResult();
assertEquals("SayHelloToLeave", processDefinition.getKey());
RuntimeServiceruntimeService = processEngine.getRuntimeService(); #2-S
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("applyUser", "employee1");
variables.put("days", 3);
ProcessInstanceprocessInstance = runtimeService.startProcessInstanceByKey(
"SayHelloToLeave", variables); #2-E
assertNotNull(processInstance);
System.out.println("pid=" + processInstance.getId() + ", pdid="
+ processInstance.getProcessDefinitionId());
TaskServicetaskService = processEngine.getTaskService(); #3-S
Task taskOfDeptLeader = taskService.createTaskQuery()
.taskCandidateGroup("deptLeader").singleResult();
assertNotNull(taskOfDeptLeader);
assertEquals("领导审批", taskOfDeptLeader.getName()); #3-E
taskService.claim(taskOfDeptLeader.getId(), "leaderUser"); #4
variables = new HashMap<String, Object>(); #5-S
variables.put("approved", true);
taskService.complete(taskOfDeptLeader.getId(), variables); #5-E
taskOfDeptLeader = taskService.createTaskQuery() #6-S
.taskCandidateGroup("deptLeader").singleResult();
assertNull(taskOfDeptLeader); #6-E
HistoryServicehistoryService = processEngine.getHistoryService(); #7
long count = historyService.createHistoricProcessInstanceQuery().finished() #8
.count();
assertEquals(1, count);
}
}
为了更好地理解代码,在开始讲解此代码清单之前先以情景模拟的方式来了解任务的签收与办理过程:公司的每个部门可能有多个领导(A是正手,B是副手),在将一项任务分配给“部门领导”角色之后所有的部门领导都会收到一条未签收状态的任务。假如公司约定请假流程的审核由B处理,在申请人申请请假之后A和B同时会看到一个需要处理的任务(Task),B签收任务并办理,此时A的待办任务列表中就少了一项任务。本来一项任务是属于一个角色或某几个候选人的,在执行了签收动作之后任务归签收人所有。在了解对任务的签收和办理的执行过程之后我们再来看看代码清单2-5的执行过程。
#1处和代码清单2-3的#3-E处一样用来部署流程,只不过变换了一种方式,不是直接部署bpmn20.xml而是部署以bpmn为扩展名的文件。原因解释一下:这个流程定义是通过Eclipse Designer创建的,默认扩展名为bpmn(实际内容是一样的),在Activiti 5.9以及之前的版本中流程引擎在读取流程定义文件时必须以bpmn20.xml结尾,所以这里需要变通一下,在部署流程的时候读取一个文件流并由foo.bpmn20.xml传入,结果和代码清单2-3中部署bpmn20.xml一样。从Activiti 5.10版本开始Activiti已经支持直接部署以bpmn扩展名结尾的流程定义文件。
#2处与代码清单2-3中的启动方式稍微有些差别,在调用启动流程的方法中传入了一个Map集合变量,这里设置了两个属性,applyUser表示申请人的名称,days表示请假的天数。这样Activiti在启动流程的时候会把这两个变量存入数据库中,以后就可以通过接口读取到节点。
#3处的任务是查询组(Group)deptLeader的未签收任务并验证任务的名称。
#4处通过taskService调用claim方法“签收”此任务归用户leaderUser所有。
#5处就是领导的处理结果。在流程启动时填写了请假人与请假天数来模拟实际场景,领导审批通过,也就是在#5-S处设置变量approval为true表示审批通过,在#5-E处完成任务的同时以流程变量的形式设置审批结果。
#6处是为了让读者更好地理解执行结果,因为任务已经办理完成,再次查询组deptLeader的任务已经为空。
#7处通过流程引擎对象获取历史记录查询接口。
#8处通过历史接口统计已经完成(finished)的流程实例数量,接着验证预期的结果。
细心的读者可能会问:明明流程图中有两个节点(领导审核和输出审批结果),为什么在代码中没有处理scriptTask呢?在讨论这个问题之前先看看运行结果:
pid=5, pdid=SayHelloToLeave:1:4
applyUser:employee1 ,days:3, approval:true
结果中第一行和代码清单2-3执行结果相同,第二行则不同。第二行的输出是scriptTask执行的结果,在流程定义中用Groovy脚本输出使用变量拼接的信息。现在可以来回答刚刚提出的问题:为什么代码没有处理scriptTask呢?原因很简单,对于scriptTask,流程引擎会自动处理,处理完成之后流转到下个节点,在本例中scriptTask之后就是结束事件了,流程结束,所以没有处理scriptTask。
就目前来说,除了userTask不能被自动处理之外,其他的任务均由流程引擎自动处理,无需人工参与。
2.5Activiti Explorer
前面提到了Activiti Explorer是Activiti为了让开发人员快速入门所设计的一个示例程序,本节将介绍如何运行Activiti Explorer以及如何部署和处理任务。
2.5.1配置并运行Activiti Explorer
如果读者下载的是最新版本,那么直接把activiti-5.1x(5.11及之后的版本)的wars/activiti-explorer.war复制到一个干净的Tomcat的webapps目录后运行Tomcat即可。如果需要运行5.10及之前的版本,需要根据下面的步骤依次操作。不管运行哪个版本,最后启动应用的访问路径看到如图2-8所示的界面。
现在我们重新回到Activiti解压缩的目录,进入setup目录,例如笔者的Activiti解压目录是:/home/henryyan/work/sources/activiti/activiti-5.10/setup。此目录中有几个子目录和配置文件,下面依次介绍。
files:此目录中包含运行Activiti Explorer所需要的一些配置文件及Web应用,例如,Tomcat配置、数据库(H2)初始化数据、所需的jar包等。
build.db.properties:用来配置数据库信息,通过配置db属性可以使用其他的数据库,例如,MySQL、Oracle、SQL Server等。此文件中还有jdbc的配置信息,可以通过更改这几项配置来使用本地数据库。
build.properties:用过Ant的读者对此肯定很熟悉了,需要把Ant运行时的配置信息单独配置到一个文件。此文件中配置了运行时使用的Tomcat版本及自动文件存放位置。
build.xml:配置了运行、停止、清理等目标。
运行Activiti Exporer比较简单,只要使用Ant执行事先定义好的目标即可。现在通过命令进入安装目录(activiti-5.x/setup),执行命令ant demo.start即可自动构建、运行目标,并且在完成之后自动打开浏览器,访问地址为:http://localhost:8080/activiti-explorer,打开的页面如图2-8所示。
图2-8Activiti Explorer登录页面截图
2.5.2使用Activiti Explorer
1. 登录系统
在通过Ant脚本启动Activiti Explorer的过程中已经自动初始化了用户和组数据,这些数据文件位于activiti-5.x/setup/files/demo/h2.data.sql;对于5.11及之后的版本在启动Activiti Explorer的时候系统会自动执行数据初始化工具类插入初始化数据。
以拥有管理员角色的用户kermit登录系统,默认密码与用户名kermit相同。登录之后的页面如图2-9所示。
2. 部署流程
单击“Manage”栏目,然后单击“Deployments”菜单选择“Upload new”,弹出的对话框如图2-10所示。
图2-9登录Activiti Explorer之后的页面截图
图2-10在Activiti Explorer中部署流程
前面设计的流程SayHelloToLeave需要在启动流程时设置变量,而在Activiti Explorer中为了简单演示笔者采用表单(form)形式单独设计了流程定义SayHelloToLeaveForActivitiExplorer。单击“Choose a file”,然后在文件选择对话框中选择bpmn20-example/src/main/resources/me/kafeitu/activiti/helloworld/SayHelloToLeaveForActivitiExplorer.bpmn20.xml,这样Activiti Explorer会自动部署流程并跳转到流程资源查看页面。
部署流程之后单击“Process”栏目,在左侧单击“SayHelloToLeaveForActivitiExplorer”可以查看流程的图片形式,此图片由引擎自动根据xml的配置信息生成。但是很不幸我们又遇到了让人郁闷的乱码问题,如图2-11所示。
具体解决办法会在5.3.4节中详细介绍。现在可以再次部署,但是这次选择SayHelloToLeaveForActivitiExplorer.zip压缩包,这个压缩包中有两个文件:SayHelloToLeaveForActivitiExplorer.bpmn20.xml和SayHelloToLeaveForActivitiExplorer.png。
图2-11在Activiti Explorer中部署流程后的中文乱码
重新部署之后可以正常显示中文,如图2-12所示。注意划线处的Version 2,它表示同一个流程定义部署了两次,版本号自动累加。
图2-12部署SayHelloToLeaveForActivitiExplorer.zip之后中文正常显示
3. 启动流程
在“Process”栏目中单击左侧的“SayHelloToLeaveForActivitiExplore”,然后单击页面右上角的“Start process”,跳转到启动流程表单页面,如图2-13所示。
图2-13启动SayHelloToLeaveForActivitiExplorer流程页面
填写Apply User和days之后单击“Start process”按钮即可启动流程。
4. 签收与办理任务
在此流程中,节点“领导审批”被设置为Management组。当前登录用户Kermit拥有此组,所以在启动此组之后单击“Task”栏目可以看到“Queued”后面显示数字1,表示当前有需要处理的任务,如图2-14所示。
单击“Management(1)”之后显示任务办理页面,其中包含任务的名称、任务所属人及表单信息,如图2-15所示。
图2-15任务办理对话框
我们已经了解了签收与办理的过程了,现在单击“Claim”按钮之后任务归kermit所属,之后就可以审批请假请求了。在“Approval”下拉框中选择一项审批结果,如图2-16所示。
选择审批结果后单击“Complete task”即完成了任务的办理。此流程比较简单,仅一个要点是学会使用Activiti Explorer。
2.6本章小结
本章从下载Activiti开始介绍,接着对配置JDK、Ant、Maven环境进行讲解,然后在此基础上逐步介绍最简单的Hello World程序,紧接着又进阶到如何使用userTask及scriptTask,并且利用单元测试验证所的期望结果,最后就如何运行Activiti Explorer进行讨论,使读者学会如何部署流程并解决中文乱码问题,以及如何在Activiti Explorer上启动、签收、完成任务。相信读者现在已经对Activiti开发环境有了一个大体的了解,本章接触到的启动事件、结束事件、用户任务、脚本任务只是BPMN 2.0规范中很小的一部分,下一章我们将学习Activiti目前支持的BPMN 2.0规范。
第二部分
基础篇
在第一部分中用两章内容介绍了Activiti的背景知识和Activiti入门,从整体上对Activiti有了初步的了解,讲述了Activiti、工作流的历史,之后通过简单的例子讲解如何使用Activiti API驱动流程。
本部分内容都是后续章节的基础。
第3章涉及两种流程设计工具Activiti Modeler和Activiti Designer,分别适用于业务人员和开发人员,详细介绍了这两种工具如何安装以及使用,尤其是利用Activiti Designer和JUnit单元测试帮助读者在Activiti引擎基础上更好地理解BPMN 2.0规范。
第4章的Activiti与BPMN 2.0规范可以说是全书的理论基础,讲述了BPMN 2.0规范中模型的图形、XML描述和Activiti在其基础上的扩展。有兴趣的读者可以结合BPMN 2.0规范的官方文档辅助学习。
第3章
流程设计工具
前面两章我们首先从概念上介绍了工作流和Activiti的背景知识,然后在第2章中通过日常办公中请假的例子打开了Activiti世界的大门。在第2章的例子中提到的流程定义文件是XML格式的,这样的文件全部手工来写肯定是不现实的,本章将带领读者学习如何使用工具拖拽的方式设计流程。
Activiti针对不同的平台提供了不同架构的流程设计器:基于B/S架构的流程设计器Activiti Modeler(可以通过浏览器在线设计流程),基于Eclipse插件的流程设计器Activiti Designer。
3.1基于B/S架构的流程设计器Activiti Modeler
Activiti Modeler是基于B/S架构的流程设计器,在Activiti 5.11版本发布之前Activiti官方提供了独立的Activiti Modeler组件,需要开发人员执行压缩包中的脚本打包,但是从Activiti 5.11开始不需要自行打包Activiti Modeler了,在下载的压缩包中已经把重新设计的Activiti Modeler集成到了Activiti Explorer中,所以不想了解如何打包的读者可以跳过本节内容。关于重新设计的Activiti Modeler将在3.2节介绍。
3.1.1Activiti Modeler特点
在介绍Activiti Modeler之前不得不先介绍一下Signavio,它是Activiti Modeler背后的Signavio开源BPMN设计器。Activiti Modeler并非Activiti官方开发的,而是基于Signavio公司开发的Signavio构建的。
Singnavio是一款成熟BPMN可视化在线设计器,底层基于德国的HPI公司开发的Web的建模工具Oryx。Signavio虽然是一家商业公司,但是它提供了遵循MIT协议开源的版本signavio-core-components。
Signavio公司同时提供了商业版本的设计器以满足更多、更强的需求,比如团队协作、高级流程设计器、流程Portal、分析与报表、导入/导出不同格式的文件(ARIS、XPDL、 PDF、 Visio、 Excel)。当然用户在正式购买商业版本之前有30天的试用期来了解商业版本的功能。
Activiti Modeler完全使用signavio-core-components的源码构建,而且在打包时仅需要简单的配置即可。
在Activiti 5.6及之前的版本中,Activiti Modeler是众多组件中的一部分,不需要我们自己打包war文件,有兴趣的读者可以下载Activiti 5.6版本的Zip包解压一下看看。
3.1.2下载signavio-core-components
signavio-core-components的源码托管在Google Code上,项目地址:http://code.google.com/p/signavio-core-components/。先利用版本控制器(Subversion)检出源码到本地工作区。有Subversion客户端的读者可以利用客户端方便地检出源码,只需要在URL中输入http://signavio-core-components.googlecode.com/svn/trunk/即可。喜欢使用命令行的读者可以执行以下命令:
svn checkout http://signavio-core-components.googlecode.com/svn/trunk/
检出之后的文件列表如图3-1所示。
对于目录中的文件,我们只关心build.properties和build.xml即可。如果读者对其他的目录文件有兴趣可以逐个浏览,以便日后在此基础上进行扩展。
3.1.3配置打包与运行
build.properties文件就是打包时需要配置的一些选项的属性文件。用编辑器将该文件打开之后看到的文件内容如图3-2所示。
下面介绍配置文件中的属性及其含义。
dir-tomcat-webapps:运行打包结果(war格式)的tomcat路径。
dir-jboss-webapps:运行打包结果(war格式)的jboss路径。
target:存放构建结果的目录,是打包之后生成的war文件的存放位置。
version:设计器的版本号,可完全由用户自定义。
war:打包的war文件的名称,例如activiti-modeler,打包之后会在target目录下生成activiti-modeler.war文件。
configuration:使用的配置,包含一些CSS样式和JSON格式的编辑器配置。目前支持3种配置:default、Activiti、jBPM。有兴趣的读者可以进入到configuration目录一探究竟。
host:打包之后运行时的路径。这个配置很关键,还要注意仅仅需要提供服务器地址(域名或IP)及端口即可。
fileSystemRootDirectory:signavio运行时的工作区目录,会保存编辑器的配置及设计的流程定义文件。值得注意的是,不管在哪种操作系统环境中都只使用“/”作为路径分隔符。
图3-2build.properties默认配置
了解了配置之后读者就可以根据自己的实际环境更改配置了。笔者只更改了几个属性,其他属性均使用默认值。
version=5.9
war = activiti-modeler
configuration = Activiti
fileSystemRootDirectory=/Users/henryyan/workspaces/activiti-modeler
了解到这里读者完全可以基于signavio-core-components构建自己的流程设计器,并且在此基础上进行改造,例如整合公司的其他产品、功能扩展以及本地化等。
更改好配置之后只需要执行ant build-all-in-one-war命令来执行打包任务,执行结果如下:
henryyan@hy-mbp~/work/sources/signavio-core-componentsant build-all-in-one-war
Buildfile: /Users/henryyan/work/sources/signavio-core-components/build.xml
…
[war] Building war:
/Users/henryyan/work/sources/signavio-core-components/target/activiti-modeler.war
BUILD SUCCESSFUL
Total time: 27 seconds
执行完以上命令后我们可以看到在signavio-core-components/target目录中生成了activiti-modeler.war文件,如图3-3所示。
打包之后复制target/activity-modeler.war到本地Tomcat的webapps目录,然后启动Tomcat,之后在浏览器中输入http://localhost:8080/activiti-modeler访问应用,加载页面如图3-4所示。
目前Signavio仅支持非IE内核的浏览器,例如Chrome、Firefox、Safari。
图3-4Activiti Modeler的加载页面
从图3-4中可以看出,图片使用的是configuration/Activiti中的 signavio_logo.png,版本号就是刚刚在build.properties中定义的version属性。
在加载完成之后进入Activiti Modeler的首页,页面如图3-5所示。
图3-5Activiti Modeler首页
3.1.4Windows用户打包
本节是针对Windows用户的老话题—乱码。
Windows用户在执行ant的打包命令ant budil-all-in-one-war时会遇到如图3-6所示的错误信息。
图3-6Windows环境下打包Activiti Modeler报错
导致构建错误的原因是编码不一致,Signavio的源码采用的是UTF-8编码,而中文版本的Windows操作系统默认的编码为GBK,编码不一致导致在转换的时候报错。笔者在最初学习Activiti时使用的是Linux操作系统(Ubuntu),在撰写本书的时候又转向了Mac OS X系统,因为Linux/UNIX默认的编码是UTF-8,所以没有遇到图3-6中的错误。
解决办法分三步,在用编辑器打开signavio-core-components/editor/build.xml文件进行以下处理。
1)找到<target name="com.signavio.editor.js.concat">,紧随其后添加一行配置代码:
<property name="charset" value="utf-8"/>
将标签中的<concat destfile='${build}/oryx.debug.js'>修改为:
<concatdestfile='${build}/oryx.debug.js' encoding="${charset}" outputencoding= "${charset}">
最终的配置如下:
<target name="com.signavio.editor.js.concat">
<property name="charset" value="utf-8"/>
<concat destfile='${build}/oryx.debug.js' encoding="${charset}" outputencoding= "${charset}">
2)找到<target name='com.signavio.editor.js.compress处代码,更改此target内的<java dir="${build}" jar="${root}/lib/yuicompressor-2.4.2.jar" fork="true" failonerror="true" output='${compress.temp}'>,将其中的yuicompressor-2.4.2.jar更改为yuicompressor-2.4.7.jar。
3)signavio默认使用yuicompressor-2.4.2版本压缩javascript和css文件。为了解决编码问题我们需要使用最新的yuicompressor版本来替换2.4.2版本。在笔者撰稿时最新的yuicompressor版本为2.4.7,读者也可以直接下载最新版本。访问http://yuilibrary.com/download/yuicompressor/下载第一个版本的压缩包,将其解压后提取build/yuicompressor-2.4.7.jar文件并复制到signavio-core-components/yuicompressor/editor/lib目录中。
再次执行打包命令ant build-all-in-one-war不会提示如图3-6所示的错误提示,如图3-7所示。
图3-7解决了Windows环境下打包Activiti Modeler错误后的提示
3.1.5设计请假流程
首先单击图3-5中的“New”按钮,然后选择“Business Process Diagram(BPMN 2.0)”新建一个流程,此时浏览器会打开一个新的如图3-8中的设计器页面。
整个设计器都是基于Ext的拖曳方式进行操作的,如图片3-8所示。其中左边栏是模型仓库,中间部分是工作区,右边栏可以用于设置中间工作区选中模型的属性,如果工作区中没有选中的模型,那么右边栏可以用于设置流程主信息。
在第2章中已经初步运行了请假流程,在这里我们仍然以请假流程为例来讲解业务需求人员如何使用Activiti Modeler从业务角度设计流程。Activiti Modeler设计的请假流程如图3-9所示。
图3-8Activiti Modeler的新建流程页面
图3-9Activiti Modeler设计的请假流程
一个完整的流程总是以开始事件(Start Event)为入口,在Activiti Modeler中只要从左侧的模型仓库找到“Start Events”,然后用鼠标将其拖曳到中间的空白工作区中。开始事件和快捷菜单如图3-10所示。
当将鼠标移动到圆形表示的“Start Event”时会出现一些快捷菜单图标,单击这些小图标可以快速创建各种模型并自动用顺序流箭头连接两个模型。
接下来创建“领导审批”节点时可以单击空白的矩形图标快速创建一个“Task(任务)”;也可以从左边栏的“Activities”中拖曳“Task”到空白区域,然后把鼠标移动到“Start Event”上单击“箭头”图标将其拖曳到刚刚创建的Task模型上。在日常的使用中还是第一种方式方便得多,第二种方式只有在特殊情况下才会用到。
了解了如何创建Task之后就可以使用第一种方式创建“领导审批”节点了。图3-11展示了通过快速方式创建的Task。
有了Task模型之后就要进一步完善Task的属性,例如任务名称、类型等。设置属性需要展开右边栏中的“Attributes”,然后单击刚刚创建的空白Task,在右边栏中设置Task的相关属性即可。在图3-12中设置了Task的“Name”和“TaskType”。读者可以继续添加其他剩余的节点并设置其属性。
图3-12设置领导审批节点的属性
一个完整的流程还要有结束事件(End Event),在普通的Task之后需要有一个结束节点。结束节点的创建方式和普通Task的创建方式一样,将鼠标移动到需要添加结束事件的任务上,然后单击粗线条的圆形图标就会自动创建一个结束事件,如图3-13所示。关于各种事件及模型会在第4章详细介绍。
图3-13创建结束事件(End Event)
到此一个完整的流程就设计完成了,在结束之前不要忘记保存劳动成果。单击页面左上角的保存按钮,弹出如图3-14所示的对话框。
在填写“Title”字段时不要输入中文,否则会导致再次打开流程图时系统报错,提示找不到页面(404错误),报错信息如图3-15所示。或许以后的版本能够解决次问题。
保存流程之后可以在Activiti Modeler主页面的Workspace中看到流程定义,选中流程定义可以进行编辑、移动、复制、删除等操作,如图3-16所示。
图3-14保存流程对话框
图3-15打开中文名称的流程图导致404错误
图3-16Activiti Modeler主页面的Workspace
3.1.6导出bpmn2.0.xml
图3-16中有两个流程定义,一个是英文名称的,一个是中文名称的。Activiti Modeler的设计结果都以xml的形式保存在磁盘上,保存xml文件的目录就是在最初打包时设置的fileSystemRootDirectory属性值。例如笔者的设置为/Users/henryyan/work/workspaces/activiti-modeler,打开该目录可以看到共有4个文件,如图3-17所示。
从图3-17中可以看出,每个流程定义都有两个文件,只不过扩展名不同。其中“bpmn20.xml”为流程定义文件,我们在第2章中部署流程的时候使用的也是扩展名为bpmn20.xml的xml文件;“signavio.xml”文件用于保存设计流程所需要的一些配置,其中配置以JSON方式保存在leaven.signavio.xml文件的“json-representation”属性中。有兴趣的读者可以打开流程文件阅读其中的配置,本书不深入讨论。
3.2在Activiti Explorer中使用Activiti Modeler
从5.11版本开始官方提供的压缩包发生了较大变化(参考2.1.1节):把重新设计的Activiti Modeler整合到了Activiti Explorer中,可以直接创建新模型然后部署到引擎中,也可以根据已有的流程定义创建模型,修改后可以直接把最新的修改部署到引擎。
由于Activiti Modeler组件需要依赖REST服务,因此读者要了解如何把Activiti Modeler集成到自己的项目中,相关内容可参考20.8节。
Activiti 5.11版本之后的Activiti Modeler依然在Signavio的基础上开发,由KISBPM提供开源版本,同时KISBPM也提供商业版本支持更多的功能,例如模型的版本控制、表单设计器、自定义属性、模板库、模型部署、角色控制等。
新版Modeler移除了原Signavio对于其他规范的支持(例如jBPM),只保留Activiti支持的BPMN 2.0规范,并且支持Activiti扩展的活动以及属性。
把压缩包中提供的activiti-explorer.war部署到Tomcat中即可使用Activiti Modeler。使用kermit/kermit登录后单击“Processes”→“Model workspace”,即可看到如图3-18所示的页面(根据系统的语言不同界面显示的语言也不同,笔者在5.11版本发布前为Activiti Explorer添加了国际化的中文部分)。
图3-18模型工作区
单击“New Model”按钮后打开创建模型对话框,如图3-19所示。
图3-19创建新模型对话框
新版设计器的界面和老版本一致(图3-8),如图3-20所示。
图3-20新版Activiti Modeler界面
从左侧选择模型后拖动到工作区即可,单击模型可以在右侧设置该模型的属性,如图3-21所示。
从图3-21中右侧部分可以看到有表单属性(Form properties)、监听器(Task listeners)等设置。
图3-21设置模型的属性
编辑完模型之后单击图3-20左上角的保存按钮保存模型,再单击右上角的“X”关闭设计器返回到Activiti Explorer中,如图3-22所示。
图3-22保存模型后返回到Activiti Explorer界面显示设计的请假流程
单击图3-18中右上角的“Deploy”即可部署该模型,在流程定义界面就可以看到刚刚部署的流程定义,如图3-23所示。
图3-23从模型工作区部署的流程定义
3.3基于Eclipse插件的流程设计器Activiti Designer
在客户的需求确定之后,由业务人员利用Activiti Modeler设计完业务流程,此时可以将设计结果导出为bpmn20.xml文件,之后由开发人员继续基于设计进一步添加涉及技术细节的配置,例如排他分支的条件、Java服务、任务监听器等。
3.3.1Activiti Designer特点
Java程序员每天工作使用的主要工具是IDE,而应用最广泛的IDE当然是Eclipse,任何开发人员都可以很容易地开发Eclipse的插件。Activiti目前提供了Eclipse的Activiti Designer插件,以后还会陆续推出其他IDE的插件,例如NetBeans、IntelliJ IDEA等。
Activiti Designer一般随着Activiti引擎一起发布,在Activiti引擎的新版本添加了对BPMN 2.0规范的支持和实现之后,Activiti Designer也同步更新了支持新规范的可视化流程设计。
相对于Activiti Modeler严格以BPMN 2.0规范为基础实现可视化设计,Activiti Designer提供了Activiti自行扩展的十几种配置(以后会陆续介绍),例如使用activiti:assginee简化UserTask的任务办理人属性,以及使用activiti:candidateGroup简化任务候选组等。
TDD(Test Driven Development,测试驱动开发)被广泛用于日常开发中。Activiti Designer当然也为TDD提供了良好的支持:开发人员可以基于流程定义文件(foo.bpmn)快速生成测试用例,在设计流程的时候把存在“坏味道”的业务流程停止。
3.3.2安装Activiti Designer
本书使用的Eclipse版本为4.2(代号Juno),Activiti Designer支持3.7(代号Indigo)以后的Eclipse版本。
建议读者采用Site方式安装Activiti Designer:单击菜单“Help”->“Install New Software”,打开如图3-24所示的对话框。然后单击“Add”按钮,在打开的对话框中填写以下内容,接着单击“OK”按钮确认添加Repository。
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
图3-24用Site方式添加Activiti Designer的Repository
3.3.3设计流程
1. 新建Activiti项目
单击“File”→“New”,选择图3-25中的“Activiti Project”项目,单击“Next”进行一些设置之后就可以创建一个Activiti项目。
图3-25新建Activiti Project向导
创建完项目后的目录结构如图3-26所示,其中展示的是一个名为“bpmn20-example”的项目,熟悉Maven的读者很快就看出这是Maven的标准目录结构。建议不熟悉Maven的读者参考Maven的文档了解相关知识。
新建的Activiti项目会自动在src/main/resources目录下创建diagrams包来存放新建的流程资源文件。在实际应用中往往习惯使用公司或组织域名倒序创建目录,如图3-26所示。
2. 新建请假流程
在“src/main/resources/me/kafeitu/activiti”目录上右击,选择“New”→“Other…”,打开如图片3-27所示的新建向导对话框,选择“Activiti Diagram”,单击“Next”按钮弹出如图3-28所示的对话框,其中默认的扩展名为.bpmn,也是众多设计器默认的扩展名。
在Activiti Designer 5.8及之前的版本中,在新建流程定义时默认的扩展名为.activiti,而不是现在看到的.bpmn;当打开foo.activiti文件设计好流程并保存时会自动在同一目录下生成同名的foo.bpmn20.xml和foo.png。
图3-27新建Activiti Diagram向导—选择新建类型
图3-28新建Activiti Diagram向导—设置文件名
单击图3-28中的“Next”按钮之后弹出如图3-29所示的选择模板对话框。为了方便快速创建流程,Activiti Designer内置了一些流程定义模板供开发人员选择。在创建流程之后开发人员可以根据自己的需求稍微调整即可。
图3-29新建Activiti Diagram向导—选择模板
在安装Activiti Designer之后,在任何目录都可以通过“New”创建流程定义,前面关于创建Activiti Project的介绍仅仅是为了向读者说明创建过程。
3. 设计请假流程
在新建流程之后要先设置流程属性,例如Id、Name等,设置请假流程的配置信息如图3-30所示。
下面介绍配置界面中的几个属性。
Id:流程的唯一标识,在2.4.2节中启动流程时指定的“leavehello”即此属性,建议使用纯英文标识。
Name:流程的名称,可以是任意字符。
Namespace:命名空间,一般使用公司名或组织域名+项目名称,可以更加细化到每个系统的模块,这样读者在实际项目中可以用此属性来归类流程,例如com/company/project/module,其中com/company是公司域名的倒序,project表示项目名称,module表示一个具体的模块;还可以使用另外一种描述方式,即通过http://www.compnay.com/project/module达到相同的目的。
Document:针对当前流程功能的简短文字描述。
图3-30设置流程属性
和Activiti Modeler设计流程的方式一样,从开始事件(Start Even)开始,在设计区域的右边栏中找到“Event”组的“StartEvent”,用鼠标将其拖动到左侧的空白区域,如图3-31所示。
图3-31中“Properties”下的Id、Name的含义和图3-30中的类似,只不过这里表示一个开始事件的属性。
在图3-31所示的工作区中,圆形图标即为开始事件,将鼠标移动到开始事件上之后和在使用Activiti Modeler时一样会浮动显示快捷方式菜单,例如可以通过单击左下的第二个图标创建一个任务(Task),如图3-32所示。
利用快捷菜单可以快速创建各种模型,当然也可以直接从右边栏的模型仓库中选择“Task”拖动到工作区。单击“Create user task”即可在开始事件的右边生成一个User Task并自动用顺序流(SequenceFlow)箭头连接,如图3-33所示。
图3-33通过快捷菜单创建User Task
在创建“领导审批”节点后单击刚刚新建的User Task就可以设置属性了,在图3-33中将Id、Name属性设置为“deptLeaderAudit”和“领导审批”,前者是Task在当前这个请假流程中的一个唯一标识。
设计“领导审批”任务在使用Activiti Modeler时已经结束了,在使用Activiti Designer时需要进一步设置任务的属性,例如任务的分配人。在图3-34中设置“领导审批”节点的分配人为“leader”。
图3-34设置User Task的任务分配人
读者可以自行依次完成剩余两个User Task的创建与设置,如图3-35所示。对于在设计过程中不容易理解的属性在以后的章节会陆续讲到,通过本章只要学会如何使用Activiti Designer设计简单流程即可。
图3-35使用Activiti Designer设计完成的请假流程
要在设计完流程后查看XML格式的文件内容,需要右击foo.bpmn文件选择“Open With”→“XML Editor”;要改用设计器打开要查看的文件,需要右击foo.bpmn文件选择“Open With”→“Activiti Digram Editor”。如图3-36所示,图中上半部分使用“Activiti Digram Editor”打开,下半部分使用“XML Editor”打开。
图3-36使用不同方式打开Leave.bpmn文件
3.3.4自动化
1. 自动生成流程图片
流程图可以帮助用户快速了解整个流程的业务逻辑并根据不同的条件采用处理方式,在流程运行过程中可以以图片形式直观体现流程状态,例如标示当前在哪个节点可以用一个红色的边框表示。
Activiti Designer默认不会自动生成和流程相关的图片文件,需要用户自行更改插件的配置来启用自动生成图片功能。不同用户分别进行如下操作:
Windows、Linux用户单击“Window”菜单选择“Preferences…”。
Mac用户单击“Eclipse”菜单选择“Preferences…”。
打开如图3-37所示的配置对话框。依次选择对话框左边栏的“Activiti”→“Save”,勾选“Create process definition image when saving the diagram”前面的复选框。
图3-37设置自动生成图片功能
单击“OK”按钮,之后在新建流程或在原有流程中调整任意一个Task并进行保存即会在流程文件的同目录下生成同名的png图片文件,如图3-38所示。
2. 自动生成测试代码
近几年敏捷开发在国内的企业和开源社区中被广泛使用,其中和开发者距离最近的当属极限编程(Extreme Programming,XP),而作为XP的核心原则之一的是测试驱动开发(Test Drive Development,TDD)。TDD能让代码更稳定、更健壮,使所有的新功能和重构都在TDD的基础上进行,把Bug扼杀在开发阶段而不是等到上线之后才发现。
Activiti引擎的开发也是使用TDD进行的,有兴趣的读者可以下载源码来阅读,其中的测试用例覆盖了大部分的引擎功能。同时Activiti Designer也为日常开发提供了便利的测试驱动的功能,使开发者可以很方便地基于流程定义文件生成对应的单元测试。
右击Leave.bpmn选择“Activiti”→“Generate unit test”,如图3-39所示。
图3-39自动生成单元测试的右键菜单
单击“Generate unit test”之后会在“test/java/org/activity/designer/test”包中生成“ProcessTestLeave.java”文件,如图3-40所示。
图3-40自动创建的单元测试类
自动生成的测试代码见代码清单3-1。
代码清单3-1Activiti Designer自动生成的测试代码
package org.activiti.designer.test;
// 省略了import部分
public class ProcessTestLeave {
private String filename = "/Users/henryyan/work/books/activiti-in-action-codes/bpmn20-example/src/main/resources/me/kafeitu/activiti/designer/Leave.bpmn";
@Rule
public ActivitiRule activitiRule = new ActivitiRule(); #1
@Test
public void startProcess() throws Exception {
RepositoryService repositoryService = activitiRule.getRepositoryService(); #2
repositoryService.createDeployment().addInputStream( #3-S
"leave.bpmn20.xml", new FileInputStream(filename)).deploy(); #3-E
RuntimeService runtimeService = activitiRule
.getRuntimeService(); #4-S
Map<String, Object> variableMap = new HashMap<String, Object>();
variableMap.put("name", "Activiti");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave", variableMap); #4-E
assertNotNull(processInstance.getId()); #5
System.out.println("id " + processInstance.getId() + " "
+ processInstance.getProcessDefinitionId());
}
}
在代码清单3-1的#1处创建一个ActivitiRule,通过实例activitiRule可以获取第1章中提到的7个Service接口实例,如#2处获取了RepositoryService;接下来在#3-S至#3-E处以leave.bpmn20.xml作为文件名部署流程;#4-S至#4-E处和代码清单2-3类似,启动了一个流程实例;最后在#5处验证流程实例processInstance的id属性不为空,进而证明流程启动成功。
有了单元测试类就可以用来验证流程的设计是否符合预期的结果,右键选择“Run As”→“JUnit Test”即可,如图3-41所示,运行结果如下:
Aug 2, 2012 11:51:37 PM org.springframework.beans.factory.xml.XmlBeanDefinition-Reader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [activiti.cfg.xml]
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.db.DbSqlSession executeSchemaResource
INFO: performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.db.DbSqlSession executeSchemaResource
INFO: performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.db.DbSqlSession executeSchemaResource
INFO: performing create on identity with resource org/activiti/db/create/activiti. h2.create.identity.sql
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.ProcessEngineImpl <init>
INFO: ProcessEngine default created
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.bpmn.deployer.BpmnDeployer deploy
INFO: Processing resource leave.bpmn20.xml
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.bpmn.parser.BpmnParse parseDefinitionsAttributes
INFO: XMLSchema currently not supported as typeLanguage
Aug 2, 2012 11:51:39 PM org.activiti.engine.impl.bpmn.parser.BpmnParse parseDefinitionsAttributes
INFO: XPath currently not supported as expressionLanguage
id 5 leave:1:4
图3-41运行单元测试
读者可以通过自动生成测试代码功能来学习Activiti,当想了解Activiti如何处理一个流程组件时可以先设计一个流程定义,然后生成单元测试代码,利用Junit的功能验证引擎运行的结果是否和预期一致。
3.3.5升级历史遗留的流程设计
第2章的Hello World是在部署XML格式的文件之后启动流程的。使用Activiti Modeler设计的流程定义会在工作区生成两个文件:foo.bpmn20.xml和foo.signavio.xml,这两个文件的作用前面已经讲到了,这里不再重复。那么在Activiti Designer中如何生成foo.bpmn20.xml文件呢?
答案很简单,用于设计流程的foo.bpmn文件就是我们想要的foo.bpmn20.xml,为什么这么说呢?这既是业界的约定,也有一定的历史原因。
bpmn是BPMN XML文件的默认扩展名。很多设计器都使用bpmn作为流程定义文件的扩展名。
Activiti Designer 5.8及之前的版本是将.activiti作为设计文件存在,在保存foo.activiti之后会自动生成foo.bpmn20.xml和foo.png两个文件。之后在Activiti Designer 5.9版本发布时放弃了这一做法改用标准的.bpmn作为扩展名,同时不会再生成foo.bpmn20.xml文件,因为新的.bpmn文件内容和Activiti Designer 5.8版本之前自动生成的foo.bpmn20.xml文件内容一样。
升级Activiti Designer 5.8及之前版本的设计文件至兼容新版本的步骤如下:
1)删除.activiti文件,不再需要它了。
2)重命名foo.bpmn20.xml为foo.bpmn,刚刚已经提到到,文件内容相同,仅仅是扩展名不同而已。
3.3.6导入Activiti Modeler设计
在3.1节中了解了如何使用Activiti Modeler设计流程,并且也看到了生成的leave.bpmn20.xml文件,现在假设业务流程设计已经完成,要将其转移给开发人员进行完善以满足引擎运行的要求。
把Activiti Modeler工作区中的leaven.bpmn20.xml复制到项目中,将其重命名为leave.bpmn,双击leave.bpmn就可以以设计器方式将其打开以便更改流程配置,如图3-42所示。
图3-42将使用Activiti Modeler设计的流程定义导入至Eclipse工程中
至此可以执行像3.3.3节“设计流程”那样的过程来完善流程定义信息,例如可以将一长串的字符表示的流程的ID属性更改为leave,完成之后同样可以生成单元测试功能来验证更改是否正确。
3.3.7泳池与泳道
图3-43是一个常见的标准游泳池,一个泳池(Pool)中分为了多个泳道(Lane),比赛时每一个参赛选手只能在自己的泳道中前行。
图3-43游泳池
相信有一部分读者使用过Viso或类似的工具来设计流程,为了沟通方便和清晰展示不同角色、岗位人员的职能,就会使用泳池与泳道来划分。
回想一下请假流程中的设计,涉及的人员角色分别有:员工、部门领导、人事,在BPMN 2.0规范中请假流程还可以这么来设计,如图3-44所示。
图3-44请假流程的泳道模式
读者可以把前面章节接触到的请假流程与图3-44进行对比,显然图3-44中的泳道模式会更加直观、清晰地划分出不同角色、职能人员所需要负责的任务。
图3-44中包含了一个泳池(名称为:请假流程)以及3个泳道(员工、领导、人事)。
3.3.8在Activiti Designer中使用泳道与泳池
新建一个流程,然后在打开的设计窗口右侧选择“Container”栏中的“Pool”并拖动到空白区域,如图3-45所示。
拖动完成后即创建了如图3-46所示的泳池,此时可以单击泳池左侧(Pool文字处)选中泳池以编辑其属性,如图3-47所示,把泳池的名称改为“请假流程”。
图3-46只包含一个泳道的泳池
图3-47编辑泳池的属性
单击图3-46中的泳道同样可以编辑属性,把第一个泳道命名为“员工”用来存放和请假申请人有关的任务,如图3-48所示。
接下来就可以像前面章节一样来设计流程,在“员工”泳道中拖动一个启动事件,如图3-49所示。
图3-48选择并编辑泳道属性
图3-49在员工泳道中拖入一个空启动事件
接下来需要再创建一个针对领导的泳道,在右侧工具栏选择“Container”中的“Lane”拖动到泳池中,如图3-50所示。
图3-50创建了一个新的泳道
以此类推读者就可以设计出如图3-44所示的流程图。
物理的泳池只能划分固定数量的泳道,在设计流程图时对泳道的数量没有限制,可以由任意多个泳道组成一个大的泳池,所以泳道与泳池常在一些相对复杂的流程中使用。
3.4本章小结
“工欲善其事,必先利其器”。本章主要介绍了Activiti引擎提供的两个流程设计器,一个是由Signavio授权的基于Web的Activiti Modeler,另外一个是Activiti Designer。两个设计器侧重面不同,Activiti Modeler偏向于业务层面,而Activiti Designer则更侧重于开发层。
针对Activit Modeler本章讲解了如何进行打包、部署、运行等,并且简单介绍了如何使用Activiti Modeler设计一个简单的请假流程。针对Activiti Designer本章详细讲解了安装插件、设计流程、自动生成图片和单元测试代码等内容,又介绍了针对历史原因致使流程定义文件扩展名不同而进行的升级设计,最后介绍了从业务层向开发层导入流程定义文件的方法。
有了对流程引擎的初步认识及使用工具设计流程的基础,对于下一章将详细介绍的目前Activiit支持的BPMN 2.0规范,读者可以借助Activiti Designer更好地学习。