Your SlideShare is downloading. ×
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
J Boss+J Bpm+J Pdl用户开发手册 3.2.3
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

J Boss+J Bpm+J Pdl用户开发手册 3.2.3

2,291

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,291
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
29
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. JBoss jBPM – java 工作流 jBPM jPDL 用户开发手册 3.2.3 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 1 页 / 共 199 页
  • 2. 目录 目录 .................................................................................................................................................. 2 第 1 章 介绍..................................................................................................................................... 9 1.1. 概述................................................................................................................................... 9 1.2. jPDL 套件 .......................................................................................................................... 9 1.3. jPDL 图形流程设计器 .................................................................................................... 10 1.4. jBPM web 控制台 ........................................................................................................... 10 1.5. jBPM 核心库 ................................................................................................................... 11 1.6. JBoss jBPM 身份组件 ..................................................................................................... 11 1.7. JBoss jBPM 工作执行器 ................................................................................................ 11 第 2 章 正式开始........................................................................................................................... 12 2.1. 下载包概述..................................................................................................................... 12 2.1.1. jPDL 3 ................................................................................................................... 12 2.1.2. jPDL 流程设计器 ................................................................................................ 12 2.2. JBoss jBPM 项目目录 .................................................................................................... 13 2.3. 访问源代码 CVS 服务器 ............................................................................................... 13 2.3.1. 匿名 CVS 访问 ................................................................................................... 13 2.3.2. 开发人员 CVS 访问 .......................................................................................... 13 第 3 章 教程................................................................................................................................... 15 3.1. Hello World 实例 ............................................................................................................ 15 3.2. Database 实例 ................................................................................................................. 18 3.3. Context 实例:流程变量 ............................................................................................... 26 3.4. Task 分配实例 ................................................................................................................ 28 3.5. 自定义动作实例 ............................................................................................................. 32 第 4 章 面向图的程序设计 ........................................................................................................... 39 4.1. 介绍................................................................................................................................. 39 4.1.1. 域特定语言 .......................................................................................................... 40 4.1.2. 基于图的语言的属性 .......................................................................................... 41 4.2. 面向图的程序设计 ......................................................................................................... 42 4.2.1. 图结构.................................................................................................................. 43 4.2.2. 执行...................................................................................................................... 43 4.2.3. 流程语言 .............................................................................................................. 44 4.2.4. 动作...................................................................................................................... 46 4.2.5. 同步执行 .............................................................................................................. 47 4.2.6. 代码示例 .............................................................................................................. 48 4.3. 面向扩展图的程序设计 ................................................................................................. 48 4.3.1. 流程变量 .............................................................................................................. 48 4.3.2. 并发执行 .............................................................................................................. 48 4.3.3. 流程组成 .............................................................................................................. 50 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 2 页 / 共 199 页
  • 3. 4.3.4. 异步连续 .............................................................................................................. 50 4.3.5. 持久化和事务 ...................................................................................................... 51 4.3.6. 服务和环境 .......................................................................................................... 52 4.4. 注意事项......................................................................................................................... 52 4.4.1. 运行时数据隔离 .................................................................................................. 52 4.4.2. GOP 与其他技术相比 .......................................................................................... 53 4.4.3. GOP 与 Petri 网比较 ........................................................................................... 53 4.5. 应用领域......................................................................................................................... 54 4.5.1. 业务流程管理 (BPM) ......................................................................................... 54 4.5.2. 服务编排 .............................................................................................................. 56 4.6. 基于嵌入图的语言 ......................................................................................................... 56 4.7. 市场................................................................................................................................. 56 4.7.1. 终极流程语言 ...................................................................................................... 56 4.7.2. 相关信息 .............................................................................................................. 57 4.7.3. 其他实现技术 ...................................................................................................... 57 第 5 章 部署................................................................................................................................... 58 5.1. jBPM 库 .......................................................................................................................... 58 5.2. Java 运行时环境.............................................................................................................. 58 5.3. 第三方库......................................................................................................................... 58 5.4. Web 应用 ......................................................................................................................... 59 5.5. 企业包............................................................................................................................. 60 5.6. jPDL 运行时和套件 ........................................................................................................ 61 5.6.1. 运行时.................................................................................................................. 61 5.6.2. 套件...................................................................................................................... 61 5.6.3. 在套件服务器上配置日志 .................................................................................. 61 5.6.4. 在套件里调试流程 .............................................................................................. 62 第 6 章 配置................................................................................................................................... 64 6.1. 配置工厂......................................................................................................................... 67 6.2. 配置属性......................................................................................................................... 68 6.3. 其他的配置文件 ............................................................................................................. 68 6.3.1. Hibernate cfg xml 文件 ........................................................................................ 68 6.3.2. Hibernate 查询配置文件 ..................................................................................... 68 6.3.3. 节点类型配置文件 .............................................................................................. 69 6.3.4. 动作类型配置文件 .............................................................................................. 69 6.3.5. 业务日历配置文件 .............................................................................................. 69 6.3.6. 变量映射配置文件 .............................................................................................. 69 6.3.7. 转换器配置文件 .................................................................................................. 69 6.3.8. 缺省模型配置文件 .............................................................................................. 69 6.3.9. 流程包解析配置文件 .......................................................................................... 70 6.4. 在 JBoss 中的 jBPM 调试日志 .................................................................................... 70 6.5. 乐观并发异常日志 ......................................................................................................... 70 6.6. 对象工厂......................................................................................................................... 70 第 7 章 持久化............................................................................................................................... 75 7.1. 持久化 API ..................................................................................................................... 75 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 3 页 / 共 199 页
  • 4. 7.1.1.配置框架关系 ........................................................................................................ 75 7.1.2. JbpmContext 上的方便方法 ................................................................................ 76 7.1.3. 管理事件 .............................................................................................................. 79 7.1.4. 注入 hibernate session ......................................................................................... 80 7.1.5. 在程序中注入资源 .............................................................................................. 81 7.1.6. 高级 API 使用 .................................................................................................... 81 7.2. 配置持久化服务 ............................................................................................................. 82 7.2.1. DbPersistenceServiceFactory ................................................................................ 82 7.2.2. hibernate 会话工厂 ............................................................................................... 83 7.2.3. 配置 c3po 连接池 ................................................................................................ 84 7.2.4. 配置 ehcache cache provider ............................................................................... 84 7.3. Hibernate 事务 ................................................................................................................. 84 7.4. JTA 事务 ......................................................................................................................... 84 7.5. 定制查询......................................................................................................................... 86 7.6. 数据库兼容性 ................................................................................................................. 87 7.6.1. JDBC 隔离级 ........................................................................................................ 87 7.6.2. 变更 jBPM 数据库 .............................................................................................. 87 7.6.3. jBPM 数据库模式 ................................................................................................ 87 7.6.4. 已知问题 .............................................................................................................. 88 7.7.合并 hibernate 类 .............................................................................................................. 88 7.8. 定制 jBPM hibernate 映射文件 ..................................................................................... 88 7.9. 二级缓存......................................................................................................................... 88 第 8 章 jBPM 数据库 ................................................................................................................... 90 8.1. 切换后端数据库 ............................................................................................................. 90 8.1.1. 隔离级别 .............................................................................................................. 90 8.1.2. 安装 PostgreSQL 数据库管理器 ........................................................................ 90 8.1.3. 安装 MySQL 数据库管理器 ............................................................................... 93 8.1.4. 使用 PostGreSQL 或 MySQL 创建 JBoss jBPM 数据库.................................. 94 8.1.5. 最后一步 .............................................................................................................. 98 8.1.6. 更新 JBoss jBPM 服务器配置 ............................................................................ 99 8.2.数据库升级..................................................................................................................... 103 8.3. 在 JBoss 上开始 hsqldb 管理器 ................................................................................... 106 第 9 章 Java EE 应用服务器工具 ............................................................................................... 109 9.1. EJB ................................................................................................................................. 109 9.2. jBPM 企业配置 ............................................................................................................. 110 9.3. Hibernate 企业配置 ....................................................................................................... 112 9.4. 客户端组件................................................................................................................... 114 第 10 章 流程建模....................................................................................................................... 120 10.1. 概述............................................................................................................................. 120 10.2. 流程图......................................................................................................................... 120 10.3. 节点............................................................................................................................. 123 10.3.1. Node 职责 ......................................................................................................... 123 10.3.2. task-node 节点类型 .......................................................................................... 124 10.3.3. state 节点类型 .................................................................................................. 124 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 4 页 / 共 199 页
  • 5. 10.3.4. decision 节点类型 ............................................................................................ 124 10.3.5. fork 节点类型 ................................................................................................... 125 10.3.6. join 节点类型 ................................................................................................... 125 10.3.7. node 节点类型 .................................................................................................. 125 10.4. 转换............................................................................................................................. 125 10.5. 动作............................................................................................................................. 126 10.5.1. 动作配置 .......................................................................................................... 128 10.5.2. 动作引用 .......................................................................................................... 128 10.5.3. 事件.................................................................................................................. 128 10.5.4. 事件传播 .......................................................................................................... 128 10.5.5. 脚本.................................................................................................................. 128 10.5.6. 定制事件 .......................................................................................................... 130 10.6. 超级状态..................................................................................................................... 130 10.6.1. 超级状态转换 .................................................................................................. 130 10.6.2. 超级状态事件 .................................................................................................. 130 10.6.3. 分层命名 .......................................................................................................... 131 10.7. 异常处理..................................................................................................................... 132 10.8. 流程组成..................................................................................................................... 132 10.9. 定制节点行为 ............................................................................................................. 133 10.10. 图执行....................................................................................................................... 134 10.11. 事务划分 ................................................................................................................... 135 第 11 章 上下文 ........................................................................................................................... 138 11.1. 访问变量 ..................................................................................................................... 138 11.2. 变量生存期 ................................................................................................................. 139 11.3. 变量持久化 ................................................................................................................. 139 11.4. 变量范围 ..................................................................................................................... 139 11.4.1. 变量重载 .......................................................................................................... 139 11.4.2.变量重写 ............................................................................................................ 139 11.4.3. 任务实例变量范围 .......................................................................................... 140 11.5. 瞬态变量 ..................................................................................................................... 140 11.6. 定制变量持久化 ......................................................................................................... 140 第 12 章 任务分配....................................................................................................................... 142 12.1. 任务............................................................................................................................. 142 12.2. 任务实例..................................................................................................................... 142 12.2.1. 任务实例生存期 .............................................................................................. 142 12.2.2. 任务实例和图执行 .......................................................................................... 143 12.3. 分派............................................................................................................................. 144 12.3.1. 分派接口 .......................................................................................................... 144 12.3.2. 分派数据模型 .................................................................................................. 145 12.3.3. 个人任务列表 .................................................................................................. 145 12.3.4. 群组任务列表 .................................................................................................. 145 12.4. 任务实例变量 ............................................................................................................. 146 12.5. 任务控制器 ................................................................................................................. 146 12.6. 泳道............................................................................................................................. 148 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 5 页 / 共 199 页
  • 6. 12.7. 开始任务中的泳道 ..................................................................................................... 148 12.8. 任务事件..................................................................................................................... 149 12.9. 任务定时器 ................................................................................................................. 149 12.10. 定制任务实例 ........................................................................................................... 149 12.11. 身份构件 ................................................................................................................... 149 12.11.1. 身份模型 ........................................................................................................ 150 12.11.2. 分派表达式 .................................................................................................... 150 12.11.3. 移除身份组件 ................................................................................................ 152 第 13 章 文档管理....................................................................................................................... 153 第 14 章 定时器........................................................................................................................... 154 14.1. 定时器......................................................................................................................... 154 14.2. 调度部署..................................................................................................................... 155 第 15 章 异步连续....................................................................................................................... 156 15.1. 概念............................................................................................................................. 156 15.2. 示例............................................................................................................................. 156 15.3.工作执行器................................................................................................................... 158 15.4. jBPM 内置异步消息 ................................................................................................... 160 15.5. 异步架构 JMS ............................................................................................................ 161 15.6. 属性用法..................................................................................................................... 161 第 16 章 商务日历....................................................................................................................... 162 16.1. 到期日期..................................................................................................................... 162 16.1.1. 持续时间.................................................................................................................. 162 16.1.2. 基准日期.................................................................................................................. 162 16.1.3. 示例.......................................................................................................................... 163 16.2. 日历配置..................................................................................................................... 163 第 17 章 邮件支持....................................................................................................................... 165 17.1. jPDL 中的邮件 ............................................................................................................ 165 17.1.1. 邮件动作 .......................................................................................................... 165 17.1.2. 邮件节点 .......................................................................................................... 166 17.1.3. 任务分派邮件 .................................................................................................. 166 17.1.4. 任务提醒邮件 .................................................................................................. 166 17.2. 邮件表达式 ................................................................................................................. 166 17.3. 指定收件人 ................................................................................................................. 167 17.3.1. 多个收件人 ...................................................................................................... 167 17.3.2. 密送邮件 .......................................................................................................... 167 17.3.3. 地址解析 .......................................................................................................... 167 17.4. 邮件模板..................................................................................................................... 168 17.5. 邮件服务器配置 ......................................................................................................... 169 17.6. From 地址配置 ............................................................................................................ 169 17.7. 自定义邮件支持 ......................................................................................................... 169 17.8. 邮件服务器 ................................................................................................................. 170 第 18 章 Web Services 支持 ........................................................................................................ 171 18.1. 调用 jPDL Web Services............................................................................................. 171 18.1.1. 部署.................................................................................................................. 171 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 6 页 / 共 199 页
  • 7. 18.1.2. 测试部署 .......................................................................................................... 171 第 19 章 日志............................................................................................................................... 172 19.1. 创建日志..................................................................................................................... 172 19.2. 日志配置..................................................................................................................... 173 19.3. 日志检索..................................................................................................................... 174 19.4.数据仓库....................................................................................................................... 174 第 20 章 业务流程仿真 ............................................................................................................... 175 第 21 章 jBPM 流程定义语言(JPDL) ................................................................................... 176 21.1. 流程包......................................................................................................................... 176 21.1.1. 部署流程包 ...................................................................................................... 176 21.1.2. 流程版本控制 .................................................................................................. 176 21.1.3. 变更已部署流程定义 ...................................................................................... 177 21.1.4. 迁移流程实例 .................................................................................................. 177 21.1.5. 流程转换 .......................................................................................................... 177 21.2.委托............................................................................................................................... 178 21.2.1. jBPM 类加载器 ................................................................................................ 178 21.2.2. 流程类加载器 .................................................................................................. 178 21.2.3. 委托配置 .......................................................................................................... 178 21.3. 表达式......................................................................................................................... 180 21.4. jPDL xml 模式 ............................................................................................................. 181 21.4.1. 验证.................................................................................................................. 181 21.4.2. 流程定义 .......................................................................................................... 181 21.4.3. 节点.................................................................................................................. 182 21.4.4. 通用节点元素 .................................................................................................. 182 21.4.5. 开始状态 .......................................................................................................... 182 21.4.6. 结束状态 .......................................................................................................... 183 21.4.7. 状态.................................................................................................................. 183 21.4.8. 任务节点 .......................................................................................................... 183 21.4.9. 流程状态 .......................................................................................................... 184 21.4.10. 超级状态 ........................................................................................................ 184 21.4.11. 分支 ................................................................................................................ 184 21.4.12. 合并................................................................................................................ 184 21.4.13. 决策................................................................................................................ 184 21.4.14. 事件................................................................................................................ 185 21.4.15. 转换................................................................................................................ 185 21.4.16. 动作................................................................................................................ 186 21.4.17. 脚本................................................................................................................ 186 21.4.18. 表达式 ............................................................................................................ 187 21.4.19. 变量................................................................................................................ 187 21.4.20. 句柄................................................................................................................ 187 21.4.21. 定时器 ............................................................................................................ 188 21.4.22. 创建定时器 .................................................................................................... 188 21.4.23. 撤消定时器 .................................................................................................... 189 21.4.24. 任务................................................................................................................ 189 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 7 页 / 共 199 页
  • 8. 21.4.25. 泳道................................................................................................................ 190 21.4.26. 分派................................................................................................................ 190 21.4.27. 控制器 ............................................................................................................ 191 21.4.28. 子流程 ............................................................................................................ 191 21.4.29. 条件................................................................................................................ 191 21.4.30. 异常处理程序 ................................................................................................ 192 第 22 章 安全............................................................................................................................... 192 22.1. 要做的事..................................................................................................................... 192 22.2. 认证............................................................................................................................. 192 22.3. 授权............................................................................................................................. 193 第 23 章 工作流的 TDD 文件 .................................................................................................... 194 23.1.工作流 TDD 介绍 ........................................................................................................ 194 23.2. XML 源 ........................................................................................................................ 195 23.2.1. 解析流程包 ...................................................................................................... 195 23.2.2.解析 xml 文件.................................................................................................... 196 23.2.3. 解析 XML 字符串 ........................................................................................... 196 23.3. 测试子流程 ................................................................................................................. 196 第 24 章 可插拨架构................................................................................................................... 197 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 8 页 / 共 199 页
  • 9. 第 1 章 介绍 jBoss jBPM 是一个灵活、可扩展的流程语言框架。jPDL 是建立在这个通用框架之上的 一个流程语言。它使用直观的流程语言以图形化的方式来表示业务流程,使用术语任务、异 步通讯等待状态、定时器、自动操作等。为了将这些操作绑定在一起,jPDL 提供了强有力 的、可扩展的流程控制机制。 jPDL 有一个具有最小的依赖性和尽可能简单易用的 java 库。但它也能被部署在高吞吐 量极为关键的 J2EE 集群应用服务器的环境里。 jPDL 能够使用任何数据库来进行配置,同时也能任何应用服务器上进行部署。 1.1. 概述 核心工作流和 BPM 功能被封装成一个简单的 java 库,它包含了一个用来管理和执行 jPDL 数据库中流程的服务。 图 1-1 jPDL 组件概况 1.2. jPDL 套件 这个套件中包含了捆绑在一起的所有 jBPM 组件的组成的简易下载包。下载包中包括以 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 9 页 / 共 199 页
  • 10. 下内容: • config:标准 java 环境的配置文件 • db:创建数据库的 SQL 脚本文件以及兼容性信息 • designer:用来创作jPDL 流程的eclipse插件,以及安装脚本(这部分内容不包含在 仅jpdl的下载中)。请参考:1.3 节 jPDL图形流程设计器。 • doc:用户手册和 javadoc 文档 • examples • lib:jbpm依赖的库文件. 需要更多的信息请参考:5.3 节 第三方库文件 • server:包含内置 jbpm 控制台 web 应用程序的预先配置好的 JBoss 服务器(这部分内 容不包含在仅 jpdl 的下载中) • src: jbpm 和身份组件(identity component)的源代码 预配置的 JBoss 应用服务器已经安装了以下组件: • Web 控制台打包成一个 web 包。控制台既能够被流程的参与者,也可以由 jBPM 管 理员来使用。 • 工作执行器扮演定时器和异步消息的角色。 在控制台的 web 应用中有一个 servlet 上 下文件监听器,用来执行工作执行器,它产生一个线程池来执行定时器和异步消息。 • jBPM 数据库,包含 jBPM 表的一个内置流程(in-process)的 hypersonic 数据库。 • 已部署进 jBPM 数据库中的示例流程。 • 身份组件库是 web 控制台应用的一部分。在数据库中的身份组件表(那些表以 “JBPM_ID_”开头 )是有效的。 1.3. jPDL 图形流程设计器 jPDL 也包含一个图形化的流程设计工具。这个设计器是创作业务流程的图形化工具。 它是一个 eclipse 插件。 流程设计工具最重要特性就是,不但支持业务分析人员,同时也很好的支持技术开发人 员。它允许从业务流程建模到实际实现之间的平滑转换。 这个插件既可以使用本地更新站点(纯 zip 文件)的方式,来通过标准的 eclipse 软件更 新机制来进行安装,同时也有一个可以将其解压到你的 eclipse 的安装目录来完成安装的功 能包。 1.4. jBPM web 控制台 jBPM web 控制台服务于两个目标。首先,作为一个中央用户接口作用于流程执行所生 成的运行时任务。其次,它是一个管理和监控控制台,控制台允许检查和操作运行时实例。 再有的功能就是业务活动的监控。这些是流程执行的统计信息,他们有助于管理人员找 出瓶颈或其他的优化方法。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 10 页 / 共 199 页
  • 11. 1.5. jBPM 核心库 JBoss jBPM 核心组件是一个纯 java(J2SE)库,用来管理流程定义和流程实例执行的 运行时环境。 JBoss jBPM 是一个 java 库。因此,它能够在任何一个 java 环境上使用,例如:web 应 用、swing 应用、EJB、Webservice 等等。jBPM 库也能够当作一个无状态会话 EJB(stateless session EJB)打包并发布。这样就适合具有极高的吞量要求场合的集群部署和伸缩性。无状 态会话 EJB 在 J2EE 1.3 规范的基础上进行重写, 这样它就可以部署在任何的应用服务器上。 依靠你所使用的功能,jbpm-jpdl.jar 库文件有一些依赖于其他的第三方库。例如 hibernate、dom4j 和其他的库。我们已经付出了极大的努力来要求那些仅仅是你真正使用的 依赖库文件。这些依赖在 第 5 章 部署 中有更多的整理文档。 对于它的持久化,jBPM 内部使用 hibernate。除了传统的 O/R 映射外,hibernate 也能解 决在不同的数据库间的 SQL 方言的差异, 使得 jBPM 可以在目前所有的数据库间进行迁移。 JBoss jBPM API 能够被项目中的任何自定义的 java 软件访问,例如 web 应用、EJB、 web service 组件、消息驱动 bean(message driven beans)或任何其他 java 组件。 1.6. JBoss jBPM 身份组件 JBoss jBPM 能够同包含用户和其他的组织结构信息的企业目录进行整合。但对于项目 来讲,是不会轻易地找到组织结构信息组件的,正因为如此,JBoss jBPM 自身就提供了这 个组件。身份组件使用的模型比传统的 servlet、ejb 和 portlet 所使用的模型更加丰富。 了解更多的信息,请参考:12.11 身份组件 1.7. JBoss jBPM 工作执行器 工作执行器是一个在标准 Java 环境中用来监控和执行工作的组件。用于定时器和异步 消息。在一个企业环境里,JMS 和 EJB 定时服务能够用于此目的。相反,工作执行器能够 用在既没有 JMS 也没有 EJB 的环境里。 可以注册 JobExecutorLauncher servlet 上下文监听器在 web 应用的部署描述符中开始/ 停止工作执行器在创建/销毁 servlet 上下文期这一期间, 也可以开始独立的 JVM 并启动程序 化的工作执行器。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 11 页 / 共 199 页
  • 12. 第 2 章 正式开始 本章将带你完成第一步,获得 JBoss jBPM 并给出立即开始运行基本的指南。 2.1. 下载包概述 下面列出的是当前可用的不周的 jBPM 包。每个包都包含了一个或更多的可下载的文 件。连同这些文件还有其内容的描述,如果是可以安装的话还会有相关的安装手册。 下列被描述的所有的下载都可以在 Sourceforge jBPM 下载页面上找到。 2.1.1. jPDL 3 下载 jBPM jPDL 3 从 Sourceforge.net站点. 这是一个主发布包,它包含了核心引擎和许 多同jPDL一起工作可能会用到的的额外模块。 • jPDL 套件(bpm-jpdl-suite.zip):jPDL套件是一个扩展了的包含设计器和服务器的 运行时。所有的这些组件被预配置来进行交互操作。如果你想快速的开始jBPM,那 么这个就是你要下载的文件。它包含了这个包的所有其他的模块加上图形化的设计 器在一个单独的下载里。解压这个压缩包到你选择的目录,并阅读“readme.html” 这个文件来获得更多的信息和深入的安装指南。有了这个新手工具箱你就能快速的 开始 第 3 章 教程。 • jPDL 运行时引擎(jbpm-jpdl.zip):这个下包含了 jBPM 核心库、文档和依赖库,还 有为参与者和群组管理提供的身份组件。要让它工作,解压压缩包进入你选择的文 件夹。 你将经“jbpm-<version>” 文件夹下找到用户指导手册的提示以及 “readme.html” 文件中的其他重要的资源信息。 2.1.2. jPDL 流程设计器 jPDL套件包中包含jPDL流程设计器。你通常将下载它在jPDL套件包中。但是你也可以 从sourceforge.net站点单独下载JBoss jBPM 流程设计器。设计器是一个eclipse的插件,你可 以用它来创作流程定义并简便的部署这些流程。 插件可以以压缩的Eclipse功能包或者Eclipse 的更新站点两种方式进行下载, 它们在内容上没有什么不同, 唯一的区别就是你所采用的安 装方式的差异。 • Eclipse 更新站点(jbpm-gpd-site-<version>.zip):如果你想绝对确保设计器流畅地 安装, 我们建议你使用 站点更新机制和全新安装的 eclipse。 当然 Eclipse 的版本要 同下载包匹配。为了启动设计器插件,包含在包根目录下的“readme.html”文件 中的下列使用说明帮助你成功安装 GPD(图形流程设计器) 。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 12 页 / 共 199 页
  • 13. • Eclipse 特性包(jbpm-gpd-feature-<version>.zip):如果你讨厌不得不每次都 刷新 Eclipse 的安装,并且你愿意去处理一些可能会发生的问题的话,你可以试着 下载它。在这种情况下释放这个包进入你的 Eclipse 的安装目录(确保你的 plugins 和 feature 目录在 Eclispe 安装的相同的位置) ,覆盖已经存在的相同的名称的目录 和文件。 这样的安装是非常的容易的, 但是当你可能会陷入兼容性问题因为你的覆 盖安装其他属性的安装文件时, 虽然他们有相同的文件名, 但也可能发生版本冲突 的 plugins 是完全相同的,以后可能会有不兼容的问题。安装说明在 readme.html” “ 文件中也还一份。 2.2. JBoss jBPM 项目目录 • 专业支持:JBoss 是这个项目背后的公司,公司提供专业的技术支持、培训 和顾问服务 • 用户手册: 你正在阅读的文档和项目的主要文档的入口 • 论坛: 取得与社区的联系,提出问题并且讨论jBPM • wiki: 额外的信息,大部分为社区提供 • 问题追踪:提交的bug和功能要求 • 下载: BPM 的sourceforge的下载页面 • 邮件列表: 用于公告发布邮件列表 • javadocs: 部分下载的 doc/javadoc 文档目录。 2.3. 访问源代码 CVS 服务器 2.3.1. 匿名 CVS 访问 作为候选,你可以使用下列信息从 CVS 服务器获得 JBoss jBPM 项目: • 连接类型: pserver • 用户名: anonymous • 主机:anoncvs.forge.jboss.com • 端口:2401 (缺省) • 库路径: /cvsroot/jbpm 2.3.2. 开发人员 CVS 访问 为了获得CVS的开发人员访问权限,你必须签署贡献者协议并有一个ssh 钥匙(key)。更 多的信息在JBoss cvs repository wiki 页面 上可以找到。 • 连接类型: ext over ssh (eclipse 里的 extssh) cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 13 页 / 共 199 页
  • 14. • 用户名: sf.net 用户名或 jboss 用户名 • 主机:cvs.forge.jboss.com • 端口:2401 (缺省) • 库路径: /cvsroot/jbpm • 标签::pserver:anonymous@cvs.forge.jboss.com:/cvsroot/jbpm cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 14 页 / 共 199 页
  • 15. 第 3 章 教程 本教程将向你展示使用 jpdl 构建基本流程及管理运行时执行的 API 的使用方法。 教程的方式是解释一系列的例子, 每个例子以特定的主题为焦点并包含大量的注释。这 些例子在 jBPM 下载包的 src/java.examples 目录中也能找到。 最好的学习方法就是创建一个项目并且在给定实例的基础上试着修改。 Eclipse 用户首先要做的是: 下载 jbpm-3.0-[version].zip 文件并解压到你的系统中。 然后 执行"File" --> "Import..." --> "Existing Project into Workspace",点击"Next"按钮,找到 jBPM 根目录点击"Finish"按钮。现在你的工作空间(workspace)中已经有一个 jbpm.3 的项目了。 现在就可以在 src/java.examples/ 文件夹下找到教程的例子,打开这些例子时,可以使用 "Run" --> "Run As..." --> "JUnit Test" 方式来运行它们。 jBPM包含一个图形化设计工具来创作例子中的XML文件。你可以找到下载说明在 2.1 下载包概述。你不需要这个图形化设计工具来完成本教程。 3.1. Hello World 实例 流程定义是一个定向图,由节点和流程转换组成。hello world 流程中有 3 个节点,来看 下如何将他们放在一起, 我们先不用流程设计工具来开始一个简单流程。 下面的图形显示了 hello world 流程的图示。 start s end 图 3-1 hello world 流程图 package org.zym.helloworld; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 15 页 / 共 199 页
  • 16. import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import junit.framework.TestCase; public class HelloWorld extends TestCase { public void testHelloWorldProcess() { // 这个方法显示一个流程定义和一个流程定义的执行。流程定义有三个节点: // 未命名的start-state, state 's' 和 命名为 'end' 的 end-state 节点。 // 下一行解析 xml 文本到 ProcessDefinition 类, // ProcessDefinition 是作为java对象的的流程的正规描述 ProcessDefinition processDefinition = ProcessDefinition .parseXmlString("<process-definition>" + " <start-state>" +" <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>"); // 下一行建立一个流程定义的执行,在创建后,流程执行有一个执行的主路径 // (=root token),定位在start-state节点位置。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 16 页 / 共 199 页
  • 17. ProcessInstance processInstance = new ProcessInstance(processDefinition); // 创建流程执行的主路径 (=root token) Token token = processInstance.getRootToken(); // 在创建流程定义后,确定主路径牌开始状态节点处 assertSame(processDefinition.getStartState(), token.getNode()); // 让我们开始流程执行,通过它的缺省流程转换离开开始状态节点 token.signal(); // signal 方法将阻止直到流程执行进入一个等待状态 // 流程执行将已经进入了这第一个等待状态,状态 's' 节点。 // 所以执行的主路径现在定位在状态's' assertSame(processDefinition.getNode("s"), token.getNode()); // 让我们发送另一个信号。假设执行离开状态's'通过缺省的流程转换 token.signal(); // 现在信息方法返回,因为这个流程实例已经到达结束节点(end-state) assertSame(processDefinition.getNode("end"), token.getNode()); } cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 17 页 / 共 199 页
  • 18. } 译者注:以上的代码是可执行的 Junit 测试用例程序,复制到 eclipse 中即可执行。与原文档的代码有所差 异。但主测试方法是完全一样的。 3.2. Database 实例 jBPM 的基本属性之一就是流程在等待状态时,能够将流程执行的状态在数据库中进行 持久化。下一个实例将向你显示如何在 jBPM 数据库中存储一个流程实例。 实例也暗示了可能遇到的上下文。独立的方法被用来创建用户代码的不同部分。例如: Web 应用中的一处代码开始流程并在数据库中持久化执行。后来,一个消息驱动 bean 从数 据库中加载流程实例并恢复它的执行。 更多的关于jBPM持久性的信息能在 第 7 章 持久性 中找到。 import java.util.List; import junit.framework.TestCase; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.db.GraphSession; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; public class HelloWorldDbTest extends TestCase { cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 18 页 / 共 199 页
  • 19. static JbpmConfiguration jbpmConfiguration = null; static { // 实例配置文件,例如此文件能够在 src/config.files 文件夹下找到。 // 正常的情况下此配置文件的信息是在 jbpm.cfg.xml 资源文件中, // 但这里我们放配置文件信息作为一个XML字符串 // 首先我们创建 JbpmConfiguration 静态变量。 // 一个 JbpmConfiguration 能够用于系统中的所有线程,那就是我们让它是static类型的原 因。 jbpmConfiguration = JbpmConfiguration.parseXmlString( "<jbpm-configuration>" + // jbpm-context 上下文机制把jBPM核心引擎和jbpm使用的服务从环境中分开 " <jbpm-context>" + " <service name='persistence' " + " factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" + " </jbpm-context>" + // 而且 jbpm 所使用的资源文件都参考 jbpm.cfg.xml 文件 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 19 页 / 共 199 页
  • 20. " <string name='resource.hibernate.cfg.xml' " + " value='hibernate.cfg.xml' />" + " <string name='resource.business.calendar' " + " value='org/jbpm/calendar/jbpm.business.calendar.properties' />" + " <string name='resource.default.modules' " + " value='org/jbpm/graph/def/jbpm.default.modules.properties' />" + " <string name='resource.converter' " + " value='org/jbpm/db/hibernate/jbpm.converter.properties' />" + " <string name='resource.action.types' " + " value='org/jbpm/graph/action/action.types.xml' />" + " <string name='resource.node.types' " + " value='org/jbpm/graph/node/node.types.xml' />" + " <string name='resource.varmapping' " + " value='org/jbpm/context/exe/jbpm.varmapping.xml' />" + "</jbpm-configuration>" ); } public void setUp() { jbpmConfiguration.createSchema(); } cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 20 页 / 共 199 页
  • 21. public void tearDown() { jbpmConfiguration.dropSchema(); } public void testSimplePersistence() { // 下面这3个方法调用中,所有数据都是通过数据库传递的。 // 在这个单元测试里,因为我们想要测试一个完整的流程环境,所以这3个方法在交互后被 执行。 // 但实际上,这些方法代表了对服务器的不同的请求。 // 因为我们开始的是一个干净的、空的内存数据库,所以我们不得不先部署流程。 // 实际上,这个以前是由流程开发人员来完成的。 deployProcessDefinition(); // 假设我们想开始一个流程实例(=流程执行)当用户在web应用中提交一个表单时... processInstanceIsCreatedWhenUserSubmitsWebappForm(); // 然后,异步消息到来流程继续执行 theProcessInstanceContinuesWhenAnAsyncMessageIsReceived(); } public void deployProcessDefinition() { // 这个测试显示流程定义和一个流程定义的执行。流程有三个节点: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 21 页 / 共 199 页
  • 22. // 未命名的开始状态、状态's'和一个命名为'end'的结束状态 ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition name='hello world'>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); // 查找上面配置的pojo持久性上下文生成器 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // 在数据库中部署流程定义 jbpmContext.deployProcessDefinition(processDefinition); } finally { // 拆毁 pojo 持久性上下文。 // 这里包含刷新SQL来插入流程定义到数据库 jbpmContext.close(); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 22 页 / 共 199 页
  • 23. } } public void processInstanceIsCreatedWhenUserSubmitsWebappForm() { // 在方法里的代码能够放在struts-ation或JSF管理bean中。 // 查找上面配置的pojo持久性上下文生成器 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { GraphSession graphSession = jbpmContext.getGraphSession(); ProcessDefinition processDefinition = graphSession.findLatestProcessDefinition("hello world"); // 用从数据库中检索到的processDefinition, // 我们能创建一个像在hello world例子中那样的流程定义执行(那个例子是持久性的) ProcessInstance processInstance = new ProcessInstance(processDefinition); Token token = processInstance.getRootToken(); assertEquals("start", token.getNode().getName()); // 让我们开始流程执行 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 23 页 / 共 199 页
  • 24. token.signal(); // 现在流程是在状态's'。 assertEquals("s", token.getNode().getName()); // 现在 processInstance 被存储在数据库中。所以流程执行的当前状态被存储在数据库中。 jbpmContext.save(processInstance); // 下面的方法将从数据库中取出流程实例,并通过提供的另一个外部信号恢复执行 } finally { // 拆毁pojo持久性上下文。 jbpmContext.close(); } } public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() { // 方法里的代码能够成为消息驱动Bean的内容. // 查找上面配置的pojo持久性上下文生成器 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { GraphSession graphSession = jbpmContext.getGraphSession(); // 首先,我们必须得到数据库中取出的流程实例.有几个选项是我们要知道的 // 什么样的流程实例我们正在处理的.在这个简单测试用例是最容易的,因为只是查找流 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 24 页 / 共 199 页
  • 25. 程实例的所有列表. // 那样就只给我们一个结果.所以让我们查看流程定义. ProcessDefinition processDefinition = graphSession.findLatestProcessDefinition("hello world"); // 我们搜索流程定义的所有流程实例 List processInstances = graphSession.findProcessInstances(processDefinition.getId()); // 因为我们知道这个单元测试的上下文里,只有一个执行。在实际的环境中, // processInstanceId 能够从到达的消息内容或用户所做的选择中进行提取。 ProcessInstance processInstance = (ProcessInstance) processInstances.get(0); // 现在我们继续执行,注意那个 processInstance 代理信号到达执行主路径(=root token)。 processInstance.signal(); // 在这个信号后,我们知道流程执行应该已到了结束状态 assertTrue(processInstance.hasEnded()); // 现在我们可以更新数据库中的执行状态 jbpmContext.save(processInstance); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 25 页 / 共 199 页
  • 26. } finally { // 拆毁pojo持久性上下文。 jbpmContext.close(); } } } 译者注:以上的代码是可执行的 Junit 测试用例程序,复制到 eclipse 中即可执行。与原文档的代码有所差 异。但主测试方法是完全一样的。 3.3. Context 实例:流程变量 流程变量包含流程执行期间的上下文信息。流程变量同 java.util.Map 类相似,映射变量 命名到值,这个值是 java 对象。流程变量作为流程实例的一部分被持久化。为了保持简单, 这个例子中我们只显示同变量一起工作的 API,而没有持久性。 更多关于变量的信息能够在 第 11 章 上下文 中找到。 import junit.framework.TestCase; import org.jbpm.context.exe.ContextInstance; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; public class TestProcessVarible extends TestCase { cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 26 页 / 共 199 页
  • 27. public void testProcessVaribles(){ // 这个例子还是从hello world流程开始,这次甚至没有修改. ProcessDefinition processDefinition = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state>" + " <transition to='s' />" + " </start-state>" + " <state name='s'>" + " <transition to='end' />" + " </state>" + " <end-state name='end' />" + "</process-definition>" ); ProcessInstance processInstance = new ProcessInstance(processDefinition); // 从流程实例中取得同流程变量一起工作的上下文实例 ContextInstance contextInstance = processInstance.getContextInstance(); // 在流程已经离开开始状态之前,我们将在流程实例的上下文中设置一些变量. cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 27 页 / 共 199 页
  • 28. contextInstance.setVariable("amount", new Integer(500)); contextInstance.setVariable("reason", "i met my deadline"); // 从现在开始,这些变量同流程实例关联在一起. // 流程变量现在可以通过显示的API被用户代码来访问. // 流程变量也可以作为流程实例的一部分存储到数据库中. processInstance.signal(); // 通过上下文实例是可以可以访问变量的 assertEquals(new Integer(500), contextInstance.getVariable("amount")); assertEquals("i met my deadline", contextInstance.getVariable("reason")); } } 3.4. Task 分配实例 下一实例中我们将显示你如何能够给用户分配任务。 因为工作流引擎和组织结构模型是 分 开 的 , 这样 用 来 计 算参 与 者 的 表达 式 语 言 总是 会 受 到 限制 。 所 以 ,你 不 得 不 指定 AssignmentHandler 的实现来计算任务的参与者。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 28 页 / 共 199 页
  • 29. package org.zym; import junit.framework.TestCase; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.jbpm.taskmgmt.exe.TaskInstance; public class TaskAssignmentTest extends TestCase { public void testTaskAssignment() { // 下面显示的流程基于hello world流程的.状态节点被任务节点替换. // 任务节点在JPDL中代表在流程继续执行前的等待状态和生成将要完成的任务的一 个节点 ProcessDefinition processDefinition = ProcessDefinition .parseXmlString("<process-definition name='the baby process'>" + " <start-state>" +" <transition name='baby cries' to='t' />" + " </start-state>" + " <task-node name='t'>" +" <task name='change nappy'>" +" <assignment cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 29 页 / 共 199 页
  • 30. class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />" +" </task>" + " <transition to='end' />" + " </task-node>" + " <end-state name='end' />" + "</process-definition>"); // 建立一个流程定义的执行 ProcessInstance processInstance = new ProcessInstance(processDefinition); Token token = processInstance.getRootToken(); // 让我们开始流程执行,通过缺省的流程转换离开开始状态(start-state) token.signal(); // 信号将阻塞流程执行,并进入等待状态,在本例中,也就是任务节点(task-node). assertSame(processDefinition.getNode("t"), token.getNode()); // 当流程执行到任务节点时,任务"换尿布"被创建了,并且 NappyAssignmentHandler 被 调用 // 来检查任务应该分配给谁. NappyAssignmentHandler 返回 'papa'. // 在真实的环境中,任务将由 org.jbpm.db.TaskMgmtSession 类中的方法从数据库中被 捕获. // 因为我们不想将持久化的复杂性引入此例,我们只是处理流程实例的第一个任务实例 // (我们知道在测试环境中也只有一个) TaskInstance taskInstance = (TaskInstance) processInstance .getTaskMgmtInstance().getTaskInstances().iterator().next(); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 30 页 / 共 199 页
  • 31. // 现在我们检查任务实例是否真正的赋给了 'papa'. assertEquals("papa", taskInstance.getActorId()); // 现在我们假设爸爸已经完成了任务并标记任务完成了 taskInstance.end(); // 由于这是上一个任务要做的,任务触发继续流程实例执行. assertSame(processDefinition.getNode("end"), token.getNode()); } } 译者增加* 由于此处使用了自定义的参与者赋值处理类,此类可以在根目录的 examples 下找到,此例的源代码如下: package org.jbpm.tutorial.taskmgmt; import org.jbpm.graph.exe.*; import org.jbpm.taskmgmt.def.*; import org.jbpm.taskmgmt.exe.Assignable; public class NappyAssignmentHandler implements AssignmentHandler { private static final long serialVersionUID = 1L; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 31 页 / 共 199 页
  • 32. public void assign(Assignable assignable, ExecutionContext executionContext) { assignable.setActorId("papa"); } } 3.5. 自定义动作实例 动作(Action)是绑定自定义的 java 代码到 jbpm 流程的一个机制。动作可以用它自己 的节点进行关联(假设他们在流程的图示中是相关的) 。或者动作要可以放在事件(event) 上,例如:流程转换、离开节点或进入节点,如果那样的话,动作不会是图示的一部分,但 在运行时流程执行击发事件执行时也会执行。 我们来看下我们例子中的动作的实现:MyActionHandler。这个动作实现程序并没有真 正的做什么实际的事…,它只是设置了 isExecuted 这个布尔变量的值为真(true) 。变量 isExecuted 是静态的,以至于能够在动作处理内部访问,同时来校验它的值。 更多的关于动作的信息可以在 10.5 动作 节中找到。 package org.zym; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.exe.ExecutionContext; // MyActionHandler 代表一个在流程执行期间被用户代码执行的类 public class MyActionHandler implements ActionHandler { // 测试每个测试开始前,设置 isExecuted 值为 false public static boolean isExecuted = false; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 32 页 / 共 199 页
  • 33. // 动作设置 isExecuted 值为 true,以至于当动作正在被执行时单元测试中能够显示 public void execute(ExecutionContext executionContext) throws Exception { isExecuted = true; } } 前面提到过,在每个测试开始前,我们将设置静态属性 MyActionHandler.isExecuted 的 值为假(fasle)。 // 每个测试将要开始时会设置 MyActionHandler 的静态成员 isExecuted 的值为 false public void setUp() { MyActionHandler.isExecuted = false; } 我们将在转换中开始一个动作。 package org.zym; import junit.framework.TestCase; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.tutorial.action.MyActionHandler; public class TransitionActionTest extends TestCase { cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 33 页 / 共 199 页
  • 34. // 每个测试将要开始时会设置 MyActionHandler 的静态成员 isExecuted 的值为 false public void setUp() { MyActionHandler.isExecuted = false; } public void testTransitionAction() { // 下一个流程是hello world流程的一个变体. // 我们在状态's'到结束状态的转换上增加了一个动作. // 这个测试的目的是展示下在 jBPM 流程中集成 java 代码时多么的容易 ProcessDefinition processDefinition = ProcessDefinition .parseXmlString("<process-definition>" + "<start-state>" + "<transition to='s' />" + "</start-state>" + "<state name='s'>" + "<transition to='end'>" + "<action class='org.jbpm.tutorial.action.MyActionHandler' />" + "</transition>" + "</state>" + "<end-state name='end' />" + "</process-definition>"); // 开始一个新的流程定义的执行. cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 34 页 / 共 199 页
  • 35. ProcessInstance processInstance = new ProcessInstance(processDefinition); // 下一个信号将导致执行离开开始状态并进入状态's'. processInstance.signal(); // 此处显示 MyActionHandler 还没有被执行 assertFalse(MyActionHandler.isExecuted); // 而且执行的主路径被定位在状态 's' 处 assertSame(processDefinition.getNode("s"), processInstance .getRootToken().getNode()); // 下一个信号将触发根令牌执行,令牌携动作执行流程转换,而且在调用信号方法时 将执行动作 processInstance.signal(); // 此处我们可以看到在调用信号方法时 MyActionHandler 被执行了 assertTrue(MyActionHandler.isExecuted); } } 下个例子显示了相同的动作,但现在这个动作分别被放在了进入节点和离开节点事件 中。注意这个节点同转换相比已经有了更多的事件类型,而那个只有一个事件。因此置于一 个节点上的动作应该放在一个事件元素上。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 35 页 / 共 199 页
  • 36. package org.zym; import junit.framework.TestCase; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.tutorial.action.MyActionHandler; public class NodeEventActionTest extends TestCase { // 每个测试将要开始时会设置 MyActionHandler 的静态成员 isExecuted 的值为 false public void setUp() { MyActionHandler.isExecuted = false; } public void testNodeEventAction() { ProcessDefinition processDefinition = ProcessDefinition .parseXmlString("<process-definition>" + " <start-state>" +" <transition to='s' />" + " </start-state>" + " <state name='s'>" +" <event type='node-enter'>" cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 36 页 / 共 199 页
  • 37. +" <action class='org.jbpm.tutorial.action.MyActionHandler' />" +" </event>" +" <event type='node-leave'>" +" <action class='org.jbpm.tutorial.action.MyActionHandler' />" +" </event>" + " <transition to='end'/>" + " </state>" + " <end-state name='end' />" + "</process-definition>"); ProcessInstance processInstance = new ProcessInstance(processDefinition); assertFalse(MyActionHandler.isExecuted); // 下一个信号将导致流程执行离开开始状态并进入状态's'. // 以至于进入状态's',此后开始执行动作 processInstance.signal(); assertTrue(MyActionHandler.isExecuted); // 重置 MyActionHandler.isExecuted MyActionHandler.isExecuted = false; // 下一个信号将触发流程执行,离开状态's',所以这个动作将再次被执行 processInstance.signal(); // 瞧 :~ assertTrue(MyActionHandler.isExecuted); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 37 页 / 共 199 页
  • 38. } } cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 38 页 / 共 199 页
  • 39. 第 4 章 面向图的程序设计 4.1. 介绍 本章可以给出 JBoss jBPM 的明细单,完整的愿景概览、当前战略下的理念以及 JBoss jBPM 项目未来的方向。这一愿景和传统的取向相比有极大的不同。 首先,我们相信复合流程(multiple process)语言。不同环境和不同目的要求他们自己 特定的流程语言。 其次,面向图形的程序设计是一个新的技术实现,这种技术实现是针对所有基于图的流 程语言的基础。 这样做的最主要的好处就是它定义了一种针对所有类型的流程语言基础技术。 当今的软件开发越来越多的依赖领域特定语言。一个典型的 java 开发人员将使用相当 多的领域特定语言。项目中的 XML 文件作为各种框架的输入就可以认为是领域特定语言。 领域特定语言 Java Ruby … jPDL BPEL PageFlow … IoC-configs Hibernate 面向图的程序设计 Mappings 程序设计语言 基于图的语言 …… 图 4-1 基于图的语言的定位 工作流、BPM、orchestration 和 pageflow 的领域特定语言是基于定向图执行之上的。 其他的像 hibernate 的映射文件、ioc-configuration 则不是这样的。面向图的程序设计是基于 执行图的领域特定语言的基础。 面向图的程序设计语言是非常简单的技术,它描述了如何定义图和被执行在纯 OO 程序 设计语言上。 在4.5 应用领域,我们将涉及大多数的常用的流程语言,这些语言都是可以使用面向图 的程序设计方法来实现的,如工作流、BPM、编排(orchestration)和pageflow。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 39 页 / 共 199 页
  • 40. 4.1.1. 域特定语言 每一流程语言都能被认为是一个领域特定语言(Domain Specific Language ,DSL)。DSL 透视图让开发人员能很好的理解流程语言和纯 OO 程序设计如何关联的。 本节也许会留下我们单纯地专注在以程序设计环境为中心的印象。 这是千真万确的。 面 向图的程序设计包含了从 API 库到完全成熟的 BPM 成套产品的整个 BPM 产品线。这套 Bpm 产品是完整的软件开发环境,这个环境是以业务流程为中心的。在那种类型的产品中,在程 序语言中的编码被尽可能的避免了。 一个重要的领域特定语言的方面就是每一种语言都有某种语法。那种语法能够被表述为 一个领域模型。 假如是 java 的话,这就是类、方法、域和构造器等等,而对于 jPDL 就是节点、转换和 动作等等,而对于规则(rules),这就是条件和结果等等。 DSL 的主旨就是开发人员当使用特定的语言创作为工件时可以考虑那些语法。IDE 工 具建立语言语法。然后,不同的编辑器就能创作工件。例如:jPDL 流程有一个图形编辑器 和一个 XML 源代码查看编辑器。而且有不同的方法来存储相同的工件:对于 jPDL 来说, 这可能是一个流程 XML 文件或序列化了的节点和转换的对象图。 另一个(理论上的)例子是 java:你可以在系统上使用 java 类文件格式。当用户启动 编辑器时,源文件就被生成了。当用户保存时这个编译过的类也就被保存了… 10 年前,开发人员最大部分的精力花在写代码上了。现在一个转换已经取代了这个位 置,他们而是去学习和使用领域特定语言。 这种趋势仍将继续而且导致的结果就是开发人员 将在框架和在主机平台上开发软件之间有了更大的选择。JBoss SEAM 正走向那个方向。 那些语言中的一些是基于图执行之上的。例如:java 的工作流 jPDL、服务编排的 BPEL 和 SEAM pageflow 等等,对于所有这些类型的领域特定语言,面向图的程序设计是一个通 用基础。 将来,对于每一个语言、开发人员能够选择一个适合自己的最好的编辑器。例如:重量 级的程序员可能更喜欢以源文件格式编辑 java,因为那样工作真的很快。但是缺少经验的 java 开发人员可能会选择一个通过点击编辑器就能编写一个功功能性的 java 类。Java 源编 辑将更加灵活。 查看这些领域特定语言(包括程序设计语言)的另一种方式是从结构化软件透视图。面 向对象的程序设计(OOP)使用数据通过分组设计方法增加结构。面向切面程序设计(AOP) 增加一个方法结构通过分组方法去提取关注的交叉切面。依赖注入(DI)和反转控制(IoC) 框架增加简易的对象图配线。 而且通过结构化图执行周围的软件项目的一部分,基于执行语 言的图(这提及的)是能有帮助的去处理复杂性的事务。 一个在领域特定语言(DSL)的基本解释能够在Martin Fowler的博客(bliki)上找到。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 40 页 / 共 199 页
  • 41. 但它之后的愿景在Martin的关于《Language Workbenches》的文章中有更加详细的说明。 4.1.2. 基于图的语言的属性 有为数众多的基于图的流程语言。在环境和焦点里有很大的不同。举例来讲,BPEL 作 为一个 XML 被计划,这个 XML 是基于在企业服务总线(Enterprise Service Bus ,ESB)架构 之上的服务编排组件的。并且 pageflow 流程语言可以解释 web 应用的页面是如何能够被导 航的。这些是两种完全不同的环境。 尽管有这些不同,你也会发现在几乎每一个流程语言里有两个特征:等待状态支持 (support for wait states)和图示(graphical representation)。这不是偶然的因为这两种特征在 纯粹的面向对象程序设计语言(如 java)中没有被充分的支持。 面向图的程序设计是一种用面向对象程序设计语言去实现的技术。 面向图的程序设计对 OOP 的依赖意味着实现于面向图的程序设计的所有具体流程语言将使用 OOP 来开发。 但是 这不是说流程语言本身暴露出了这种 OOP 的任何特性。例如:BPEL 和 OO 程序设计没有 任何关系并且它可以在面向图的程序设计上被实现。 4.1.2.1. 等待状态支持 命令式的程序设计语言(如 java)常被用来表示一个系统中被执行的顺序指令。没有等 待指令。命令式的语言描述起来是完美的,例如一个服务器的请求响应周期。系统将连续执 行顺序指令直到请求被处理并且完成响应。 但一个这样的请求是较大的场景典型代表。例如:客户端提交一个采购订单,这一订单 将被采购经理验证。在审批后,这一信息必须录入到 ERP 系统中去。大量的对服务器的请 求是相同的大场景的部分。 所以流程语言是描述较大场景的语言。一个非常重要的区别是我们必须认为这是一个系 统(编排)上是可执行的场景,而且场景也是在多系统(安排)间可以描述协议的。 基于图的程序设计实现技术只是瞄准可以在机器(编排机)上执行的流程语言。 所以一个编排流程根据一个系统描述整个场景.例如:当客户端提交一个订单时开始流 程。流程的下一步是订单经理审批。 所以系统必须在订单经理的任务列表中增加一个入口点 和一个等待(状态)直到订单经理处理这个请求的输入。 当这个输入接收后,流程继续执行。 现在一个消息送达到 ERP 中流程处理待状态直到响应消息返回。 所以为一个系统描述整个场景,我们需要一个应付等待状态的机制。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 41 页 / 共 199 页
  • 42. 在大多数的应用领域, 执行必须在等待状态时被持久化。 那也就是为什么只阻塞线程是 不足够的原因。聪明的 Java 程序员也许会考虑 Object.wait()和 Object.notify()方法。那种处理 用于模拟等待状态还可以,但问题是线程是不可被持久化的。 连续(Continuations)是使线程(和上下文变量)可持久化的技术。这个也许是足够 解决等待状态问题的了。而且我们也将在下一节讨论,图示在许多应用领域也是重要的。而 且连续(Continuations)是一个基于命令式程序设计的技术,所以它是不适合的对于图形 表示。 所以支持等待状态的一个重要的方面就是执行需要是可持久化的。 不同的应用领域也许 有不同的需求来持久化这样一个执行。对于大多数工作流,BPM 和编排应用,执行需要在 关系数据库中持久化。代表性的是,一个流程执行中的状态转换将和数据库中的一个转换进 行通信。 4.1.2.2. 图形化表示 软件开发的某些方面得益于基于图的途径是非常好的。 业务流程管理是基于图的语言的 应用领域中的最明显的一个。在那个例子中,在业务分析和开发人员间的通讯是被改进的用 基于图的业务流程图解像通用语言那样。参考4.5.1 业务流程管理(BPM)节 另一方面能够从图示中得益的是页面流(pageflow)。假若那样的话,在图示里页面、 导航和动作命令是被显示和链接在一起的。 在面向图的程序设计里,我们瞄准表示一些执行的表单的图表。那是和诸如 UML 类图 有明显的区别的,这种 UML 类图表示一个 OO 数据结构的静态模型。 此外图形表示可以被看作在 OO 程序设计中错过的特性。 而且也没有什么明智的方法能 够以图形化的表示 OO 程序的执行。所以在 OO 程序和图形视图间没有什么直接关系。 面向图程序设计,是以图为中心并且是一个的真正的软件制品,如描述流程图的 XML 文件。因为图形视图是软件的固有部分,它总是同步的。无须将一个图形需求插翻译成软件 设计。软件是以图进行组织的。 4.2. 面向图的程序设计 这里我们展示的是一个基于图的执行语言的实现技术。这里展示的技术是基于与图集成 的运行时的,其他的图执行技术都是基于消息队列或代码生成的。 本节会解释这个策略如何在 OO 程序设计语言上实现图执行。那些同设计模型是相似 的,它是命令模式(command pattern)和责任链模式(chain of responsibility pattern)的结合 体 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 42 页 / 共 199 页
  • 43. 我们将用最简单的可能模型来开始并一点点的扩展它。 4.2.1. 图结构 首先,图的结构是用节点(Node)和转换(Transition)类来表示的。一个转换有一个 方向,因此节点具有了离开和到达转换(的事件) 。 离开转换 转换 节点 目标 图 4-2 节点和转换类 4.2.2. 执行 执行(execution)模型在图结构上定义,它看起来像一个有限状态机或 UML 状态图。 实际上面向图程序设计能够用来实现那些各类的行为,但是也可以做更多的事。 一个执行(也叫令牌[token])用一个叫做 Execution 的类来表示。一个执行有一个一当 前节点的引用。 节点 执行 节点 图 4-3 执行(Execution)类 转换能够用方法 take 从源节点到目标节点通过执行。 执行 +take(in Exection e) : void 图 4-4 转换(Transition)的 take 方法 当一个执行到达节点时,那个节点被执行。这个节点执行方法来负责传播这个执行。传 播执行意味着能够传递执行到节点,也就是通过它的离开转换到下一个节点。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 43 页 / 共 199 页
  • 44. 节点 +execute(in Execution e) : void 图 4-5 节点(Node)的 execute 方法 当一个节点的 execute 方法不能传播执行时,它表现为一个等待状态。而且当一个新的 执行被创建时,它被初始为一个开始节点并且等待一个事件。 事件(event)被赋给一个执行而且它有触发执行开始移动。如果事件赋给与当前节点 相关的离开转换的话, 执行将处理那个转换。执行然后将继续传播直到它进入另一个具有等 待行为的节点。 执行 +event(in Event e) : void 图 4-6 执行的 event 方法 4.2.3. 流程语言 所以现在我们能够看到已经支持了两个主要的特性:等待状态和图形化表示。 在等待状态期间,执行只是指向图中的一个节点。流程图和执行都能被持久化。例如: 使用 O/R 映射工具存到一个关系数据库中或者是序列化图存成一个文件。 此外你可以看到那个节点和转换形成图并且以后是直接和一个图示结合的。 流程语言无非是一系列节点实现。每个节点实现和流程构图(constructs)进行通信。 流程构图的需要的行为通过覆盖 execute 方法来实现。 这里显示一个有 4 个构图的流程语言的例子:一个开始状态、一个条件、一个任务和一 个结束状态。这个例子没有关联 jPDL 流程语言。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 44 页 / 共 199 页
  • 45. Node +execute() : void StartState Decision Task EndState +execute() +execute() +execute() +execute() 流程语言示例 图 4-6 流程语言示例 具体的节点对象现在可以用来建立流程图在我们流程语言示例中。 销售:开始状态 条件(amount>5K) 是 否 仔细检查:任务 交付:任务 结束:结束状态 图 4-8 流程示例 当创建一个流程的新的执行时,我们开始定位这个执行在开始节点。因此只要执行不接 收到一个事件,这个执行就将一直定位在开始状态。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 45 页 / 共 199 页
  • 46. 开始销售:开始状态 e:Execution 条件(amount>5K) 是 否 图 4-9 新执行 现在让我们看下当一个事件到达时会发生什么。在最初状态下,我们击发缺省的事件将 和缺少的转换进行通信。 也就是在执行对象上调用事件方法。 这个事件方法将传播找到缺少的离开转换并传递执 行通过转换,调用转换的 take 方法并将其自己作为一个参数进行传递。 这个转换将传递执行到决策节点并调用 execute 方法。让我们假设这个决策的 execute 方法发送了“是”这个事件到执行上。那将导致这个执行继续通过“是”转换并且执行将到 达任务节点——“仔细检查”。 让我们假设执行“仔细检查”任务节点的实现加一个入口刊检查员的任务列表,然后等 待检查员输入而不会进一步传播执行。 现在,执行将一直定位在“极细检查”任务节点。所有的嵌套调用将开始运行直到那个 最初的事件方法返回。 e:Execution 条件(amount>5K) 是 否 仔细检查:任务 交付:任务 图 4-10 在“仔细检查”等待状态的执行 4.2.4. 动作 在一些应用领域一定有一种方法来包含程序设计逻辑的执行,而不用为它引入节点。在 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 46 页 / 共 199 页
  • 47. 业务流程管理示例这是一个非常重要的方面。业务分析负责图形表示而开发人员负责让其执 行。如果开发人员必须改变图表来囊括业务分析中没有吸引力的技术细节的话将是不可接受 的。 一个动作也是一个执行方法的命令。动作能够同事件关联。 当执行正在执行时,有两个基本事件:节点离开和结节进入被 Node 类击发。连同事件 一起。 伴随有引起了要被处理的转换的事件,使得自由的注入程序设计逻辑进入这个图的执 行。 隐藏动作 隐藏动作 当执行“是”转换 当进入“交付”节点 时,调用我的java 时,调用我的java bean: bean: MyStats.update Deliveries.Start 条件(amount>5K) 是 否 仔细检查:任务 交付:任务 图 4-11 图形视图中的隐藏动作 每个事件可以关联一系列的动作列表。所有的动作将随着事件的触发而被执行。 4.2.5. 同步执行 默认的执行的传播是同步的。在4.3.4 异步连续 节,我们将要看到缺省行为是如何被改 变的。 当事件送到执行时,执行开始。那个执行将要开始传播通过转换并进入节点。如果节点 决定传播这个执行,这个 take 方法被调用了在离开转换上而且这个执行进一步传播。通过 缺省,所有的这些传播作为嵌套方法调用被处理。 那就意味着当执行已经进入新的等待状态 时,原始事件方法只能返回。所以这个执行在一个事件方法的调用期间能够遍历多个节点。 典型的,signal 方法在转换内部被调用了。这就意味着在一个转换里,在流程图上这个 执行可以通过多个节点潜在地移动。 那个结果在系统上有很大的程度的性能益处,即每个节 点需要一个转换。 另一个同步执行的益处是有更多的异常处理选项。 如果所有节点是被同步执行的, 所有 执行的传播将被嵌套方法调用。当 signal 方法返回时,这个 signal 方法的调用方将知道新的 等待状态已经到而且没有问题发生。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 47 页 / 共 199 页
  • 48. 4.2.6. 代码示例 为了让人们了解面向图的程序设计的原则,我们用少于 130 行的代码写了四个类。你可 以只是读代码就能想法或者你能真正的和他们一起开始工作及实现自己的节点类型。 这有示例代码:  Execution.java  Node.java  Transition.java  Action.java 你可以下载整个源项目(297KB)并且自己开始。它包含一个 eclipse 项目,所以你只 要在你的 eclipse 中导入它就可以开始了。在下一节还提及了一系列的用来显示基本的流程 执行和高级的图执行概念测试。 4.3. 面向扩展图的程序设计 上一部分介绍了纯面向图编程模型用它的最简单的表单。这部分将讨论基于图语言的各 个方面以及面向图的程序设计怎样才能被使用或扩展这些需求。 4.3.1. 流程变量 流程变量维护流程执行的上下文数据。在一个保险说明流程时在,'claimed amount'、 'approved amount' 和 'isPaid'是流程变量的好例子。在许多方面,他们是同类的成员域是相 似的。 面向图的设计有了流程变量支持可以容易地被扩展, 这些流程变量是与执行关联的一系 列的键值(key-value)对。并发执行路径(Concurrent execution paths)和流程组成(process composition)将是有点复杂的事。如果发生执行的并发路径或子流程那么界定(scoping) 规则将定义流程变量的可视性。 工作流数据模型是一个通用的研究报告,在界定类型上的这个报告可以被应用到子流程 和并发执行中的流程变量。 4.3.2. 并发执行 假设你使用工作流基于图的流程语言开发一个“sale”流程。在客户端提交订单后中, 有一系列的客户端记账活动并且还有一系列的有关运输项目的活动。 正像你想象的一样,记 账活动和运输活动能够被并行处理。 那样的话,一个执行将不足以保持整个流程状态的跟踪。让我们逐步来扩展基于图程序 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 48 页 / 共 199 页
  • 49. 设计模型并增加并发执行支持。 首先, 让我们重命名空上执行到一个执行路径。 然后我们可以引入新的概念叫流程执行 (process execution)。流程执行代表一个完整的流程执行并且包含多个执行路径。 这个执行路径可以被分层排序。 那也就意味着当新的流程执行被实例化时一个根执行路 径被创建。当一个根执行路径分叉进入多个并发执行路径时,这个根是父亲,新创建的执行 路径称为这个根的孩子。这样的话,结合实现可以变成直接的:这个结合实现就不得不去检 查所有的兄弟执行路径(sibling-execution-paths)已经处在结合节点了。如果那样的话,这 个父执行路径可以恢复执行离开结合节点。 当层级执行路径和基于兄弟执行路径的结合实现覆盖了用例的大部分时,其他的并发行 为在这种情形下也许是合适的。例如当多个合并关联到一个分裂时。在这样的形式下,其他 的运行时数据的合并以了合并实现是必须的。 :ProcessExecution Root: ExecutionPath 开始状态:开始销售 billing: shipping: ExecutionPath ExecutionPath 记账 运货 送帐单:任务 取配额:任务 等款:任务 选运货商:任务 图 4-12 并发路径执行 执行的多并发路经常与多线程编程混淆。尤其在工作流和BPM的上下文中,这些是完 全不同的。流程指状态机。考虑状态机总是在一个稳定状态和瞬间的状态转换中。这样你就 可以通过查看导致状态转换的事件来解析执行的并发路径。 并发执行的意思是被处理的事件 和并发执行路径两者间是无关的。 现在让我们假设在流程执行中的状态转换和数据库转换是 相关的(也在4.3.5 持久化和事件节被解释了) 那么你将发现多线程程序设计是完全不需要 , 支持执行的并发路径的。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 49 页 / 共 199 页
  • 50. 4.3.3. 流程组成 流程组成是去包含作为超流程(super process)部分的子流程(sub process)的这个能 力。这个高级的属性使得增加一个抽象到流程模型成为可能。对于事务分析,这个属性处理 在较小的板块里中止大模型是重要的。 这个主旨思想是超流程有在图中有一个代表完整子流程执行的节点。 当执行进入超流程 的子流程节点(sub-process-node)时,将要考虑几个事情:  首先,为子流程创建新执行  可选的,一些存储在超流程中的流程变量信息能够从超流程执行注入子流程执行。 最容易的形式是子流程配置一系列的用来把变量从超流程复制到子流程的变量。  子流程的开始节点(start-node)应该只有一个离开转换。支持多离开转换的流程语 言必须有一个选择那些基于超流程的流程变量的转换中的一个转换的机制。  子流程执行通过发送一个响应开始状态默认转换的事件被启动。 在子流程进入等待状态后,超流程执行将指示子流程节点和子流程执行在一些等待状 态。 当子流程执行完成时,超流程执行能够继续。下列方面那时是需要被考虑的:  流程变量信息可能需要从子流程执行中复制回超流程执行。  超流程执行将要继续。典型地,流程语言只允许在子流程节点上有一个离开转换。 那样的话超流程执行被传播通过那个默认的离开转换。  如果子流程节点被允许多于一个的离开转换,一个机制将被引入到选择离开转换 中。这个选择能够基于子流程执行的变量也可以是子流程的结束状态(典型状态机 能有多个结束状态)。 WS-BPEL 有一个子流程处理的隐性概念,要是显式的就好了。调用将开始新的子流程。 然后超流程将有一个直到子流程结束的等待的接收活动。 所以正常的调用和接收被特殊的活 动取代了。 4.3.4. 异步连续 以上的,我们看到了去异步执行流程的默认行为直到有一个等待状态。典型地这全部的 状态变换被打包成一个转换。在这部分里,你将看到如何在流程语言里划分转换边界。异步 连续意思是流程可以异步地继续。这就意味着第一个转换将发送一个消息。那个消息代表一 个连续命令。然后这个消息接收器执行第二个转换命令。然后这个流程继续它的自动执行, 但是它分开了两个转换。 为了增加异步连续到面向图的程序设计, 需要一个消息系统, 这样的一个集成了你的程 序 设 计 逻 辑的 系 统 允 许消 息 事 务 发送 和 接 收 。消 息 系 统 也被 叫 做 面 向消 息 的 中 间件 (message oriented middleware, MOM) 且 Java 消息服务 而 (Java Message Service , JMS) cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 50 页 / 共 199 页
  • 51. 是用在这个系统上的标准 API。 有三个执行能够被异步地继续的地方:  在节点的 execute 方法前。即进入节点后。  当执行将要通过一个转换进行传播时。即在离开节点前。  每个动作里也可以被异步的执行。 让我们详细地考虑下第一种情形当他被显示在下列图形中。假设某些事件导致一个执行 通过图开始传播而且在“ generatePdf ”节点转换将要调用 execute 方法。取代直接地在 “generatePdf”节点上的 execute 方法调用,新的消息命令正在使用一个指针到创建到执行。 命令消息应该被解析为作为“通过执行节点继续这个执行” 。这个消息通过消息队列被发送 到命令执行器。 这个命令执行器从消息执行器取得消息并以这个执行作为参数来调用节点的 execute 方法。 图 4-13 异步连续 注意现在两个独立的事务被涉及了。 从初始事件发起的一个事务。那个事务包含了移动 这个执行进入“generatePdf”节点并发送命令消息。在第二个事务中,命令消息被消费掉并 且节点的 execute 方法以执行为参数被调用。在这两个事务之间,执行应该被到来的事件阻 塞。 4.3.5. 持久化和事务 流程定义信息(像节点、转换和动作)和执行信息(像执行)两者都可以被存储在关系 数据库中。ORM 解决方案(像 Hibernate/EJB3)可以用来做映射在数据库记录和 OOP 对象 之间。 所有流程定义信息是静态的。因此它可以被缓存在内存中。这给了一系列的性能提高。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 51 页 / 共 199 页
  • 52. 不过每个转换中的运行时执行数据将不得不从数据库中被加载。 转换通常相当于执行上的事件方法。当事件正在被处理的时候转换开始。 这个事件方法 将触发执行继续直到新的等待状态到达。当那个发生时,执行的事件方法返回并且转换结束。 这整个事件方法调用的变换就是执行已经被移动了, 它的节点指针从一个节点到另一个 节点。这个 ORM 解决可以计算出原始数据库状态和被更新的 java 对象之间的差异。然后那 些变化将在执行事件方法的结束点被刷到数据库中。 在我们的例子中这是一个执行上的 SQL 更新(update)语句,设置节点指针到新(等待状态)节点。 类似 hibernate/EJB3 的 ORM 解决方案中,它们在每个 session 中同不同的对象集一起工 作。这就意味着所有访问 Node 实现是被序列化了的并且只要代码使用执行数据(不是静态 变量、实例)的话就移除写线程安全的代码的必要性。 4.3.6. 服务和环境 节点可能想利用可插拨的服务或新的节点实现可能想使用新服务,这还是个未知数在设 计时。为了适应这方面,一个服务框架应该被增加到面向图的程序设计中,以至于节点可以 访问何意的服务和配置。 基本上,有两方面:  向下传递执行上下文对象(上面提到的包裹了执行对象传递被传递)  线程本地执行上下文 执行上下文包含通过“环境”来访问服务是可用的。这个环境是客户端代码(代码调用 Execution.event(String)加上一个可选的客户端代码的运行容器)。 服务的例子是定时器服务、异步的消息服务和数据库服务(java.sql.Connection)等等。 4.4. 注意事项 4.4.1. 运行时数据隔离 面向图的程序设计清晰地从运行时数据中(执行)中分离了定义数据(节点、转换和动 作)。 所以代替了进入节点的传播执行,任何节点实现可以重新安排代表执行的整个运行时数 据。这就创造了许多为实现不同形式的 fork.split 和结合(join/merge)行为的灵活性。 此外,定义信息是静态的而且从不变化。这个对于各种各样的执行优化是重要的。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 52 页 / 共 199 页
  • 53. 4.4.2. GOP 与其他技术相比 在这部分我们描述下面向图的程序和其它的实现技术如何被用于基于图的执行语言。 在基于面向消息的中间件(MOM)的执行引擎中,执行通过沿着消息队列传递的消息 来表示的。每一个流程图中的节点是通过系统中的消息队列来表示的。实际上,面向图的程 序设计是基于 MOM 的执行的超集。 在面向图的程序设计(GOP)中,缺省的,从一个等待状态到另一个等待状态移动执行 的计算是被同步完成的。在这份文件的后部分, 我们将提到用来解释 MOM 怎样才能够被用 来在流程同步中做一步的异步连续扩展。 所以基于 MOM 的执行在所有节点被异步地执行方 面同面向图的程序设计是相似的。 另一种被用来实现工作流、BPM 和编排系统的技术是代码生成。在那个方案中,基于 图的流程被翻译成命令式的编程逻辑(如 java) 。这个被生成的编程逻辑对于外部的触发器 有一个方法,它可以在等待状态后被给出。那些方法将计算转换到一个新的等待状态。这一 技术是在流程版本能力和实践中被限制的, 因为代码生成已经被证明在软件开发流程中是不 实用和具有瓶颈的。 4.4.3. GOP 与 Petri 网比较 学术界,长时间以来,对于工作流和业务流程建模一直集中在 petri 网上,这主要是因 为 petri 网是支持执行的并发路径唯一的精确定义的模型。由于数学基础的原因,许多有趣 的验证和完整性算法可以被定义。 在 petri 网和 GOP 间最大的差异就是他们的本质。Petri 网是一数学模型,而 GOP 是一 个实现技术或设计模式。 GOP 可以用来实现 petri 网。Petri 网位置(places)和 petri 网转换可以作为两个不同的 节点类型来实现。Petri 网弧(arcs)相当于 GOP 的转换。一个 petri 网令牌(token)相当于 GOP 执行。 已经被定义在 petri 网上较高层的扩展(extensions)也可以用 GOP 的术语进行定义。 GOP 自身并不支持分析算法当他们被定义在 petri 网上时。那是因为 GOP 没有一个具 体的解释。分析算法只能定义在具有确定设计时解释的模型上。GOP 在另一方面也支持具 有非确定设计时解释节点。GOP 节点实现可以潜在的去做任何运行时计算类型和决定,以 及执行如何被传播。 分析算法只能被定义在具体的流程语言上, 因为这个节点实现让确定设 计时解释成这个节点类型。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 53 页 / 共 199 页
  • 54. 4.5. 应用领域 4.5.1. 业务流程管理 (BPM) 4.5.1.1. BPM 不同方面 BPM 的目标是让一个组织能够更高效的运行。第一步是分析和描述出组织是如何完成 工作的。让我们定义一个业务流程即人和系统一起工作来完成一个特定的工作的方法的描 述。一旦业务流程被描述了搜索优化就可以开始了。 有时业务流程已经有组织地被演化并且只是看这整个的业务流程显示某些明显的无效 率 。 查 找 出 使 这 些 业 务 流 程 更 加 高 效 的 修 正 称 为 业 务 流 程 重 建 ( Business Process Reengineering ,BPR)。一旦流程大部分是自动地、统计和审核追踪能帮助查找并标识出这些 无效性。 另一种提高效率的方式能够使用信息技术去自动化业务流程整体或部分。 自动化和修改业务流程是使组织更加高效地运行的最通用方式。总之重要的是注意那些 是业务流程管理的部分内容。 经理不断地被他们的组员打断工作,进入被执行的步骤。 例如一个软件开发经理组织一 个团队建设事件。假使那样的话,业务流程的描述可能只在经理的前面完成。其他的情形像 一个大保险公司处理一桩保险索赔而需要一个更正式的 BPM 方案。 总收入能够从管理业务流程并改进一定数量的业务流程执行时间效率中获得。管理业务 流程正式化的成本是花费在分析、描述、改进和自动化业务流程上的额外成果。所以当决定 哪一个流程将被选出来进行正规化管理及自动化时这些成本就不得不被考虑。这个解释就是 要集中在高复发率的过程上。 4.5.1.2. BPM 系统的目标 BPM 系统的主要目标是促进商务流程的自动化。在为业务流程构建软件中,两个角色 能够被区别:业务流程分析人员和开发人员,在小点的团队里,这两个角色当然可以是一个 人。业务分析人员研究、描述业务流程并指出软件需求,而开发人员创建可执行的软件。 传统 BPM 套件试着开始从业务分析模型并且按他们的方式向下完成可执行软件。他们 试着最小化技术技能需求, 以至于业务分析人员能够生产出可执行软件。所有这一切不可避 免的是以图为中心的,技术细节扰动着分析人员的世界。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 54 页 / 共 199 页
  • 55. 图 4-14 传统的 BPM 方案 在我们的视野里,中心思想是业务分析人员和开发人员使用通用的语言进行沟通在流程 的图形视图的帮助下。 技术技能将总是必须的当开发软件。业务分析人员负责图形化视图并且不应该去被迫处 理流程的技术面。 没有那些技术面流程将不会完全的定义而且以后它是不可执行的。开发人员负责技术实 现面。技术面不应该要求图的改变。 图 4-15 改进的 BPM 方案 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 55 页 / 共 199 页
  • 56. 4.5.2. 服务编排 最多的被公认服务编排语言的名称 BPEL。服务编排常在企业服务总线(Enterprise Service Bus,ESB)的上下文中看到。ESB 是企业级的中央通信主干。它集成了许多的相异的 系统而且基于 XML 技术。 假设在你的 ESB 上你有 A、B 和 C 三个服务。服务编排是用来写作为现有服务的一个 函数的一个新的服务的基于图的执行语言。例如使用编排脚本,一个新服务 D 能够作为服 务 A、B 和 C 的函数而被写。 服务编排引擎 例如:BPEL <svcD> <callSvcA /> <callSvcB /> <callSvcC /> </svcD> 企业服务总线(Enterprise Service Bus,ESB) <XML /> <svcA /> <svcA /> <svcA /> 图 4-16 服务 4.6. 基于嵌入图的语言 当 BPM 引擎能够被完全地整合到一个软件开发项目中、甚至 BPM 引擎的数据表也被 整合到项目的数据库中时,然后我们提到可嵌入的 BPM 引擎。那就是我们用 GOP 瞄准的 这个目标:一个基于图的语言实现的通用基础。 4.7. 市场 4.7.1. 终极流程语言 传统上, 厂商正在找终极的流程语言。这个方案是用来指定流程语言作为一系列的构成 (construct)。每个构成有一个图形表示和一个运行时行为。换句话说,在流程图中每个构 成是一个节点类型。这样流程语言只是一系列的节点构成。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 56 页 / 共 199 页
  • 57. 这个思想是厂商正在找最好的流程构成去形成一个统一的可应用流程语言。这个想法今 天还是发现了许多而且我们称它为终极流程语言。 我们相信这一焦点不放在试着去找终极流程语言,而是去找在不同的场景和不同的环境 中都能够被用来作为流程语言根基的通用基础。我们提出的 GOP 下一步将被看作这样的一 个基础。 4.7.2. 相关信息 当前工作流景色,BPM 和编排解决方案完全地分立的。这部分我们将描述下两个分裂 的维度。第一个维度叫 BPM 产品线(BPM product continuum) ,它被展示在下一幅图片中。 这个术语最初是被 Derek Miers 和 Paul Harmon 在《The 2005 BPM Suites Report》中引入的。 在左边,你可以看到编程语言。连续体的这边被瞄准在 IT 开发人员。编程语言是最灵 活的而且它完整地集成了为特定的项目而开发的其他软件。它带来了相当多的程序去实现业 务流程。 右边,有一个 BPM 套件。这个套件是完整的被用来瞄准用于业务分析软件开发环境。 软件围绕业务流程被开发。无须编程不得不被做用来在 BPM 套件里创建可执行的软件。 编程 库和 BPM 语言 工具 套件 图 4-16 BPM 产品线 4.7.3. 其他实现技术 基于消息队列技术。 基于代码生成技术。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 57 页 / 共 199 页
  • 58. 第 5 章 部署 jPDL 是可嵌入式的 BPM 引擎,意味着你可以使用并嵌入 jPDL 库到你的 java 项目中, 而不用再安装一个独立的产品然后再同它进行集成。 它最关键的一个方面就是尽可能的最小 化依赖性。本章将讨论 jbpm 库和他们的依赖关系。 5.1. jBPM 库 jbpm-jpdl.jar 是 jpdl 功能的核心库。 jbpm-identity.jar是一个包含了身份组件的可选库,身份组件将在12.11 身份组件节中描述。 5.2. Java 运行时环境 jBPM 3 要求运行在 J2SE 1.4.2 版本以上。 5.3. 第三方库 所有库都可以在 lib 目录中找到,这些库也许是依赖性的。 在一个最小的部署中,你可以创建并运行只放 commons-logging 和 dom4j 库到你的 classpath 路径中就可以用 jBPM。注意那些需要持久化到一个数据库中的流程是不能被支持 的。如果你不使用流程 xml 解析的话 dom4j 库可以被移除,但取而代之的是要用程序来构 建对象图。 (译者注:将流程的 xml 文件以字符串的方式写在程序中) 库 使用 描述 jBPM代码通用日志记录。通用日志库可以被配置 来调度日志(如java 1.4 的logging、login4j等 ) 等 , 参考apache commons 用户手册 来查找更多的关 commons-logg Jbpm 日志和 hibernate 于如何配置commons logging的信息。如果你使用 ing.jar log4j,最容易的方法是将log4j的lib库和 log4j.properties放在你的类路径里。commons logging将自动检测并使用这些配置。 流程定义和持久 dom4j.jar 解析 XML hibernate 化 表 5-1 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 58 页 / 共 199 页
  • 59. 典型的 jBPM 部署将包含流程定义的持久化存储和流程执行。那样的话,jBPM 没有任 何的 hibernate 以外的依赖和相关库文件。 当然,hibernate 所要求的库相关环境和结属性你还是要用到的。详细的请参考 hibernate 的文档。下一个表显示了一个独立的纯 POJO 部署环境。 库 使用 描述 hibernate3.jar Hibernate 持久性 最好的 O/R 映射工具 antlr-2.7.6rc1.jar Hibernate 持久性查 解析器(parser)库 询解析所使用 cglib.jar Hibernate 持久性 Hibernate 代理( proxies)用的反射库 commons-collections.jar Hibernate 持久性 asm.jar Hibernate 持久性 Asm 字节代码核心库 表 5-2 beanshell 库是可选的。如果你包含它,你就不能在 jBPM 流程语言中使用 beanshell 集 成,而且你将得到一个日志消息说 jbpm 现在和以后都不能加载 Script 类,脚本元素将不再 可用。 库 使用 描述 只在脚本和决策中使用。当你不用这些流程元素 时,beanshell 库可以被移除,但是然后你不得不在 bsh.jar beanshell 脚本分析器 hibernate.cfg.xml 文件中写出 Script.hbm.xml 的映射 行。 表 5-3 5.4. Web 应用 在下载包的 deploy 目录中,你能够找到 jbpm-console.war 文件。它就是包含了在 JBoss 上运行这个 web 应用的 jPDL 库、配置文件和所需的库。 这个 war 文件根本就没有包含 hibernate 库。那是因为 JBoss 已经包含了 hibernate 库。 要是想在其他的服务器(像 Tomcat)上运行这个 web 应用,你不得不将 hibernate 的库放在 你的 war 文件的 WEB-INF/lib 目录中。最简单的方法是使用这个目录中的 ant 构建脚本。 此外,这个 war 文件可以给你一个好的指导,即如何在自己的 web 应用中部署 jbpm 库 和配置文件。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 59 页 / 共 199 页
  • 60. 在 这 个 web 应 用 的 web.xml 文 件 里 , JobExecutorServlet 被 配 置 完 了 。 这 样 当 jbpm-console.war 包被部署时将开始 JobExecutor,它是标准 java 平台上执行定时器和异步消 息的基础。 5.5. 企业包 在下载包的 deploy 目录中,你能够找到 jbpm-enterprise.ear 文件。它就是 J2EE 1.4 兼容 企业包,它包括:jPDL 库、jPDL 配置文件、jPDL web 控制台和几个 EJB(enterprise bean) 。 在这个包里,jBPM 被配置好了的使用在应用服务器(像 JBoss) 。异步消息服务一定是 JMS 和 EJB 定时服务的调度服务。 所以这个.ear 文件没有开始 JobExecutor,持久性服务是被配置到整个 JTA 事务中去了。 在企业包中是下列文件:  jbpm-console.war:web 控制台应用,适用于 J2EE 集成  jbpm-enterprise.jar:企业 bean 支持  lib/jbpm-configs.jar:配置文件  lib/jbpm-identity.jar:身份组件类  lib/jbpm-jpdl.jar:标准 jPDL 类  meta-inf/application.xml:J2EE 应用描述符 jbpm-enterprise.jar 包含下列 EJB 组件:  CommandServiceBean  CommandListenerBean  JobListenerBean  TimerEntityBean 这些bean是兼容于J2EE 1.4 / EJB 2.1,允许部署在各种应用服务器上。注意然而那个 jBPM却只能提供JBoss应用服务器的部署描述符。所有的bean被部署都有事务属性的需求。 EJB类的源代码和接口规范是在src/enterprise目录和javadoc所在的doc/javadoc-enterprise目录 中。详细的描述可以在9.1 企业Bean 中找到。 jbpm-configs.jar 包含下列文件:  jbpm.cfg.xml:jBPM 配置  hibernate.cfg.xml:Hibernate 配置  jbpm.mail.templates.xml:e-mail 模板 在9.2 jBPM企业配置和9.3 Hibernate 企业配置节可以查看前两个文件中的企业特定设 置。最后的文件在17.4 邮件模板节也被描述了。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 60 页 / 共 199 页
  • 61. 5.6. jPDL 运行时和套件 5.6.1. 运行时 jPDL 运行时是你开始 jPDL 的一切:jpdl 库、第三方库、示例和文档。它不包含图形设 计器和 web 控制台工具,这两个被放在了套件包中了。 目录 内容 config 包含所有的配置文件。注意为了容易测试和开发,当前的 hibernate 配置点 是一个 jbpm 内存数据库 db 包含了在你的 DB 中创建 jPDL 表的脚本。 它包括了成立于数据库兼容性的 wiki 页的副本 doc 包含了 jpdl 和身份(identity)组件源代码的用户开发指南和 javadocs 文档 examples 每个示例都是一个独立可以使用 ant 或 eclipse 来运行的项目 lib 所有第三方库和他们的协议 src jpdl 和 身份组件的源代码目录. 表 5-4 jPDL 运行时目录 5.6.2. 套件 jPDL 套件是一个带有两个工具的 jPDL 运行时的扩展:eclipse 的图形设计器插件和带 有预配置的部署了 jPDL 运行时和 web 控制台应用的 JBoss 服务器。包括的工具全都是预配 置的可以恰好一起工作的缺省配置(out of the box)。 目录 内容 这个设计器(designer)是允许以图形流程编辑 jPDL 流程文件的 eclipse 插 designer 件。查看 designer 目录下的 readme.html 文件以得到更多的安装设计器的指 导 server 真正的 JBoss 应用服务器,其上部署了 jPDL 运行时和 jPDL web 控制台 表 5-5 jPDL 套件附加目录 5.6.3. 在套件服务器上配置日志 如果你想这套服务器上查看 debug 日志,更新 jpdl-suite-home/server/server/jbpm/config/log4j.xml 文件,请查找: <!-- ============================== --> <!-- Append messages to the console --> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 61 页 / 共 199 页
  • 62. <!-- ============================== --> <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Target" value="System.out"/> <param name="Threshold" value="INFO"/> 并用参数 param Threshold,改变 INFO 为 DEBUG。 然后你就可以得到所有组件的 debug 日志。为限制 debug 日志的数量,进一步地看下面 的文件直到你看到“Limit categories”,你也许想加一个 tresholds 为指定的包,如: <category name="org.hibernate"> <priority value="INFO"/> </category> <category name="org.jboss"> <priority value="INFO"/> </category> 5.6.4. 在套件里调试流程 首先,假设你刚刚开始开发一个新的流程,是很容易的去使用纯JUnit测试并且在内存 中运行流程,像第 3 章 教程中解释的那样。 但是如果你想在控制台中运行流程并调试(debug)它的话,这里有两步你需要做: 1) 在 jpdl-suite-home/server/server/bin/run.bat 文件中,结尾的某个地方,有一行像: rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS% cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 62 页 / 共 199 页
  • 63. 因为备份的原因,等把那行做个拷贝,然后移除第一个“rem”并改变 suspend=y 为 suspend=n。然后得到下列这样的: rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS% set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n %JAVA_OPTS% 2) 用你的 IDE 连接到远端的 java 应用上,使用 localhost 端口是 8787。然后你可以增加 一个断点并在控制台中运行流程直到遇到断点。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 63 页 / 共 199 页
  • 64. 第 6 章 配置 最简单的配置 jBPM 方式的通过将 jbpm.cfg.xml 配置文件放在类路径的根下。如果那个 文件没有作为一个资源被发现的话,包含在 jbpm 库中的默认的最小配置将被使用。注意那 个最小的配置不会有任何的持久化的配置。 jBPM 配 置 通 过 org.jbpm.JbpmConfiguration 这 一 java 类 来 表 示 。 最 简 单 的 获 得 JbpmConfiguration 的方法是使用单态实例方法 JbpmConfiguration.getInstance()。 如果你想加载一个配置从另一个源的话,你能够使用 JbpmConfiguration.parseXxxx 这个 方法。 static JbpmConfinguration jbpmConfiguration = JbpmConfinguration.getInstance(); JbpmConfiguration 是线程安全的而且此后能够被保存在一个静态成员里。 所有线程都可 以把 JbpmConfiguration 当作 JbpmContext 对象的工厂来使用。一个 JbpmContext 一般代表一 个转换。在一个上下文块里 JbpmContext 让服务变得可用。一个上下文块像这样: JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // 这是我们调用的上下文块 // 这里你可以招生工作流操作 } finally { jbpmContext.close(); } JbpmContext 使得 jBPM 的一系列服务和配置可用。这些服务是在 jbpm.cfg.xml 配置文 件中被配置的并使它尽可能地运行在任何的 Java 环境里,在那个环境里无论什么使用服务 都是可以。 这有一个典型的 JbpmContext 的配置,你可以在 src/config.files/jbpm.cfg.xml 文件中找到 它。 <jbpm-configuration> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 64 页 / 共 199 页
  • 65. <jbpm-context> <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' /> <service name='message' factory='org.jbpm.msg.db.DbMessageServiceFactory' /> <service name='scheduler' factory='org.jbpm.scheduler.db.DbSchedulerServiceFactory' /> <service name='logging' factory='org.jbpm.logging.db.DbLoggingServiceFactory' /> <service name='authentication' factory='org.jbpm.security.authentication.DefaultAuthenticationServiceFactory' /> </jbpm-context> <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar --> <string name='resource.hibernate.cfg.xml' value='hibernate.cfg.xml' /> <!-- <string name='resource.hibernate.properties' value='hibernate.properties' /> --> <string name='resource.business.calendar' value='org/jbpm/calendar/jbpm.business.calendar.properties' /> <string name='resource.default.modules' value='org/jbpm/graph/def/jbpm.default.modules.properties' /> <string name='resource.converter' value='org/jbpm/db/hibernate/jbpm.converter.properties' /> <string name='resource.action.types' value='org/jbpm/graph/action/action.types.xml' /> <string name='resource.node.types' value='org/jbpm/graph/node/node.types.xml' /> <string name='resource.parsers' value='org/jbpm/jpdl/par/jbpm.parsers.xml' /> <string name='resource.varmapping' value='org/jbpm/context/exe/jbpm.varmapping.xml' /> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 65 页 / 共 199 页
  • 66. <int name='jbpm.byte.block.size' value="1024" singleton="true" /> <bean name='jbpm.task.instance.factory' class='org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl' singleton='true' /> <bean name='jbpm.variable.resolver' class='org.jbpm.jpdl.el.impl.JbpmVariableResolver' singleton='true' /> </jbpm-configuration> 在这个配置文件中你能够看到 3 个部分:  第一部分用一系列的服务实现配置 jbpm 上下文。可能的配置选项被提及在特定的 服务实现中提到了。  第二部分是所有的持久性映射的配置资源。 这些资源引用可以被更新如果你想定制 这些资源中的一个文件的话。通常,你可以生成一个在 jbpm-3.x.jar 中的缺少配置 的一个拷贝然后把它放在你的类路径上的某处。 然后你更新这个文件中的引用那么 jbpm 将使用你定制的配置文件。  第三部分是一些用于 jbpm 的零散配置。这些配置选项在特定主题的章节中被描述。 缺省的配置一系列的服务瞄准在一个简单的 webapp 环境并最小化其依赖性。持久 化服务将获得一个 jdbc 连接而且所有的其他的服务将使用相同的连接去执行他们的服 务。所以你的工作流的所有操作都是集中在 JDBC 连接上的一个事务中而不需要一个事 务管理器。 JbpmContext 为大多数的通用流程操作内置了方便的方法: public void deployProcessDefinition(ProcessDefinition processDefinition) {...} public List getTaskList() {...} public List getTaskList(String actorId) {...} public List getGroupTaskList(List actorIds) {...} public TaskInstance loadTaskInstance(long taskInstanceId) {...} public TaskInstance loadTaskInstanceForUpdate(long taskInstanceId) {...} public Token loadToken(long tokenId) {...} cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 66 页 / 共 199 页
  • 67. public Token loadTokenForUpdate(long tokenId) {...} public ProcessInstance loadProcessInstance(long processInstanceId) {...} public ProcessInstance loadProcessInstanceForUpdate(long processInstanceId) {...} public ProcessInstance newProcessInstance(String processDefinitionName) {...} public void save(ProcessInstance processInstance) {...} public void save(Token token) {...} public void save(TaskInstance taskInstance) {...} public void setRollbackOnly() {...} 注意 XxxForUpdate 这样的方法将注册在被加载的对象上为了自动保存,那样你就不得 不明确的调用一个保存方法。 指定多个 jbpm 上下文中可能的,但是那样的话你就不得不确保每个 jbpm 上下文赋予 了唯一的命名(name)属性。被命名的上下文能够被 JbpmConfiguration.createContext(String name)方法检索。 服务元素指定服务的名称和那个服务的服务工厂。这个服务将只在它使用 JbpmContext.getServices().getService(String name)方法进行请求时被创建。 这个工厂也能被指定作当一个元素代替一个属性时。 那样也许必须要注入一些配置信息到这 个工厂对象里。这个组件负责解析 XML,创建并写被工厂对象调用的这些对象。 6.1. 配置工厂 当定制工厂时一个共同的错误是弄混长短标记(notation)。短标记的例子能在缺省配置 文件和上面的内容中看到,例如: ... <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' /> 如果在服务上的特定属性要被指定的话,短注释不能使用,代替的是,就不得不用长标 记了,像这样: <service name="persistence"> <factory> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 67 页 / 共 199 页
  • 68. <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory"> <field name="dataSourceJndiName"><string value="java:/myDataSource"/></field> <field name="isCurrentSessionEnabled"><true /></field> <field name="isTransactionEnabled"><false /></field> </bean> </factory> </service> 6.2. 配置属性 jbpm.byte.block.size:文件附件和二进制变量被存储在数据库中。不是用 blobs,而是一 列固定大小的二进制对象。 这样做是为了提高在不同的数据库间的兼容性和 jBPM 的整体的 嵌入性。这个参数控制着固定长度的数据块的尺寸。 jbpm.task.instance.factory:定制任务实例创建的方式,指定一个完全限定类名在这个属 性中。当你想定制TaskInstance bean并给它增加新属性时这个属性你也许是需要的。参 考:12.10 定制任务实例 节。那个特定的类org.jbpm.taskmgmt.TaskInstanceFactory应该被实 现。 jbpm.variable.resolver:定制 jBPM 在类 JSF(JSF-like)的表达式里查找第一个术语的的 方式。 6.3. 其他的配置文件 这有 jBPM 可定制的所有配置文件的一个简短描述。 6.3.1. Hibernate cfg xml 文件 这个文件包含了 hibernate 映射资源文件的配置和引用。 位置:除非在 jbpm.properties 文件中指定在 jbpm.hibernate.cfg.xml 属性。否则 jbpm 项 目中缺省的 hibernate cfg xml 文件放在 src/config.files/hibernate.cfg.xml 处。 6.3.2. Hibernate 查询配置文件 这个文件包含 hibernate 的查询被使用在 jBPM 的 session 类 org.jbpm.db.*Session 中。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 68 页 / 共 199 页
  • 69. 位置:org/jbpm/db/hibernate.queries.hbm.xml 6.3.3. 节点类型配置文件 这个文件包含了节点元素到节点实现类的 XML 映射。 位置:org/jbpm/graph/node/node.types.xml 6.3.4. 动作类型配置文件 这个文件包含动作元素到动作实现类的 XML 映射。 位置:org/jbpm/graph/action/action.types.xm 6.3.5. 业务日历配置文件 包含工作时间和空闲时间的定义。 位置:org/jbpm/calendar/jbpm.business.calendar.properties 6.3.6. 变量映射配置文件 指定如何将流程变量(java object)的值转换为在数据库中存储的变量实例。 位置:org/jbpm/context/exe/jbpm.varmapping.xml 6.3.7. 转换器配置文件 指 定 id 到 类 名 ( id-to-classname ) 的 映 射 。 这 个 id 被 存 储 在 数 据 库 中 。 org.jbpm.db.hibernate.ConverterEnumType 用来映射 id 到单例对象。 位置:org/jbpm/db/hibernate/jbpm.converter.properties 6.3.8. 缺省模型配置文件 指定哪个模块缺省的被加到新的流程定义(ProcessDefinition)中。 位置:org/jbpm/graph/def/jbpm.default.modules.properties cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 69 页 / 共 199 页
  • 70. 6.3.9. 流程包解析配置文件 指定流程包分析的阶段。 位置:org/jbpm/jpdl/par/jbpm.parsers.xm 6.4. 在 JBoss 中的 jBPM 调试日志 当在 JBoss 中运行 jPDL 而且你想查看 jBPM 的调试日志时,用你的 jPDL 发布包中的 deploy/log4j.xml 文件替换 jboss 服务器配置中的 conf/log4j.xml 文件。在套件中,完整的文 件位置是[jpdl.home]/server/server/jbpm/conf/log4j.xml。 6.5. 乐观并发异常日志 当运行在一个集群时,jBPM 在数据库上同步。缺省的是乐观锁。这就意味着每个操作 在一个事务中被执行。 而且如果在冲突的结束点被检测到的话, 那么这个事务将回滚而且必 须被处理。例如:通过重试。所以乐观锁异常通常是正常操作的一部分。因此,缺省情况下 hibernate 抛出 org.hibernate.StateObjectStateExceptions 异常,那时候是不会用错误(error)记 录日志和进行栈追踪的,但是取代的是显示一个简单的信息消息“乐观锁失败” 。 Hibernate 自己将记录 StateObjectStateException 包含一个栈追踪。 如果你想移除这些栈追踪, 放 org.hibernate.event.def.AbstractFlushingEventListener 的级别为 FATAL。如果你用 log4j 的 话下列的配置行能够被用到: log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=FATAL 如果你想允许记录 jBPM 的栈追踪的话,增加下列行到你的 jbpm.cfg.xml 中: <boolean name="jbpm.hide.stale.object.exceptions" value="false" /> 6.6. 对象工厂 对象工厂能够创建对象通过珍上类 bean(bean-like)的 xml 配置文件。这一配置文件指 定了对象应该如何被创建、 配置及绑定在一起形成一个完整的对象图。 对象工厂能够注入配 置和其他的 bean 到一个 bean 中。 在它最简单的形式里,对象工厂是能够用这样一个配置来创建基本类型和 java bean 的: <beans> <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance"/> <string name="greeting">hello world</string> <int name="answer">42</int> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 70 页 / 共 199 页
  • 71. <boolean name="javaisold">true</boolean> <float name="percentage">10.2</float> <double name="salary">100000000.32</double> <char name="java">j</char> <null name="dusttodust" /> </beans> --------------------------------------------------------- ObjectFactory of = ObjectFactory.parseXmlFromAbove(); assertEquals(TaskInstance.class, of.getNewObject("task").getClass()); assertEquals("hello world", of.getNewObject("greeting")); assertEquals(new Integer(42), of.getNewObject("answer")); assertEquals(Boolean.TRUE, of.getNewObject("javaisold")); assertEquals(new Float(10.2), of.getNewObject("percentage")); assertEquals(new Double(100000000.32), of.getNewObject("salary")); assertEquals(new Character('j'), of.getNewObject("java")); assertNull(of.getNewObject("dusttodust")); 你也能配置列表: <beans> <list name="numbers"> <string>one</string> <string>two</string> <string>three</string> </list> </beans> 和 map: <beans> <map name="numbers"> <entry><key><int>1</int></key><value><string>one</string></value></entry> <entry><key><int>2</int></key><value><string>two</string></value></entry> <entry><key><int>3</int></key><value><string>three</string></value></entry> </map> </beans> Beans 可以被配置为使用直接域注入而且是通过 setter 属性。 <beans> <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" > <field name="name"><string>do dishes</string></field> <property name="actorId"><string>theotherguy</string></property> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 71 页 / 共 199 页
  • 72. </bean> </beans> Beans 能够被引用。这引用对象不是必须是 bean 的,它还可以是字符串、整数或是任何的 其他的对象。 <beans> <bean name="a" class="org.jbpm.A" /> <ref name="b" bean="a" /> </beans> Beans 能够用构造器进行构造。 <beans> <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" > <constructor> <parameter class="java.lang.String"> <string>do dishes</string> </parameter> <parameter class="java.lang.String"> <string>theotherguy</string> </parameter> </constructor> </bean> </beans> ... 或者是 bean 的工厂方法... <beans> <bean name="taskFactory" class="org.jbpm.UnexistingTaskInstanceFactory" cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 72 页 / 共 199 页
  • 73. singleton="true"/> <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" > <constructor factory="taskFactory" method="createTask" > <parameter class="java.lang.String"> <string>do dishes</string> </parameter> <parameter class="java.lang.String"> <string>theotherguy</string> </parameter> </constructor> </bean> </beans> ... 或者是类的静态工厂方法... <beans> <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" > <constructor factory-class="org.jbpm.UnexistingTaskInstanceFactory" method="createTask" > <parameter class="java.lang.String"> <string>do dishes</string> </parameter> <parameter class="java.lang.String"> <string>theotherguy</string> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 73 页 / 共 199 页
  • 74. </parameter> </constructor> </bean> </beans> 每个命名对象能够使用属性 singleton=”true”标记为单态。那就意味着一个给定的对象工 厂将总是为每个请求返回同一对象。注意单态在不同的对象工厂之间是不能共享的。 单态属性导致在方法 getObject 和 getNewObject 之间的区别。典型的用户的对象工厂将 使用 getNewObject 方法。这就意味着第一个对象工厂的对象缓存在这个新对象图被构造前 就被清除了。在对象图构造期间,非单态对象被存储在对象工厂的对象缓存中,它允许共享 引用给一个对象。这个单态对象缓存同普通对象缓存是不同的。单态缓存是从不清除,而普 通对象缓存在每个 getNewObject 方法开始处会被清除。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 74 页 / 共 199 页
  • 75. 第 7 章 持久化 大多数的情况,jBPM 用于维护流程的执行跨度很长时间(long time)。在这个语境下, “很长时间”意味着跨度几个事务。 这个持久化的主要意图就是在等待状态期间存储流程执 行。所以把流程执行当作一个状态机。在一个事务里,我们移动流程执行状态机从一个状态 到另一个状态。 流程定义能够被持久化以 3 种不同的形式:xml、java 对象和 jBPM 数据库记录。执行 (即运行时)信息和日志信息可以表示成 2 种形式:java 对象和 jBPM 数据库记录。 纯java xml jBPM对象模型 jBPM数据库 定义 定义 定义 执行 执行 日志 日志 图 7-1 转换和其不同形式 关于流程定义xml表示和流程包的更多信息,参考第 21 章 jBPM流程定义语言(JPDL)。 更多的关于如部署流程包到数据库的信息可以在21.1.1 部署流程包 节找到。 7.1. 持久化 API 7.1.1.配置框架关系 持久化 API 是一个通过在 JbpmContext 上暴露一些方便的持久化方法同配置框架进行集 成, 持久化 API 操作可以因此在 jBPM 上下文块中被调用,像这样: JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // 调用持久化操作 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 75 页 / 共 199 页
  • 76. } finally { jbpmContext.close(); } 在下文中,我们假设那个配置包括一个持久化服务跟这个(就像在例子配置文件 src/config.files/jbpm.cfg.xml)相似: <jbpm-configuration> <jbpm-context> <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' /> ... </jbpm-context> ... </jbpm-configuration> 7.1.2. JbpmContext 上的方便方法 三个最通用的持久操作:  部署流程  开始一个新的流程执行  继续一个执行 首先部署一个流程定义。典型的,这将直接被图形流程设计器或部署流程的 ant 任务完 成。但这里你可以看到程序中如何做的。 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessDefinition processDefinition = ...; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 76 页 / 共 199 页
  • 77. jbpmContext.deployProcessDefinition(processDefinition); } finally { jbpmContext.close(); } 为了创建一个新的流程执行, 我们需要指定哪一个流程定义上执行将实例化。 最常用的 方式是指定这个流程的名字并让 jBPM 在数据库中查找那个流程的最新版本: JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { String processName = ...; ProcessInstance processInstance = jbpmContext.newProcessInstance(processName); } finally { jbpmContext.close(); } 为了继续一个流程执行,我们需要从数据库中捕获流程实例、token 或 taskInstance,调 用在 POJO jBPM 对象上的一些方法然后保存这些更新使这个流程实例再次进入数据库中。 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { long processInstanceId = ...; ProcessInstance processInstance = jbpmContext.loadProcessInstance(processInstanceId); processInstance.signal(); jbpmContext.save(processInstance); } finally { cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 77 页 / 共 199 页
  • 78. jbpmContext.close(); } 注意如果你用 JbpmContext 的 xxxForUpdate 方法的话,一个明确的 jbpmContext.save 方 法调用就不再是必须的了,因为在关闭 jbpmContext 期间它将自动的调用。例如:假设我们 想通知 jBPM 关于 taskInstance 已经完成了。注意任务实例完成能够触发执行继续所以与 taskInstance 相关的 processInstance 必须被保存。最方便的方式是使用 loadTaskInstanceForUpdate 方法来做: JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { long taskInstanceId = ...; TaskInstance taskInstance = jbpmContext.loadTaskInstanceForUpdate(taskInstanceId); taskInstance.end(); } finally { jbpmContext.close(); } 正如背景信息那样,下部分是一个解释 jBPM 如何管理持久化和使用 hibernate。 JbpmConfiguration 维护一系列的服务工厂。 这些服务工厂在 jbpm.cfg.xml 文件中被配置 正如上面显示的那样,而且是懒初始化的(instantiated lazy) 。DbPersistenceServiceFactory 只在它第一次需要的时候被初始化。在那以后,服务工厂被维护在 JbpmConfiguration 里。 DbPersistenceServiceFactory 管理 hibernate 的 SessionFactory。并且当第一次请求 hibernate session 工厂时它被懒创建(creat lazy) 。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 78 页 / 共 199 页
  • 79. JbpmConfiguration (配置在jbpm.cfg.xml文件中 (所有线程使用一个) 对象工厂 服务工厂 DbPersistenceServiceFactory Hibernate SessionFactory JbpmContext(每线程使用一个) 服务 DbPersistenceService Hibernate Session 可选的 Hibernate 事务 图 7-2 持久化关联类 在 jbpmConfiguration.createJbpmContext()调用期间,只有 JbpmContext 被创建。那时没 有更多的有关持久化的初始化被做。JbpmContext 管理 DbPersistenceService,它在第一次请 求时被实例化。DbPersistenceService 管理 hibernate session。而且 hibernate session 是在 DbPersistenceService 内部被懒创建。结果,hibernate session 将只有当第一个请求持久化的 操作被调用且不会早于此请求的时候才被打开。 7.1.3. 管理事件 管理事务的大多数通常的情形是当在 JEE 应用服务器上(如 JBoss)使用 jBPM 时。大 多数通常的情形如下:  在你的应用服务器上配置一个 DataSource  配置 hibernate 连接用数据源  使用窗口管理事务  在 jBPM 中禁止事务 在 jBPM 前端的无状态的 session facade 是个好的习惯。最容易的方式如何绑定 jbpm 事 务到容器事务是要确信 hibernate 配置被 jbpm 引用的 xa-datasource 所使用。所以 jbpm 将使 用它自己的 hibernate session,那将只被 1 个 jdbc 连接和 1 个事务使用。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 79 页 / 共 199 页
  • 80. jbpm session facade 方 法 的 事 务 属 性 应 该 被 “ 要 求 ” 。 被 jbpm 使 用 的 指 定 在 hibernate.cfg.xml 中的最重要的配置属性是: hibernate.connection.datasource= --数据源 JNDI 名 — 例如: java:/DefaultDS 更多的关于如何配置hibernate的jdbc连接的信息,参考hibernate参考文档, “Hibernate provided JDBC connections”。 更多 的关于如何在jboss中配置xa_datasource的信息,参考jboss应用服务器指导, “Configuring JDBC DataSources”。 7.1.4. 注入 hibernate session 在某些情况下,你已经有了一个 hibernate session 并且你要是想合并所有的持久化工作 从 jBPM 进入那个 hibernate session 中。 那么第一件要做的事情就是确信 hibernate 配置知道所有的 jBPM 映射文件。你应该确 保引用到 src/config.files/hibernate.cfg.xml 文件里的所有 hibernate 映射文件被提供用于 hibernate 配置。 然后,你能够注入一个 hibernate session 进入 jBPM 的上下文就像下列的 API 片段中显 示的那样: JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { jbpmContext.setSession(SessionFactory.getCurrentSession()); // jbpmContext 上的 jBPM 操作 } finally { jbpmContext.close(); } 传递将在当前的 hibernate session 中通过容器到 jBPM 上下文。当一个 session 被注入到 上下文中时没有 hibernate 事务被初始化。因此这能使用缺省配置。 被传递的 hibernate session 将不会在 jbpmContext.close()方法中被关闭。这行是整个的程 序注入的精髓所在,它将下一部分被解释。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 80 页 / 共 199 页
  • 81. 7.1.5. 在程序中注入资源 jBPM 的配置提供了 jBPM 创建 hibernate session 工厂、hibernate session、 连接和 jbpm jdbc 必须服务等等的必要信息。但是所有的这些资源也能够被在程序中提供给 jBPM。正好注入 他们到 jbpmContext 中。被注入的资源总是在从 jbpm 配置信息创建资源之前被接受。 主要的哲学是用户 API 负责用户在程序中注入 jbpmContext 的所有事情。另一方面,所 有被 jBPM 打开的项目,将被它关闭。有一个例外,那就是当捕获一个被 hibernate 创建的 连接。 当调用 jbpmContext.getConnection()时, 用户使用这个中转 API (transfers)负责从 jBPM 关闭连接。 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { // 为了在他们被使用之前注入资源,你可以使用 jbpmContext.setConnection(connection); // 或 jbpmContext.setSession(session); // 或 jbpmContext.setSessionFactory(sessionFactory); } finally { jbpmContext.close(); } 7.1.6. 高级 API 使用 DbPersistenceService 维护 hibernate session 的延迟初始化。所有的数据库访问都是通过 这个 hibernate session 来完成的。所有的查询和更新都是通过 jBPM 暴露的 XxxSession 类, 像 GraphSession、SchedulderSession、LoggingSession 等等来完成的。这些 session 类使用 hibernate 的查询并且在相同的 hibernate session 下。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 81 页 / 共 199 页
  • 82. XxxxSession 类也是可以通过 JbpmContext 进行存取的。 7.2. 配置持久化服务 7.2.1. DbPersistenceServiceFactory DbPersistenceServiceFactory 自 己 有 三 个 以 上 的 配 置 属 性 : isTransactionEnabled 、 sessionFactoryJndiName 和 dataSourceJndiName。 在 jbpm.cfg.xml 文件中指定属性中的任 为了 何一个,你需要像这样把服务工厂指定为 factory 元素里的一个 bean: 重要:不要混用长短标签来配置工厂。参考:6.1 配置工厂。如果工厂只是一个类的新 实例,你可以使用工厂的属性去引用工厂元素类名。但是如果工厂属性必须被配置的话,必 须使用长标签而且factory和bean必须作为一个嵌套元素来组合使用。像这样: <jbpm-context> <service name="persistence"> <factory> <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory"> <field name="isTransactionEnabled"><false /></field> <field name="sessionFactoryJndiName"> <string value="java:/myHibSessFactJndiName" /> </field> <field name="dataSourceJndiName"> <string value="java:/myDataSourceJndiName" /> </field> </bean> </factory> </service> ... cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 82 页 / 共 199 页
  • 83. </jbpm-context>  isTransactionEnabled:缺省的,jBPM将开始一个hibernate事务当第一次捕获session 并且如果jbpmContext被关闭时,hibernate事务将被结束。然后事务被提交或者是回 滚都依赖于是否jbpmContext.setRollbackOnly被调用。isRollbackOnly属性在 TxService中维护。为了禁止并限制jBPM使用hibernate管理事务。配置 isTransactionEnabled属性为false像在上面的例子中的那样。这个属性只能控制 jbpmContext的行为。你仍然可以直接使用API来调用 DbPersistenceService.beginTransaction(),那就可以忽略isTransactionEnabled这个属性 设置。更多的关于事务的信息,参考:7.3 Hibernate事务。  sessionFactoryJndiName:缺省的,这个值是 null,意味着 session 工厂不会被 JNDI 捕获。如果设置和 session 工厂是必须的去创建一个 hibernate session 的话,那么这 个 session 工厂将从 jndi 中使用提供的 JNDI 命名被捕获。  dataSourceJndiName:缺省的,这个值是 null 并且 hibernate 将代理来创建 JDBC 连 接。通过指定数据源,jBPM 将从数据源捕获一个 JDBC 连接并在打开一个新的 session 时提供给 hibernate。为用户提供 JDBC 连接的内容请参考 ???.(原文没有注 明,估计是遍地皆有吧) 7.2.2. hibernate 会话工厂 缺省情况下,DbPersistenceServiceFactory 将使用类路径根下的资源 hibernate.cfg.xml 文 件去创建 hibernate 的对象工厂。注意 hibernate 配置文件资源是在 jbpm.hibernate.cfg.xml 中 映射。而且你可以在 jbpm.cfg.xml 中进行定制。这个缺省的配置是: <jbpm-configuration> ... <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar --> <string name='resource.hibernate.cfg.xml' value='hibernate.cfg.xml' /> <!-- <string name='resource.hibernate.properties' value='hibernate.properties' /> --> ... </jbpm-configuration> 当 这 个 属 性 resource.hibernate.properties 被 指 定 时 , 资 源 文 件 中 的 属 性 将 重 写 hibernate.cfg.xml 中的所有属性。取代更新 hibernate.cfg.xml 文件指明数据库,hibernate 属性 能够方便的用于处理 jbpm 的升级:复制 hibernate.cfg.xml 文件就可以而不用非得修改它了。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 83 页 / 共 199 页
  • 84. 7.2.3. 配置 c3po 连接池 请参考hibernate文档: http://www.hibernate.org/214.html 7.2.4. 配置 ehcache cache provider 如果你想使用jBossCache来配置jBPM的话,看下jBPM配置wiki页面。 更多的关于在hibernate中配置cache provider的信息,去看hibernate文档,二缓存部分。 jBPM 使用的 hibernate.cfg.xml 包含下列行: <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property> 这个被完成将使人们尽可能快地开始和运行而不用担心类路径问题。注意 hibernate 包 含一个声明不要在产品中使用 HashtableCacheProvider 的警告。 为了使用 ehcache 代替 HashtableCacheProvider,只是移除那行代码并在类路径下放上 ehcache.jar 就可以。注意你也许还得找下和你的环境兼容的正确 ehcache 的库版本。前一个 在 jboss 和 特 定 的 ehcache 版 本 之 间 的 不 兼 容 的 原 因 是 因 为 改 变 了 缺 省 的 HashtableCacheProvider 7.3. Hibernate 事务 缺省情况下,jBPM 将委托事务给 hibernate 并使用每事务模型 session。jBPM 将在一个 hibernate session 被打开时开始一个 hibernate 事务。 这个将当持久化操作在 jbpmContext 上被 调用时的第一次发生。这个事务将恰好在 hibernate session 关闭前被提交。那个将发生在 jbpmContext.close()的内部。 使用 jbpmContext.setRollbackOnly()来标记事务回滚。那样的话,这个事务将被恰好地 回滚到 jbpmContext.close()内部关闭 session 之前的位置。 为了限制 jBPM 调用 hibernate API 的事务方法,设置 isTransactionEnabled 为 false,这 个在上面的 7.2.1 DbPersistenceServiceFactory 部分被解释了。 7.4. JTA 事务 大多数情况下为了在一个 JEE 应用服务器(如 JBoss)上使用 jBPM 时管理事务。为了 绑定事务到 JTA,使用下面的代码: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 84 页 / 共 199 页
  • 85. <jbpm-context> <service name="persistence"> <factory> <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory"> <field name="isTransactionEnabled"><false /></field> <field name="isCurrentSessionEnabled"><true /></field> <field name="sessionFactoryJndiName"> <string value="java:/myHibSessFactJndiName" /> </field> </bean> </factory> </service> ... </jbpm-context> 然后你应该在你的hibernate session工厂中指定使用数据源并且绑定hibernate到事务管 理器。确保你绑定数据源到一个XA数据源以防止你使用多个资源。更多关于绑定hibernate 到你的事务管理器的信息,请查阅hibernate文档中的“事务策略配置”这一段。 <hibernate-configuration> <session-factory> <!-- hibernate dialect --> <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property> <!-- DataSource properties (begin) --> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 85 页 / 共 199 页
  • 86. <property name="hibernate.connection.datasource">java:/JbpmDS</property> <!-- JTA transaction properties (begin) --> <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</pr operty> <property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransaction ManagerLookup</property> <property name="jta.UserTransaction">java:comp/UserTransaction</property> ... </session-factory> </hibernate-configuration> 然后确信你已经配置了 hibernate 使用 XA 数据源。 这些配置允许 EJB 使用 CTM 而且仍然允许 web 控制台使用 BMT。那就为什么 'jta.UserTransaction'属性也被指定的原因。 7.5. 定制查询 所有 jBPM 使用的 HQL 查询以一个配置文件为中心。那个资源文件被在 hibernate.cfg.xm 配置文件中像这样被引用: <hibernate-configuration> ... <!-- hql queries and type defs --> <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" /> ... </hibernate-configuration> 为了定制一个或更多的查询,复制下这个源文件并放你的定制的版本到你的类路径的某 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 86 页 / 共 199 页
  • 87. 处。然后在你的 hibernate.cfg.xml 文件中更新'org/jbpm/db/hibernate.queries.hbm.xml'引用指向 你定制的版本。 7.6. 数据库兼容性 jBPM 能运行在任何被 hibernate 支持的数据库上。在 jBPM(src/config.files)下的这个 示例配置文件指定了使用 hypersonic 内存数据库。那个数据库在开发和测试期间是理想的。 hypersonic 数据库保持所有的数据在内存中而不在硬盘上存储它。 7.6.1. JDBC 隔离级 确保为你的 JDBC 连接配置的数据库隔离级至少是 READ_COMMITTED。 即使是 READ_UNCOMMITTED(隔离级别 0 和 HSQLDB 所支持的隔离级别)几乎所 有属性工作正常,但工作执行器和多 token 同步也许会发生竞争状态(race condition) 。 7.6.2. 变更 jBPM 数据库 下面是当改变 jBPM 使用不同的数据库时需要做的指示性列表:  放 jdbc 驱动程序库到类路径上  更新 jBPM 使用的 hibernate 配置  在新数据库中创建模式(schema) 7.6.3. jBPM 数据库模式 jbpm.db 子项目,包含许多驱动、使用说明和脚本帮助你在你选择的数据库上开始。请 查阅 jbpm.db 项目根下的 to the read 获得更多的信息。 jBPM 能够为所有的数据库生成 DDL 脚本,这些脚本未必是最优的。所以你也许想让 你的 DBA 评审下所生成的 DDL 并为其优化列类型及使用索引。 在 开 发 中 你 可 能 感 兴 趣 下 列 的 hibernate 配 置 : 如 果 你 设 置 hibernate 配 置 属 性 'hibernate.hbm2ddl.auto' 为'create-drop'的话(例如在 hibernate.cfg.xml 中),这个模式将在应 用第一次用到它时自动地在数据库中创建。当应用关闭时,这个模式将被丢弃。 模式生成在程序中也可以使用 jbpmConfiguration.createSchema()和 jbpmConfiguration.dropSchema()进行调用。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 87 页 / 共 199 页
  • 88. 7.6.4. 已知问题 本节高亮显示这些已经使用 jBPM 被测试过的在数据库上使用的已知问题。 7.6.4.1. Sybase 问题 某些 sysbase 发布包有一个已知的问题截短二进制文件导致系统的不正当的行为。这个 限制原因在于 sysbase 数据库的二进制的存储机制。 7.7.合并 hibernate 类 在你的项目中,你也许为持久化使用 hibernate。将你的持久化类同 jBPM 的持久化类合 并是可选的操作。当将你的 hibernate 持久化同 jBPM's hibernate 持久化合并时有两个主要的 益处: 首先,session、连接和事务管理变得更容易些。通过合并 jBPM 和你的持久化到一个 hibernate session 工厂,就由一个 hibernate session、一个 jdbc 连接处理你和 jBPM 的持久化。 所以当更新到你自己的域模型时在同一个 session 中 jBPM 被自动地更新。这就能够消除了 使用事务管理的必要。 其次,这样允许你扔你的 hibernate 持久化对象到流程变量而不会有更多的麻烦(hassle)。 整 合 你 的 持 久 化 类 同 jBPM 的 持 久 化 类 最 简 易 的 方 式 是 通 过 创 建 一 个 中 心 的 hibernate.cfg.xml 文件。你可以用 jBPM 的 src/config.files/hibernate.cfg.xml 作为一个开始点, 然后在那里增加些引用到你自己的 hibernate 映射文件。 7.8. 定制 jBPM hibernate 映射文件 要定制 jBPM hibernate 的映射文件,你可以像下面这样处理:  从源文件(src/java.jbpm/...)处或 jbpm jar 包内部段复制 jBPM 的映射文件。  放这个拷贝到你的类路径上的任何地方  在你的 hibernate.cfg.ml 配置文件中更新引用为这些定制的映射文件。 7.9. 二级缓存 一旦加载他们之后 jBPM 使用 hibernate 的二级缓存来保持流程定义在内存中。流程定 义类和集合在 jBPM 的 hibernate 映射文件中被配置使用 cache 元素像下面这样: <cache usage="nonstrict-read-write"/> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 88 页 / 共 199 页
  • 89. 因为流程定义从不会改变,所以保持它们在二级缓存中是好的。参考21.1.3 改变部署的 流程定义。 二级缓存是 JBoss jBPM 实现的一个重要方面。如果它没有这个缓存,JBoss jBPM 同其 他的实现 BPM 引擎的技术比较可能会有一系列的缺陷。 缓存的策略是设置非严格读写(nonstrict-read-write) 。在运行时,缓存策略可以被设置 为只读(read-only)。但是那样的话,你将需要为部署的每个流程设置一个独立的 hibernate 映射文件。那就是为什么我们选择 nonstrict-read-write 的原因。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 89 页 / 共 199 页
  • 90. 第 8 章 jBPM 数据库 8.1. 切换后端数据库 切换后端 JBoss jBPM 是切实可行的。 我们将使用 PostgreSQL 和 MySQL 作为示例走完 这个流程。 这个流程对于所有的其他支持的数据库都是相同的。 对于这些大量支持的数据库、 大量的驱动程序、hibernate 配置文件和 ANT 构建文件在当前的 jBPM 发布的 DB 子项目中 生成数据库创建脚本。 如果你不能找到你要使用的数据库的这些文件的话, 你应该首先确认 是否 hibernate 支持你的数据库。如果真的不支持的话,你可以看下在 DB 项目中的当前数 据库中的一个文件并模仿这个来使用你自己的数据库。 对于这个文档,我们将使用 jBoss jBPM 新手工具箱。我们假设这个新手工具箱是被释 放到你电脑的叫${JBPM_SDK_HOME}目录的位置。你能够找到 jBPM 的 DB 子项目在 ${JBPM_SDK_HOME}/jbpm-db 处。 在安装了数据库后, 你需要运行数据库创建脚本。这些脚本将在数据库中创建 jBPM 表。 为了让缺省的 web 应用使用这个新数据库来运行。我们需要更新新手工具箱中的一些服务 器的配置文件。对于这些配置的变化,我们将不会涉及的太多。如果你想知道更多的关于不 同的服务器的配置设定的话,我们建议你去看一下 JBoss 的文档。 8.1.1. 隔离级别 无 论 你 使 用 什 么 数 据 库 , 确 信 配 置 的 JDBC 连 接 的 隔 离 级 别 至 少 是 READ_COMMITTED,那个在7.6.1 JDBC连接隔离级别有所解释。 8.1.2. 安装 PostgreSQL 数据库管理器 为了安装 PostgrepSQL 或其他你可能正在使用的数据库,我们查阅这些产品的安装手 册。在 windows 上安装 PostgreSQL 数据库是相当的完美的。 安装程序创建一个专一 windows 用户并定义数据库管理员(administrator) 。我们将使用 PostgreSQL 自带一个叫 pgAdmin III 的管理工具来创建 jBPM 数据库。 这个工具创建了 jbpmDB 数据库后的屏幕快照显示在下面 的图中。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 90 页 / 共 199 页
  • 91. 图 8-1 创建 jbpmDB 数据库后 PostgreSQL pgAdmin III 工具快照 完成数据库的安装后,我们可以使用数据库浏览器工具(像 DBVisualizer)来查看数据 库的内容。在你用 DBVisualizer 定义数据库连接前,你应该增加一个 PostgreSQL 的 JDBC 驱动管理器。选择'Tools->Driver Manager...'来打开驱动管理器窗口。看下面的一个如何增加 PostgreSQL JDBC 驱动的示例图。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 91 页 / 共 199 页
  • 92. 图 8-2 增加 JDBC 驱动到驱动管理器 这些就用 DBVisualizer 设置定义数据库连接到我们的新创建的数据库的所有事。在这个 文档中我们将用进一步的使用这个工具来确保创建脚本并流程部署预期有那样工作。使用 DBVisualizer 工具来创建连接的示例请参考在下面的图。正像你看到的一样,还没有表在你 的数据库中。我们将在下一部分来创建这些表。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 92 页 / 共 199 页
  • 93. 图 8-3 为 jBPM 数据库创建连接 另一个值得提一下的是上图中的数据库 URL: 'jdbc:postgresql://localhost:5432/JbpmDB'。 如果你使用别的名称来创建 jbpmDB 数据库的话,或者 PostgreSQL 没有运行在本机或在另 一个端口上,你就不得不相应地调整你的数据库 URL。 8.1.3. 安装 MySQL 数据库管理器 要安装 MySQL 数据库,请查阅 MySQL 提供的文档。安装是相当的容易和笔直的,在 windows 上只需要几分钟的时间。你需要使用 MySQL 提供的数据库管理控制台。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 93 页 / 共 199 页
  • 94. 图 8-4 MySQL Administrator 8.1.4. 使用 PostGreSQL 或 MySQL 创建 JBoss jBPM 数据 库 为了使你的数据库得到正确的被生成的数据库脚本。 你应该使用 jBPM 新手工具箱中提 供 的 脚 本 。 在 新 手 工 具 箱 的 ${JBPM_SSTARTERSKIT_HOME}/jbpm-db/build/${DATABASE_TYPE}/scripts 下你可以找 到所有主流数据库的创建脚本。 使用你的数据库管理控制台, 定位到数据库然后打开并执行 一个创建我们所提到的脚本。下面是 PostGreSQL 和 MySQL 在他们各自的控制台上的屏幕 快照。 图 8-5 8.1.4.1. 使用 PostGreSQL 创建 JBoss jBPM 数据库 正像已经提到的那样,你将在 DDB 子项目中找到许多被支持的数据库的脚本。 PostgreSQL 数据库的脚本可以在'${JBPM_SDK_HOME}/jbpm-db/build/mysql/scripts 目录下 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 94 页 / 共 199 页
  • 95. 找到。这个创建脚本叫'postgresql.create.sql'。使用 DBVisualizer,你可以加载这个脚本并切 换到'SQL Commander'标签页然后选择'File->Load...'。在下面的对话框中, 定位到创建脚本文 件。做这个的结果显示在下图中。 图 8-6 加载数据库创建脚本 要用 DBVisualizer 执行这个脚本,你选择'Database->Execute'。在这一步之后所有的 JBoss jBPM 表被创建了。这种情形如下面这个插图。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 95 页 / 共 199 页
  • 96. 图 8-7 运行数据库创建脚本 8.1.4.2. 使用 MySQL 创建 JBoss jBPM 数据库 一旦你已经提前安装了 MySQL 并创建了 jbpm 数据库,用你喜欢的任何数据库名称。 在本例中使用的是”jbomdb”。一个数据库的屏幕快照如下: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 96 页 / 共 199 页
  • 97. 图 8-8 MySQL 下创建 jbpm 数据库时的 MySQL Administrator 你可以使用命令行工具来加载数据库脚本。打开一个 DOS 窗口或终端窗口并键入下面 的命令行: mysql -u root -p 你将得到 MySQL 要求 root 用户密码的提示,无论你使用哪个帐户修改这个数据库。在登录 之后,键入下面的命令使用新建的 jbpmdb: use jbpmdb cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 97 页 / 共 199 页
  • 98. 图 8-9 为 MySQL 加载数据库创建脚本 现在你可以通过执行下面的命令为 jBPM 加载数据库脚本: source mysql.drop.create.sql 一旦脚本执行,你应该在 MySQL 的命令窗口中得下面的输出: 图 8-10 为 MySQL 数据库加载数据库创建脚本 8.1.5. 最后一步 在这步后,在当前的表中仍然没有任何数据。要想让 jBPM web 应用工作,你至少应该 在 jbpm_id_user 表中创建一些记录。当缺省的新手工具箱的发布包在 HSQLDB 上运行时, 为了能够在这个表中得到正确的相同入口,我们建议运行下面的脚本: insert into JBPM_ID_USER (ID_, CLASS_, NAME_, EMAIL_, PASSWORD_) values ('1', 'U', 'user', 'sample.user@sample.domain', 'user'); insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) values ('2', 'U', 'manager', 'sample.manager@sample.domain', 'manager'); insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 98 页 / 共 199 页
  • 99. values ('3', 'U', 'shipper', 'sample.shipper@sample.domain', 'shipper'); insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) values ('4', 'U', 'admin', 'sample.admin@sample.domain', 'admin'); 8.1.6. 更新 JBoss jBPM 服务器配置 在我们能够真正使用新创建的数据库来运行 JBoss jBPM 的 web 应用前, 我们还得去更 新些 JBoss jBPM 的配置。服务器上的位置是’ ${JBPM_SDK_HOME}/jbpm-server’。第一件 事我们将要做的是更新创建指出 JbpmDB 数据库的新数据源的配置。第二步是,我们将确 保那个缺省的 web 应用使用的是正在讨论的数据源而不再是那个 HSQLDB 的数据源。 <?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>JbpmDS</jndi-name> <connection-url>jdbc:postgresql://localhost:5432/JbpmDB</connection-url> <driver-class>org.postgresql.Driver</driver-class> <user-name>user</user-name> <password>password</password> <metadata> <type-mapping>PostgreSQL 8.1</type-mapping> </metadata> </local-tx-datasource> </datasources> 对于 MySQL 来说,数据源定义看起来像下面这样: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 99 页 / 共 199 页
  • 100. <?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>JbpmDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/jbpmdb</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>root</user-name> <password>root</password> <metadata> <type-mapping>MySQL</type-mapping> </metadata> </local-tx-datasource> </datasources> 为了创建一个新的数据源,你应该使用显示在上面的程序列表的内容创建一个叫 jbpm-ds.xml 文件。当然它可能不得不改变这个文件中的一些值来适应你的特殊的情形。然 后在${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy 文件夹中保存这个文件。恭喜, 你刚才为你的 JBoss jBPM 服务器创建了一个新的数据源。好,差不多的时候,为了使它们 真正地工作你不得不复制正确的 JDBC 驱动程序到 ${JBPM_SDK_HOME}/jbpm-server/server/jbpm/lib 目录下。当我们安装它在 DBVisualizer 中 为了能够浏览我们新创建的数据库时,我们已经在使用了这个 JDBC 程序。这个文件叫做 'postgresql-8.1-*.jdbc3.jar',在你的 PostgreSQL 安装文件夹的 jdbc 子文件夹下能够找到它。 对于MySQL,复制被安装的jdbc驱动程序从MySQL的ConnectorJ包中。当前MySQL Connector/J 3.1 这个版本你需要使用的,可以在http://www.mysql.com/products/connector/j/上 找到。 如果你使用不是 PostgreSQL 或 MySQL 而且正在考虑如何为你的特殊的数据库来创建 数据源定义, 你可以找到这个样例数据源定义在 JBoss 应用服务器发布中的 docs/examples/jca 文件夹下。如果你有你的数据库驱动程序,你使用 jBPM 就不会有什么问题。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 100 页 / 共 199 页
  • 101. 让缺省的 web 应用使用正确的数据源不再困难。第一步是在 '${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy/jbpm.sar/META-INF'文件夹下定位 'jboss-service.xml'文件。 使用下面所列的内容来修改这个文件的内容。细心的读者将会注意到唯一的不同是 'DefaultDS'和'JbpmDS'这两个词的交换。 <?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.jbpm.db.jmx.JbpmService" name="jboss.jbpm:name=DefaultJbpm,service=JbpmService" description="Default jBPM Service"> <attribute name="JndiName">java:/jbpm/JbpmConfiguration</attribute> <depends>jboss.jca:service=DataSourceBinding,name=JbpmDS</depends> </mbean> </server> 为了让每件事情都能运行我们最后需要做的是文件夹 '${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy/jbpm.sar'中的'jbpm.sar.cfg.jar'文件的 处理。然后替换包含 jdbc 连接属性的部分。这部分应该是看起来像下面列表中显示的那样: 在这个文件中有两处变化: hibernate.connection.datasource 属性应该指到我们创建的 JbpmDS 数据源正像在本节的第一步那样, 而且 hibernate.dialect 属性应该匹配 PostgreSQL 或 MySQL 的方言。 下面是所需的这两个变化的一个样例,注释掉你不需要的使用的数据库的方言版本。你 可以 从http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html#configurati on-optional-dialects处找到所支持的数据库方言类型列表。 <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 101 页 / 共 199 页
  • 102. "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- jdbc connection properties --> <!-- comment out the dialect not needed! --> <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.datasource">java:/JbpmDS</property> <!-- other hibernate properties <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> --> <!-- ############################################ --> <!-- # mapping files with external dependencies # --> <!-- ############################################ --> ... cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 102 页 / 共 199 页
  • 103. </session-factory> </hibernate-configuration> 现在我们准备启动服务器,看下这个 web 应用是否工作了。你还不能开始任何的流程, 因为还没有流程被部署。为了做这个我们查阅关于流程定义部署的文档。 8.2.数据库升级 在 jbpm.db 子项目中,你能够找到: • 创建 jBPM 3.0.2 模式(为 Hypersonic 数据库)的 SQL 脚本 • 创建 jBPM 3.1 模式(为 Hypersonic 数据库)的 SQL 脚本 • 从 jBPM 3.0.2 升级到 jBPM 3.1 的 SQL 脚本(为 Hypersonic 数据库) • 创建模式更新的 ant 脚本 模式 SQL 能够在 hsqldb/upgrade.scripts 目录下找到。 为了为你的数据库运行模式更新工具,看下列这些指导: • 先决条件:确信你已经安装了 jbpm.db 项目除了 jbpm 项目之外。在新手工具箱中, 这个是自动的方案。 如果 jbpm 安装在一个不同的位置, 相应地在你的 build.properties 中更新 jbpm.3.location 目录位置。 • 先决条件:你应该让你的数据库有一个合适的 JDBC 驱动程序 jar 包。 • 在 jbpm.db 项目的根下更新在 build.properties 中的属性: o upgrade.hibernate.properties:包含数据库的 hibernate 样式的连接属性的属 性文件 o upgrade.libdir:包含数据库 jdbc 驱动程序 jar 文件的目录 o upgrade.old.schema.script:创建旧数据库模式的模式生成在脚本(如果它已 经存在,你就不需要这个属性) • 为了创建旧模式然后计算差异,运行'ant upgrade.db.script'这个 ant 脚本 • 对 于 只 计 算 这 个 更 新 脚 本 而 没 有 第 一 次 加 载 旧 数 据 库 模 式 , 运 行 'ant upgrade.hibernate.schema.update' 这个 ant 脚本。 • 在成功完成后,你将在这个 build/database.upgrade.sql 目录下找到升级脚本 为了从 jBPM 3.0.2 升级到 jBPM 3.1,生成的升级 SQL 脚本(为 HSQLDB)举例如下: # New JBPM_MESSAGE table create table JBPM_MESSAGE ( ID_ bigint generated by default as identity (start with 1), CLASS_ char(1) not null, cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 103 页 / 共 199 页
  • 104. DESTINATION_ varchar(255), EXCEPTION_ varchar(255), ISSUSPENDED_ bit, TOKEN_ bigint, TEXT_ varchar(255), ACTION_ bigint, NODE_ bigint, TRANSITIONNAME_ varchar(255), TASKINSTANCE_ bigint, primary key (ID_) ); # Added columns alter table JBPM_ACTION add column ACTIONEXPRESSION_ varchar(255); alter table JBPM_ACTION add column ISASYNC_ bit; alter table JBPM_COMMENT add column VERSION_ integer; alter table JBPM_ID_GROUP add column PARENT_ bigint; alter table JBPM_NODE add column ISASYNC_ bit; alter table JBPM_NODE add column DECISIONEXPRESSION_ varchar(255); alter table JBPM_NODE add column ENDTASKS_ bit; alter table JBPM_PROCESSINSTANCE add column VERSION_ integer; alter table JBPM_PROCESSINSTANCE add column ISSUSPENDED_ bit; alter table JBPM_RUNTIMEACTION add column VERSION_ integer; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 104 页 / 共 199 页
  • 105. alter table JBPM_SWIMLANE add column ACTORIDEXPRESSION_ varchar(255); alter table JBPM_SWIMLANE add column POOLEDACTORSEXPRESSION_ varchar(255); alter table JBPM_TASK add column ISSIGNALLING_ bit; alter table JBPM_TASK add column ACTORIDEXPRESSION_ varchar(255); alter table JBPM_TASK add column POOLEDACTORSEXPRESSION_ varchar(255); alter table JBPM_TASKINSTANCE add column CLASS_ char(1); alter table JBPM_TASKINSTANCE add column ISSUSPENDED_ bit; alter table JBPM_TASKINSTANCE add column ISOPEN_ bit; alter table JBPM_TIMER add column ISSUSPENDED_ bit; alter table JBPM_TOKEN add column VERSION_ integer; alter table JBPM_TOKEN add column ISSUSPENDED_ bit; alter table JBPM_TOKEN add column SUBPROCESSINSTANCE_ bigint; alter table JBPM_VARIABLEINSTANCE add column TASKINSTANCE_ bigint; # Added constraints alter table JBPM_ID_GROUP add constraint FK_ID_GRP_PARENT foreign key (PARENT_) references JBPM_ID_GROUP; alter table JBPM_MESSAGE add constraint FK_MSG_TOKEN foreign key (TOKEN_) references JBPM_TOKEN; alter table JBPM_MESSAGE add constraint FK_CMD_NODE foreign key (NODE_) references JBPM_NODE; alter table JBPM_MESSAGE add constraint FK_CMD_ACTION foreign key (ACTION_) references JBPM_ACTION; alter table JBPM_MESSAGE add constraint FK_CMD_TASKINST foreign key (TASKINSTANCE_) references JBPM_TASKINSTANCE; alter table JBPM_TOKEN add constraint FK_TOKEN_SUBPI foreign key cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 105 页 / 共 199 页
  • 106. (SUBPROCESSINSTANCE_) references JBPM_PROCESSINSTANCE; alter table JBPM_VARIABLEINSTANCE add constraint FK_VAR_TSKINST foreign key (TASKINSTANCE_) references JBPM_TASKINSTANCE; 8.3. 在 JBoss 上开始 hsqldb 管理器 对于 jBPM 来说这个不是关键的,但是对于开发期间的一些情况,它可以方便的打开 hyphersonic 数据库管理器并让你在 jBoss 中访问 hyphersonic 数据库中的数据。 通过浏览器并定位到jBPM服务器的JMX控制台来开始。你应该在你的浏览器上使用这 个URL: http://localhost:8080/jmx-console。当然如果你在另一个机器或其他的端口而不是默认 的来运行的话这个看起来稍有点不同。一个结果页面的屏幕快照显示如下图: 图 8-11 JBoss jBPM JMX 控制台 如果你在 JBoss 入口上点击'database=jbpmDB,service=Hypersonic'链接,你将看到 HSQLDB 数据库管理的 JMX Mbean 视图。向下滚动一点这个页面,在操作部分,你将看到 'startDatabaseManager()'操作。这个屏幕快照的插图如下: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 106 页 / 共 199 页
  • 107. 图 8-12 HSQLDB Mbean 点击这个 invoke 按钮将开始 HSQLDB 数据库管理器应用。这是一个相当粗糙的数据库 客户端工具,但是它为我们执行这个生成的脚本工作的很好。你可以按 ALT-TAB 来浏览这 个应用当它被其他的窗口覆盖时。下面的这个图显示了上面这个应用已加载脚本并准备执 行。按下'Execute SQL'按钮将执行脚本并高效地更新你的数据库。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 107 页 / 共 199 页
  • 108. 图 8-13 HSQLDB Database 管理器 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 108 页 / 共 199 页
  • 109. 第 9 章 Java EE 应用服务器工具 本章将描述 jBPM 提供的工具,它对 Java EE 基础架构起到一个杠杆的作用。 9.1. EJB CommandServiceBean 是一个无状态的会话 bean,它在独立的 jBPM 上下文里通过调用 它的 execute 方法执行 jBPM 命令。下表中总结了可定制的环境入口和资源。 名称 类型 描述 用来读 jBPM 配置的类路径资源。可选,缺省是 JbpmCfgResource 环境入口 jbpm.cfg.xml。 ejb/LocalTimerEntit 连接到实现任务服务的本地实体 bean。当包含定时器 EJB 引用 yBean 时是流程需要。 给 jBPM 持久化服务提供 JDBC 连接的数据源的逻辑 jdbc/JbpmDataSour 资源管理器 名 。 必 须 同 hibernate 配 置 文 件 中 的 ce 引用 hibernate.connection.datasource 属性相匹配。 jms/JbpmConnectio 资源管理器 给 jBPM 消息服务提供 JMS 连接的工厂的逻辑名称。 nFactory 引用 包含同步连续的流程需要。 jBPM 消息服务发送工作消息到这里引用的队列。为了 消息目的引 确保这个和 job 监听器 bean 收到消息是同一个队列, jms/JobQueue 用 message-destination-link 指 出 一 个 通 用 的 逻 辑 目 标 , JobQueue。 表 9-1 Command service bean 环境 CommandListenerBean 是一个在 JbpmCommandQueue 上用来监听命令消息消息驱动 bean。这个 bean 为 CommandServiceBean 代理命令执行。 消息体必须是一个实现了 org.jbpm.Command 接口的 Java 对象。消息属性,如果有的 话,将被忽略。如果消息不匹配预期的格式,它被转进 DeadLetterQueue 中。不再进一步地 处理那个消息。如果缺少目标引用的话,消息被拒绝。 这样收到的消息指定了一个 replyTo 目标,命令执行的结果就是打包消息对象并发送到 那里。命令连接工厂环境引用显示 JMS 连接所支持的资源管理器。 相反,JobListenerBean 是一个在 JbpmJobQueue 上用来监听工作消息的消息驱动 bean 来支持异步连续(asynchronous continuations)。 消息必须有一个叫 jobId 的长整数类型属性引用一个数据库中的待决的工作。对于消息 体,如果有的话,可以忽略。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 109 页 / 共 199 页
  • 110. 这个 bean 扩展 CommandListenerBean 并且可定制来继承它的环境入口和资源引用。 名称 类型 描述 ejb/LocalCommand 链接到在独立的 jBPM 上下文上执行命令的本地会话 EJB 引用 ServiceBean baen。 jms/JbpmConnectio 资源管理器 给产生结果消息的 JMS 连接提供本地工厂的逻辑命 nFactory 引用 名。命令消息需要显示一个回复目标。 不包含命令的消息被送到这里引用的队列。可选的,如 jms/DeadLetterQue 消息目标引 果没有的话,这消息将被驳回,可能导致容器再投递消 ue ce 用 息。 表 9-2 Command/Job 监听器 bean 环境 TimerEntityBean 同 EJB 定时器服务共同作用来调度 jBPM 定时器。在定时到期后,定 时器的执行才真正地被委托给命令服务 bean。 为了读取定时器的数据定时器的实体 bean 需要访问 jBPM 数据源。EJB 部署符没有提 供一种方式去定义如何让一个实体 bean 映射到一个数据库。这个留给了容器的提供器 (provider)。在 JBoss AS 中,jbosscmp-jdbc.xml 描述符定义数据源 JNDI 名和关系映射数据 (它们中的表和列名) 。注意 JBoss XMP 描述符使用一个全书 JNDI 名(java:JbpmDS)这 , 和资源管理器引用形成对照(java:comp/env/jdbc/JbpmDataSource) 。 jBPM 较早版本使用一个叫 TimerServiceBean 的无状态会话 bean 同 EJB 定时器服务交 互。这 session 方案不得不被放弃了因为在 cancelation 方法中有一个不可避免的瓶颈问题。 因为会话 bean 没有身份,定时器服务被迫去迭代所有的定时器来查找出已经退出的。这个 bean 仍然是围绕着向后的兼容性。它像 TimerEntityBean 一样工作在相同的环境下,以至于 更容易迁移。 名称 类型 描述 ejb/LocalCommand 链接到独立的 jBPM 上下文中执行定时器的本地 EJB 引用 ServiceBean {@linkplain CommandServiceBean 会话 bean}。 表 9-2 Command/Job 监听器 bean 环境 9.2. jBPM 企业配置 jbpm.cfg.xml 包含下列的配置项: <jbpm-context> <service name="persistence" factory="org.jbpm.persistence.jta.JtaDbPersistenceServiceFactory" /> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 110 页 / 共 199 页
  • 111. <service name="message" factory="org.jbpm.msg.jms.JmsMessageServiceFactoryImpl" /> <service name="scheduler" factory="org.jbpm.scheduler.ejbtimer.EntitySchedulerServiceFactory" /> </jbpm-context> JtaDbPersistenceServiceFactor 允许 jBPM 加入 JTA 事务。如果一个现有的事务正在进行 的话,JTA 持久化事务将保持它,否则它开始一个新的事务。jBPM 企业 bean 配置为给窗口 分派一个事务管理。 然而, 如果你在没有活动的事务的环境中创建一个 JbpmContext 的话 也 ( 就是说,在一个 web 应用中),一个事务将自动开始。JTA 持久化服务工厂有个下面描述 的配置域。 • isCurrentSessionEnabled:如果是 true,jBPM 将使用同进行的 JTA 事务关联的“当 前”Hibernate 会话。这是缺省设置。参考 Hibernate 手册,2.5 Contextual sessions 节 描述了这个行为。你可以利用 session 机制的上下文关系来使用被 jBPM 在应用的其 他部分通过调用 SessionFactory.getCurrentSession()取得的同一个 session。另一方面, 你可以让 jBPM 支持你自己的 hibernate session。这样做的话,设置 isCurrentSessionEnabled 为 false 并通过 JbpmContext.setSession(session)方法注入 session。 这也要确保 jBPM 同你应用的其他部分使用相同的 Hibernate session。 意 , 注 Hibernate session 可能通过持久化上下文注入到无状态的会话 bean 中。 • isTransactionEnabled:这个域值为 true 意味着 jBPM 将通过 Hibernate 的事务 API (Hibernate 手册的 11.2 数据库事务分界部分显示了这些 API)用 JbpmConfiguration.createJbpmContext()来创建事务,使用 JbpmContext.close()来提交 事务并关闭 Hibernate 会话。当 jBPMp 以 ear 来部署时这绝对不是一个期望的行为, 因此 isTransactionEnabled 缺省情况下设置为 false。 JmsMessageServiceFactoryImpl 对可靠的通信基础结构有一个杠杆作用通过暴露的 JMS 接口传递异步连续消息到 JobListenerBean。JMS 消息服务工厂暴露下列的配置域。 • connectionFactoryJndiName: 在 JNDI 初始上下文中的 JMS 连接工厂的名字。对于 Java 缺省是:comp/env/jms/JbpmConnectionFactory。 • destinationJndiName: 工作消息将被传送那里 JMS 目标的名字。必须同 JobListenerBean 接收消息的目标相匹配。 对于 java 缺省是:comp/env/jms/JobQueue。 • isCommitEnabled: 告诉 jBPM 是否应该使用 JbpmContext.close()提交 JMS 会话。消 息被 JMS 消息服务产生, 那就意味着 (消息)在当前的事务提交前将从不会被接收。 因此通过服务创建的 JMS 会话总是被处理。当在用的连接工厂是 XA 时缺省的值 false 是合适的, 同样的 JMS 会话产生的消息将通过被全部的 JTA 事务控制。 这个域 应该被设置为 true 如果 JMS 连接工厂不是 XA 的话,因此 jBPM 将明确地提交这个 JMS 会话的本地事务。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 111 页 / 共 199 页
  • 112. EntitySchedulerServiceFactory 构建在事务告知服务上,为 EJB 容器提供的定时事件调度 企业流程定时器。EJB 调度服务工厂有下面描述的可配置域。 • timerEntityHomeJndiName: TimerEntityBean 在 JNDI 初始上下文中的本地 home 接口 的名称。对于 java 缺省是:comp/env/ejb/LocalTimerEntityBean. 9.3. Hibernate 企业配置 hibernate.cfg.xml 包含下列被修改后去支持其他数据库或应用服务器的配置项目: <!-- sql dialect --> <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.HashtableCacheProvider </property> <!-- DataSource properties (begin) --> <property name="hibernate.connection.datasource">java:comp/env/jdbc/JbpmDataSource</property> <!-- DataSource properties (end) --> <!-- JTA transaction properties (begin) --> <property name="hibernate.transaction.factory_class"> org.hibernate.transaction.JTATransactionFactory </property> <property name="hibernate.transaction.manager_lookup_class"> org.hibernate.transaction.JBossTransactionManagerLookup cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 112 页 / 共 199 页
  • 113. </property> <!-- JTA transaction properties (end) --> <!-- CMT transaction properties (begin) === <property name="hibernate.transaction.factory_class"> org.hibernate.transaction.CMTTransactionFactory </property> <property name="hibernate.transaction.manager_lookup_class"> org.hibernate.transaction.JBossTransactionManagerLookup </property> ==== CMT transaction properties (end) --> 你可以符合你的数据库管理系统的一个方言来替换hibernate.dialect。hibernate参考手册 在3.4.1 SQL方言部分列举了可用的数据库方言。 HashtableCacheProvider能够使用其他支持的缓存提供器实替换。对于支持的缓存提供器 列表可以查阅hibernate手册 19.2 二级缓存部分。 JbossTransactionManagerLookup可以被一个策略替换适合的不同于JBoss的应用服务器。 查看3.8.1 事务策略配置部分找到符合每个应用服务器的lookup类。 注意用于 hibernate.connection.datasourceJNDI 名称, 实际上,是一个资源管理器引用, 可移植多应用服务器。也就是说引用意味着一个真正的数据源在部署时绑定到目标服务器 上。在包含的 jboss.xml 描述符中,引用绑定到 java:JbpmDS 上。 默认包中,jBPM 被配置使用 JTATransactionFactory。如果一个现有的事务是正在进行 中的话,JTATransaction 工厂使用它,否则它创建一个新的事务。jBPM 企业 Bean 被配置委 派事务管理到容器上。然而,如果你使用的 jBPM API 在没有的事务活动事务的上下文中的 话(也就是说,在一个 web 应用中),一个事务被将自动开始。 如果你自己的 EJB 容器管理事务并且你想限制非计划的事务创建的话,你可以切换到 CMTTransactionFactory。使用空上设置,hibernate 将总是查找一个现有的事务并且如果什么 也没有找到的话就汇报问题。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 113 页 / 共 199 页
  • 114. 9.4. 客户端组件 客户组件依赖 jBPM API 编写,希望对企业服务确保他们的部署描述符在适当的位置有 个合适的环境引用起到杠杆作用。 下面的描述符可以被看作典型的客户端会话 Bean。 <session> <ejb-name>MyClientBean</ejb-name> <home>org.example.RemoteClientHome</home> <remote>org.example.RemoteClient</remote> <local-home>org.example.LocalClientHome</local-home> <local>org.example.LocalClient</local> <ejb-class>org.example.ClientBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <ejb-local-ref> <ejb-ref-name>ejb/LocalTimerEntityBean</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home> <local>org.jbpm.ejb.LocalTimerEntity</local> </ejb-local-ref> <resource-ref> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 114 页 / 共 199 页
  • 115. <res-ref-name>jdbc/JbpmDataSource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-ref> <res-ref-name>jms/JbpmConnectionFactory</res-ref-name> <res-type>javax.jms.ConnnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <message-destination-ref> <message-destination-ref-name>jms/JobQueue</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <message-destination-usage>Produces</message-destination-usage> </message-destination-ref> </session> 被提供的目标服务器是 JBoss,上面的环境引用可以像后面的那样被绑定资源在目标操 作环境上。注意 JNDI 名称必须同 jBPM Bean 使用的值相匹配。 <session> <ejb-name>MyClientBean</ejb-name> <jndi-name>ejb/MyClientBean</jndi-name> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 115 页 / 共 199 页
  • 116. <local-jndi-name>java:ejb/MyClientBean</local-jndi-name> <ejb-local-ref> <ejb-ref-name>ejb/LocalTimerEntityBean</ejb-ref-name> <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name> </ejb-local-ref> <resource-ref> <res-ref-name>jdbc/JbpmDataSource</res-ref-name> <jndi-name>java:JbpmDS</jndi-name> </resource-ref> <resource-ref> <res-ref-name>jms/JbpmConnectionFactory</res-ref-name> <jndi-name>java:JmsXA</jndi-name> </resource-ref> <message-destination-ref> <message-destination-ref-name>jms/JobQueue</message-destination-ref-name> <jndi-name>queue/JbpmJobQueue</jndi-name> </message-destination-ref> </session> 假使客户端组件是一个 web 应用,与企业 Bean 形成对照,部署描述符看起来像这样: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 116 页 / 共 199 页
  • 117. <web-app> <servlet> <servlet-name>MyClientServlet</servlet-name> <servlet-class>org.example.ClientServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyClientServlet</servlet-name> <url-pattern>/client/servlet</url-pattern> </servlet-mapping> <ejb-local-ref> <ejb-ref-name>ejb/LocalTimerEntityBean</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home> <local>org.jbpm.ejb.LocalTimerEntity</local> <ejb-link>TimerEntityBean</ejb-link> </ejb-local-ref> <resource-ref> <res-ref-name>jdbc/JbpmDataSource</res-ref-name> <res-type>javax.sql.DataSource</res-type> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 117 页 / 共 199 页
  • 118. <res-auth>Container</res-auth> </resource-ref> <resource-ref> <res-ref-name>jms/JbpmConnectionFactory</res-ref-name> <res-type>javax.jms.ConnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <message-destination-ref> <message-destination-ref-name>jms/JobQueue</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link>JobQueue</message-destination-link> </message-destination-ref> </web-app> 如果目标应用服务器是 JBoss 的话,上面的环境引用能够像下面一样在目标操作的环境 中绑定到资源上。 <jboss-web> <ejb-local-ref> <ejb-ref-name>ejb/LocalTimerEntityBean</ejb-ref-name> <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 118 页 / 共 199 页
  • 119. </ejb-local-ref> <resource-ref> <res-ref-name>jdbc/JbpmDataSource</res-ref-name> <jndi-name>java:JbpmDS</jndi-name> </resource-ref> <resource-ref> <res-ref-name>jms/JbpmConnectionFactory</res-ref-name> <jndi-name>java:JmsXA</jndi-name> </resource-ref> <message-destination-ref> <message-destination-ref-name>jms/JobQueue</message-destination-ref-name> <jndi-name>queue/JbpmJobQueue</jndi-name> </message-destination-ref> </jboss-web> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 119 页 / 共 199 页
  • 120. 第 10 章 流程建模 10.1. 概述 流程定义代表业务流程的正式规范并且基于有向图。图由节点和转换组成。图中的每个 节点指定类型。节点类型定义运行时行为。一个流程定义绝对有一个开始状态。 令牌(token)是一个执行路径。一个令牌是维护图中一个节点指针的运行时概念。 流程实例是一个流程定义的执行。当一个流程实例被创建时,令牌为主执行路径创建。 这个令牌叫做流程实例的根令牌并且它被放在流程定义的开始状态上。 Signal 指示一个令牌继续图执行。当收到一个未命名的 signal 时,领先将离开它的当前 节点通过缺省的离开转换。当在 signal 中指定一个被命名的转换时,节点将通过指定的转换 离开当前节点。Signal 让流程实例分派到根令牌。 在令牌进入节点后,这个节点被执行。节点自己负责图执行连续(continuation of the graph execution)。图执行连续完成后使令牌离开这个节点。每个节点类型为图执行连续实现 一个不同的行为。不传播执行的节点将成为状态节点。 动作(Actions)是在流程执行里的事件上被执行的 java 代码片段。图是一个关于软件 需要的重要沟通工具。图仅是正开发软件的是一个视图(投影) 。它隐藏了许多的技术细节。 动作是在图形表示外面增加技术细节的一个机制。 一旦这个图放在这个位置, 它可以被动作 修饰。主要事件类型包含进入节点、离开节点和取得转换。 10.2. 流程图 流程定义的基础是由节点和转换组成的图。那信息被表达在一个叫 prcessdefinition.xml 的 xml 文件中。每个节点都有一个类型,如状态、决策、分支、合并等等,每个节点都有 一系列的离开转换。为了区分开他们可以给转换一个名称。例如:下面的图显示的是 jBAY 拍卖流程的流程图: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 120 页 / 共 199 页
  • 121. 撤消 拍卖 卖出 发货 记帐 发货 收款 收货 付款 图 10-1 拍卖流程图 下面是用使用 xml 表示的 jBay 的拍卖流程图: <process-definition> <start-state> <transition to="auction" /> </start-state> <state name="auction"> <transition name="auction ends" to="salefork" /> <transition name="cancel" to="end" /> </state> <fork name="salefork"> <transition name="shipping" to="send item" /> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 121 页 / 共 199 页
  • 122. <transition name="billing" to="receive money" /> </fork> <state name="send item"> <transition to="receive item" /> </state> <state name="receive item"> <transition to="salejoin" /> </state> <state name="receive money"> <transition to="send money" /> </state> <state name="send money"> <transition to="salejoin" /> </state> <join name="salejoin"> <transition to="end" /> </join> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 122 页 / 共 199 页
  • 123. <end-state name="end" /> </process-definition> 10.3. 节点 一个流程图由节点和转换组成。更多的关于图和它的执行模型的信息,参考第 4 章 面 向图的程序设计。 每个节点有一个给定的类型。节点类型决定了在运行时当一个执行到达节点时将发生 什么事情。你可以使用 jBPM 已经实现的一系列的节点类型。也许,你可以写自定义的代码 来实现你自己的特定的节点行为。 10.3.1. Node 职责 每一个节点有两个责任:首先,它可以执行一个纯 java 代码。典型地纯 java 代码同节 点的功能是相关联。例如:创建一个新的任务实例、发送通知、更新数据库等等,其次,节 点负责传播流程执行。基本上,每个节点为了传播流程执行有下列的选项: • 1、不传播执行。那样的话这个节点相当于等待状态。 • 2、通过节点的离开转换中的一个来传播执行。这就意味着最初到达节点的令牌 (token)使用 API 调用 executionContext.leaveNode(String)方法通过其中的一个离 开转换。这个节点将扮演一个自动节点,从某种意义上说,它可以执行一些定制的 程序逻辑并且而后不用等待自动地继续流程执行。 • 3、创建新的执行路径。一个节点决定创建新的令牌。每个新令牌代表一个新的执 行路径并且每个新令牌可以通过节点的离开转换被启动。 这种行为相当的例子就是 一个分支(fork)节点。 • 4、结束执行路径。节点能够决定执行路径的终点。那意味着令牌被结束并且执行 路径被完成。 • 5、通常,节点可以修改整个流程实例的运行时结构。运行时结构是包含令牌树的 一个流程实例。 节点能够被创建并结束令牌、 在图的一个节点上设置令牌以及通过 转换启动令牌。 jBPM包含(像任何工作流和BPM引擎一样)一系列预实现的有明确的文档化配置和行 为的节点类型。但关于jBPM和面向图的程序设计基础的唯一的事情是我们为开发人员展现 了这样的模型。开发人员可以非常容易地写他们自己的节点行为以及在流程中使用它。 传统工作流和BPM系统更加封闭。他们通常提供一套固定的节点集合(叫流程语言)。 它们的流程语言是封闭的并且执行模型是隐藏在运行时环境中。工作流模型的研究已经显示 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 123 页 / 共 199 页
  • 124. 出任何的流程语言都不是足够强大的。 我们选定了一个简单的模型并允许开发写自己的节点 类型。那就是JPDL流程语言最后开放的途径。 下一步,我们将讨论下 JPDL 最重要的节点类型。 10.3.2. task-node 节点类型 任务(task)节点代表一个或多个将要被人们执行的任务。所以当执行到达任务节点时, 任务实例将在工作流参与者的任务列表中创建。后来,节点将相当于一个等待状态。所以当 用户执行他们的任务时,任务完成将触发执行的恢复。换句话说,那将导致在令牌(token) 上调用一个新的信号(signal)。 10.3.3. state 节点类型 状态(state)节点是一个最基本的等待状态。同任务节点的不同是不会在任何任务列表 中创建任务实例。如果流程要等待一个外部系统时是有用的。例如,在节点的入口上(通过 一个在节点进入事件上的动作) ,一个消息可以被送到外部系统。在那之后,流程将进入等 待状态。当这个外部系统发送一个响应消息时,这可以引起一个 token.signal()方法,触发流 程执行的恢复。 10.3.4. decision 节点类型 实际上有两种方法来塑造决策(decision)。两种方法间的区别是由“谁”产生决策。应 该是流程产生决策(读:规定在流程定义里) ,还是一个外部实体提供决策的结果。 当决策被流程产生时,决策节点应该被使用。有两个基本的方法去指定决策的条件。最 简单的方式是在转换(trasitions)上增加条件元素。条件是 EL 表达式或 beanshell 脚本,它 将返回一个布尔(boolean)值。 在运行时决策节点将“第一次”循环通过它的离开转换。那有一个指定的条件。它首先 将顺序地计算那些在 xml 中指定的转换。第一个被解析为 true 的条件的那个转换将被处理。 如果所有的都解析为 false 的话,缺省的转换(在 XML 中的第一个)被处理。 At runtime the decision node will FIRST loop over its leaving transitions THAT HAVE a condition specified. It will evaluate those transitions first in the order as specified in the xml. The first transition for which the conditions resolves to 'true' will be taken. If all transitions with a condition resolve to false, the default transition (the first in the XML) is taken. 另一种方案是使用一个表达式返回要处理转换的名称。关于表达式(expression)属性, 你可以在决策上指定一个表达式必须解析到决策节点的一个离开转换中。 Another approach is to use an expression that returns the name of the transition to take. With the 'expression' attribute, you can specify an expression on the decision that has to resolve to one of cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 124 页 / 共 199 页
  • 125. the leaving transitions of the decision node. 下一个方案在决策上的“handler”元素,那个元素能够被用来指定一个已被指定在决策 节点上的 DecisionHandler 接口的实现。然后决策在 java 类中被计算并通过 DecisionHandler 实现的决策方法返回选择的离开转换。 当决策被外部方(意思是:不是流程定义的部分)处理时,你应该使用多个转换离开一 个状态或等待状态节点。然后离开转换能够被等待状态完成后恢复执行的外部的触发器提 供。例如:Token.signal(String transitionName)和 TaskInstance.end(String transitionName)。 10.3.5. fork 节点类型 分叉(fork)节点分开一个执行路径成为多个并发的执行路径。缺省的分叉行为是为每 个离开这个分叉节点的转换创建一个子令牌(token) ,在到达分叉节点时生成和这个令牌的 一个父子关系(parent-child relation) 。 10.3.6. join 节点类型 缺省的,合并(join)假设所有到达合并节点的令牌是同一个父亲的孩子。这种情形被 创建当使用上面所提及的分叉时创建同且当所有被分叉创建的令牌到达同一个合并时。 一个 合并将结束每一个进入合并的令牌。当所有的兄弟令牌已经到达合并时,父令牌将通过“唯 一”的离开转换被传播。当仍然有兄弟令牌活动时,合并将扮演一个等待状态。 10.3.7. node 节点类型 这一类型节点可用作你想在在节点中写自己的代码的这种情形。 节点类型节点有一个子 元素动作。这个动作在执行到达节点时被执行。你在actionhandler中写的代码可以做你完成 的任何事,但是它也要负责执行的传播。 如果你想使用 JavaAPI 来实现一些对于业务分析来说是重要的功能逻辑的话,这个节点 可被使用。由于使用节点,这个节点在流程图形表示中可见。作为比较,动作(下面要讲的) 允许你增加代码,但它在流程图形表示中不可见, 如果那样的话对于业务分析逻辑无关紧要。 10.4. 转换 转换(transitions)有一个源节点和一个目标节点。源节点代表来自(from)属性,目 标节点代表到达(to)属性。 转换能够可选的来命名。注意 jBPM 的大部分属性依赖转换的命名的唯一性。如果有多 个转换有相同的名称,第一个命名的转换将被处理。万一多个重复转换名称占用一个节点, 方法 Map getLeavingTransitionsMap()返回的元素要比 List getLeavingTransitions()少。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 125 页 / 共 199 页
  • 126. 缺省的转换是列表中的第一个转换。 10.5. 动作 动作(actions)是在流程执行里的事件上执行的 java 代码片段。图是关于软件需求沟通 上一个重要的工具。而且图正好也是将要被开发的软件的一个视图(项目) 。它隐藏了许多 的技术细节。动作是在图形化表示外面增加技术细节的一个机制。一旦图放在那里,它可以 被动作修饰。这就意味着在不改变图的结构的情况下可以让 java 代码同图关联起来。主要 事件类型是进入节点(entering a node)、离开节点(leaving a node)和处理转换(taking a 。 transition) 注意下动作放在事件和放在节点上的不同。动作放在事件上当事件被触发时执行。 事件 上的动作没有办法去影响流程的控制流。它同观察者模式(observer pattern)很相似。另一 方面,放置在节点上的动作有责任来传播执行。 让我们看一个事件上动作的例子。假设我们想在给定的转换上执行一个数据库更新。数 据库更新在技术上是至关重要的,但是对于业务分析就无关紧要了。 解雇雇员 在这个转换上我们想 在HR数据库上执行更新 收集标记 图 10-2 数据库更新动作 public class RemoveEmployeeUpdate implements ActionHandler { public void execute(ExecutionContext ctx) throws Exception { // 从流程变量上取得被操作的员工 String firedEmployee = (String) ctx.getContextInstance().getVariable("fired employee"); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 126 页 / 共 199 页
  • 127. // 得到同 jbpm 更新使用的一样的同一个数据库连接,我们为我们的数据库更新 // 重用 jbpm 的事务 Connection connection = ctx.getProcessInstance().getJbpmSession().getSession().getConnection(); Statement statement = connection.createStatement(); statement.execute("DELETE FROM EMPLOYEE WHERE ..."); statement.execute(); statement.close(); } } <process-definition name="yearly evaluation"> ... <state name="fire employee"> <transition to="collect badge"> <action class="com.nomercy.hr.RemoveEmployeeUpdate" /> </transition> </state> <state name="collect badge"> ... cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 127 页 / 共 199 页
  • 128. </process-definition> 10.5.1. 动作配置 关于增加你自己的定制动作的配置和如何指定processdefinition.xml的配置的信息,请参 考21.2.3 代理配置。 10.5.2. 动作引用 动作可以被命名。命名的动作能够被另一个可以指定动作的位置引用。命名的动作也能 作为一个子元素放在流程定义中。 这个属性令人感兴趣的如果你想限制重复的动作配置的话(例如动作有一个复杂的配 置)。另一个用例是执行或运行时动作调度。 10.5.3. 事件 事件指定片刻(moments)在流程执行里。jBPM 引擎将触发事件在图执行期间。这将 出现当 jbpm 计算下一状态时(请读:处理一个信号) 。事件总是关联着流程定义元素(如 流程定义、节点或转换) 。大部分的流程元素都能用不同类型的事件触发。例如节点能触发 节点进入事件和节点离开事件。事件挂在动作上。每个事件都有一个动作的列表。当 jBPM 引擎触发事件时,动作列表将被执行。 10.5.4. 事件传播 超级状态(Superstates)在流程定义元素里创建一个父子关系。包含在超级状态中的节 点和转换把它当作父亲。上层元素把流程定义作为父亲,流程定义没有父亲。当一个事件被 触发时,事件将向上传播到父层。 这样就允许捕捉所有流程的转换事件及在一个中心位置同 这些事件相关的动作。 10.5.5. 脚本 脚本(script) 是一个动作执行的beanshell脚本。更多的关于beanshell的信息,请看beanshell 网站。缺省情况下, 所有流程变量可以当作脚本变量使用而且不会有脚本变量被写入流程变 量中。下面的脚本变量是可用的: • executionContext • token • node cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 128 页 / 共 199 页
  • 129. • task • taskInstance <process-definition> <event type="node-enter"> <script> System.out.println("this script is entering node "+node); </script> </event> ... </process-definition> 为了定制缺省的加载行为了存储变量到脚本中, 变量元素可以被用作脚本的子元素。那 样的话,脚本表达式也会放进脚本的子元素:expression 中。 <process-definition> <event type="process-end"> <script> <expression> a = b + c; </expression> <variable name='XXX' access='write' mapped-name='a' /> <variable name='YYY' access='read' mapped-name='b' /> <variable name='ZZZ' access='read' mapped-name='c' /> </script> </event> ... cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 129 页 / 共 199 页
  • 130. </process-definition> 在脚本开始之前, 流程变量 YYY 和 ZZZ 分别作为脚本变量 b 和 c 来用。在脚本完成后, 脚本变量的值 a 被存储进流程变量 XXX 中。 如果访问变量属性包含“read”,那么流程变量将在脚本计算前作为脚本变量被加载。 如果访问属性包含“write”,那么脚本变量将在计算后作为流程变量被存储。属性 mapped-name 能够让流程变量以脚本中的另一个名称来使用。 这样可以很方便当你的流程变 量命名中包含空格或其他的无效脚本字符(script-literal-characters)时。 10.5.6. 定制事件 注意也可能在流程执行期间触发你自自己的事件。事件通过图的组成元素(节点、转换 流程定义和超级状态是图元素) 以及事件类型 (java.lang.String) 唯一地定义。 jBPM 为节点、 转换和其他的图元素定义一系列的触发事件。 在动作中、 自定义的节点实现中或甚至流程执 行 的 外 面 , 你 可 以 调 用 GraphElement.fireEvent(String eventType, ExecutionContext executionContext);。事件类型的名称可以被自由的选择。 10.6. 超级状态 超级状态(superstate)是一组节点。超级状态可以递归地嵌套。超级状态常用来指出一 些流程定义中的层次。例如:一个应用能够组合流程的所有节点成为了阶段。动作同超级状 态事件相关联。结果是令牌可以在一个给定的时间被多重嵌套节点里。 这是很方便的去检查 是否流程执行在开始(start-up)阶段。在 jBPM 模型中,你可以在超级状态中自由地组合的 任何节点集。 10.6.1. 超级状态转换 所有的转换离开一个超级状态都能够被超级状态所包含的节点里的令牌处理。转换也能 到达超级状态。那样的话,令牌将重定位超级状态的第一个节点。超级状态外面的节点有转 换直接地到内部节点。而且,与之相反,超级状态内部节点有转换到外部节点或到超级状态 自身。超级状态也可以有自己的引用。 10.6.2. 超级状态事件 超 级 状 态 有 两 个 唯 一 的 事 件 : 超 级 状 态 进 入 ( superstate-enter ) 和 超 级 状 态 离 开 (superstate-leave。这些事件都将被触发无论是通过各自的节点进入或离开转换。只要令牌 在超级状态的内部处理转换,这些事件就不会被触发。 注意我们已经为状态和超级状态创建了独立的事件类型。这就使它变得容易了,区分从 超级状态内部传播出的超级状态事件和节点事件。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 130 页 / 共 199 页
  • 131. 10.6.3. 分层命名 节点名称在它们的范围内必须是唯一的。 节点的范围是它的节点集合 (node-collection) 。 流程定义和超级状态都是节点的集合。为了在超级状态中引用节点,你必须指定相对、正斜 线(/)分隔的名称。斜线分隔节点名称。使用“..”引用上级。下个例子显示了如何在超级 状态中引用一个节点: <process-definition> ... <state name="preparation"> <transition to="phase one/invite murphy"/> </state> <super-state name="phase one"> <state name="invite murphy"/> </super-state> ... </process-definition> 下个例子显示了如何建立超级状态的级别。 <process-definition> ... <super-state name="phase one"> <state name="preparation"> <transition to="../phase two/invite murphy"/> </state> </super-state> <super-state name="phase two"> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 131 页 / 共 199 页
  • 132. <state name="invite murphy"/> </super-state> ... </process-definition> 10.7. 异常处理 jBPM 异常处理机制只应用于 java 异常。图执行在它自己上面不会导致问题。它只是能 够导致异常的代理类(delegation classes)的执行。 在流程定义、节点和转换上,指定了一列异常处理(exception-handlers)。每个异常处 理有一列动作。当在代理类中发生异常时,这个流程元素的父亲层次会搜索一个合适的异常 处理。当它被找到后,异常处理的动作被执行。 注意 jBPM 的异常处理机制同 java 的异常处理不完全相似。在 java 中,一个被捕捉的 异常对控制流会有影响。就 jBPM 来说,控制流不能通过 jBPM 异常处理机制来改变。异常 要么被捕获要么没有。没有被捕获的异常被抛出到客户端(例如:调用 token.signal()的客户 端),要么异常就被 jBPM 异常处理捕获。对于捕获的异常,图执行继续就像没有异常一样。 注意在一个处理异常的动作里,它可能使用 Token.setNode(Node node)方法来放置这个 令牌在图的任意一个节点上。 10.8. 流程组成 流程组成借助流程状态(process-state)的在 jBPM 中被支持。流程状态是一个同另一 个流程定义关联的状态。当图执行到达流程状态时, 一个子流程的新流程实例被创建并且同 在流程状态上到达的执行路径相关联。超级流程(super process)的执行路径将等待直到子 流程实例已经结束。当子流程实例结束时, 超级流程执行路径将离开流程状态并继续超级流 程里的图执行。 <process-definition name="hire"> <start-state> <transition to="initial interview" /> </start-state> <process-state name="initial interview"> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 132 页 / 共 199 页
  • 133. <sub-process name="interview" /> <variable name="a" access="read,write" mapped-name="aa" /> <variable name="b" access="read" mapped-name="bb" /> <transition to="..." /> </process-state> ... </process-definition> 这个 hire 流程包含一个产生 interview 流程的流程状态。 当执行到达 “first interview”时, 一个 interview 流程的新的执行(=process instance)被创建。如果没有明确版本被指定,当 部署 hire 流程时已知子流程的最新版本将被使用。为了让 jBPM 实例化一个特定版本,可选 的版本属性可以被指定。 为了延迟绑定指定的或最新的版本直到真正创建子流程时, 可选的 绑定属性应该被设置为 late。 后 hire 流程的变量 然 “a”被复制进入 interview 流程的变量 “aa” 中。同样的方法, hire 流程的变量 “b”被复制进入 interview 流程的变量 “bb” 。当 interview 中 流程完成时,只有 interview 流程的变量“aa”被复制回 hire 流程的变量“a”中。 通常,当一个子流程开始时,所有的读访问(read access)变量从超级流程中读取并在 signal 被给予离开开始状态前输入新创建的子流程中。当子流程实例被完成时,所有的写访 问 ( write access ) 变 量 将 从 子 流 程 变 量 复 制 到 超 级 流 程 中 。 变 量 元 素 的 映 射 名 称 (mapped-name)属性允许你去指定应该在子流程中使用的变量名称。 10.9. 定制节点行为 在 jBPM 中,它是非常容易的去写你自定义和节点。对于创建自定义节点,一个 ActionHandler 的实现已经被写完,这个实现可以执行任何的业务逻辑,但也负责去传播图 执行。让我们看一个更新 ERP 系统的例子。我们将从 ERP 系统中读一个数量,增加一个存 储在流程变量中的数量然后再存这个结果回到 ERP 系统中。基于数量的大小,我们不得不 通过“小额”和“大额”离开转换离开这个节点。 在ERP系统中更新汇总 大额 小额 图 10-3 更新 ERP 例子的流程片段 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 133 页 / 共 199 页
  • 134. public class AmountUpdate implements ActionHandler { public void execute(ExecutionContext ctx) throws Exception { // 业务逻辑 Float erpAmount = ...从 ERP 系统中获取数量...; Float processAmount = (Float) ctx.getContextInstance().getVariable("amount"); float result = erpAmount.floatValue() + processAmount.floatValue(); ...update erp-system with the result...; // 图执行传播 if (result > 5000) { ctx.leaveNode(ctx, "big amounts"); } else { ctx.leaveNode(ctx, "small amounts"); } } } 它也可能创建并合并令牌在定制的节点实现里。就如何做这个的例子,到 jbpm 源代码 检出 Fork 和 Join 节点实现吧 :-)。 10.10. 图执行 jBPM 的图执行模型是基于流程定义解析和命令模式链(chain of command pattern) 。流 程定义解析意味流程定义数据被存储在数据库中。 在运行时流程定义信息在流程执行期间被 使用。注意相关的,我们使用 hibernate 的二级缓存去避免在运行时定义信息的加载。因为 流程定义不能改变(查看流程版本管理(process versioning)) 以 hibernate 能够在内存中缓 ,所 存流程定义。 命令链模式意味着图的每个节点负责传播流程执行。如果某个节点不传播执行的话,那 么它相当于一个等待状态。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 134 页 / 共 199 页
  • 135. 这个想法是在流程实例上开始执行而且那个执行继续直到它进入一个等待状态。 一个令牌代表执行的路径。一个令牌在流程图中有一个指向节点的指针。在等待状态期 间,这个令牌能够被持久化在数据库中。现在我们要看一下计算令牌执行的算法。执行当一 个信号被送到令牌时开始,执行然后传递转换和节点通过命令链模式。这些是在一个类图中 的相关的方法。 节点 +leavingTransitions +from 转换 * +enter(in executionContext:ExectionContext) : void +arrivingTransitions +execute(in executionContext:ExectionContext) : void +take(in executionContext:ExectionContext) : void +leave(in executionContext:ExectionContext) : void +to * 图 10-4 图执行的相关方法 当令牌是在一个节点上时, 信号能够被送到令牌上。 发送一个信号是一个开始执行的指 令。一个信号因此必须指定令牌的当前节点的离开转换。第一个转换是缺省的。在一个信号 到达令牌上, 令牌处理它的当前节点并调用 Node.leave(ExecutionContext,Transition)方法。认 为 ExecutionContext 是令牌因为在 ExecutionContext 中的主要对象是一个令牌。 Node.leave(ExecutionContext,Transition)方法将触发转换事件并在转换的目标结点上调用 Node.enter(ExecutionContext)方法。那个方法将触发节点进入事件并且调用 Node.execute(ExecutionContext)方法。 每一节点类型都有它自己的在 execute 方法中实现的行 为。每一个节点通过再次调用 Node.leave(ExecutionContext,Transition)方法负责传播图执 行。综上所述: • Token.signal(Transition) • --> Node.leave(ExecutionContext,Transition) • --> Transition.take(ExecutionContext) • --> Node.enter(ExecutionContext) • --> Node.execute(ExecutionContext) 注意下一个状态的完全计算,包含在客户端的线程里的动作调用完成。通常的误解是在 客户端的线程的所有计算必须被完成。与异步调用一样,你可以使用异步消息(JMS)来完 成那些。当消息在与流程实例更新相同的事务里被发送时,所有同步问题被处理。某些工作 流系统在图的所有节点间使用异步消息。但在高吞吐量的环境中,这个算法为调整业务流程 的性能提供了更多的控制和灵活性。 10.11. 事务划分 同样的解释在10.10 图执行部分和第4 章 面向图的程序设计,jBPM在客户端线程中运 行流程而且天生就是同步的。 那意味着token.signal()或taskInstance.end()当流程已经进入新的 等待状态时将只是返回。 我们这里描述的jPDL属性是来自第 15 章 异步连续的建模视图。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 135 页 / 共 199 页
  • 136. 在大部分情形下这是最直接的方案,因为流程执行可以容易地也必然是有服务器端事 务:在一个事务中流程从一个状态移动到下一个状态。 在一些场景里的流程内部计算花费许多时间, 这个行为也许是令人讨厌的。 了为应付这 个问题,jBPM 包括一个异步消息系统,它允许以一种异步的方式继续流程。当然,在 java 企业环境里,jBPM 能够被配置为使用 JMS 消息中间件(broker)取代内置的消息系统。 在任一节点里,jPDL 支持属性 async="ture"。异步节点将不被姓在客户端线程中,一个 消 息 通 过 异 步 消 息 系 统 被 发 送 而 且 线 程 返 回 到 客 户 端 ( 意 味 着 token.signal() 或 taskInstance.edn()将返回)。 注意 jbpm 客户端代码能够现在提交事务。正在发送的消息应该在和流程更新相同的同 一个事务中完成。所以事务的最终结果是令牌已经移动到下一个节点(还没有被执行的)并 且一个 org.jbpm.command.ExecuteNodeCommand 消息已经在异步消息系统上被发送 jBPM 命 令执行器。 jBPM 命 令 执 行 器 从 队 列 中 读 取 命 令 并 执 行 他 们 。 就 org.jbpm.command.ExecuteNodeCommand 来说,流程将依赖在一个独立的事务中执行的 node. Each 命令而被继续。 所以为了使异步流程继续,jBPM 命令执行器需要运行。最简单的方式是在你的 web 应 该中配置 CommandExecutionServlet,作为选择,你应该确信 CommandExecutor 线程是启动 并以任何其他的方式在运行。 作为一个流程建模人员,你不应该太在意所有的异步消息。 主要暗示记住界限:缺省 jBPM 将 在 客 户 端 事 务 中 操 作 , 执 行 整 个 的 计 算 直 到 流 程 进 入 一 个 等 待 状 态 。 使 用 ansync="true"来划分流程中的一个事务。 让我们看一个例子: ... <start-state> <transition to="one" /> </start-state> <node async="true" name="one"> <action class="com...MyAutomaticAction" /> <transition to="two" /> </node> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 136 页 / 共 199 页
  • 137. <node async="true" name="two"> <action class="com...MyAutomaticAction" /> <transition to="three" /> </node> <node async="true" name="three"> <action class="com...MyAutomaticAction" /> <transition to="end" /> </node> <end-state name="end" /> ... 客户端代码同流程执行的交互(开始和恢复)是同正常(同步)流程绝对相同的: ...start a transaction... JbpmContext jbpmContext = jbpmConfiguration.createContext(); try { ProcessInstance processInstance = jbpmContext.newProcessInstance("my async process"); processInstance.signal(); jbpmContext.save(processInstance); } finally { jbpmContext.close(); } 在第一个事务后,流程实例根令牌指到节点 one 并且一个 ExecuteNodeCommandmessage 被送到命令执行器。 在随后的事务里,命令执行器将从队列中读消息并执行节点 one。动作可以决定是传播 执行还是进入等待状态。如果动作决定传播执行,当执行到达节点 two 时事务将被结束,等 等,等等…… cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 137 页 / 共 199 页
  • 138. 第 11 章 上下文 上下文是关于流程变量的。流程变量是维护同流程实例相关信息的键值对。因为上下文 必须在数据库中可以存储,所以使用了一些细小的限制。 11.1. 访问变量 org.jbpm.context.exe.ContextInstance 服务于同流程变量一同工作的中心接口。你可以 serves ProcessInstance 像这样来获得 ContextInstance: ProcessInstance processInstance = ...; ContextInstance contextInstance = (ContextInstance) processInstance.getInstance(ContextInstance.class); 最基本的操作是: void ContextInstance.setVariable(String variableName, Object value); void ContextInstance.setVariable(String variableName, Object value, Token token); Object ContextInstance.getVariable(String variableName); Object ContextInstance.getVariable(String variableName, Token token); 变量命名是 java.lang.String。缺省,jBPM 支持下列的值类型:  java.lang.String  java.lang.Boolean  java.lang.Character  java.lang.Float  java.lang.Double  java.lang.Long  java.lang.Byte  java.lang.Short  java.lang.Integer  java.util.Date  byte[]  java.io.Serializable  hibernate 使用的可持久化的类 无类型的 null 值也能被持久化地存储。 在流程变量中存储所有其他的类型没有什么问题。但当你保存流程实例时它将导致一个 异常。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 138 页 / 共 199 页
  • 139. 为了在流程变量中配置 jBPM 存储 hibernate 持久化对象,查看存储 hibernate 持久化对象。 11.2. 变量生存期 变量不必在在流程包中声明。在运行时,你可以在流程变量中放置任何对象。如果那个 变量不存在的话,那么它将被创建。这恰好和纯 java.util.Map 是一样的。 变量可以使用下面的方法来删除: ContextInstance.deleteVariable(String variableName); ContextInstance.deleteVariable(String variableName, Token token); 现在支持自动类型改变。这意味着允许用一个不同类型的值去覆盖一个变量。当然同, 你应该尝试去限制类型改变的数量,因为这会创建更多的数据库通信及纯粹的列更新。 11.3. 变量持久化 变量是流程实例的一部分。在数据库中保存流程实例,让数据库和流程实例同步。变量 在数据库中创建、更新和删除相当于流程实例在数据库中保存结果(等于更新)。更多的信 息,请查看第 7 章 持久化。 11.4. 变量范围 每一个执行(阅读:令牌(token))有它自己的流程变量集。请求一个变量总是在令牌上 完成的。流程变量有一个令牌树(查看:面向图的程序设计) 。当请求一个变量而没有指定 令牌时,默认的令牌是根令牌。 变量查找递归地通过给定的令牌的父亲完成。 这个行为和程序设计语言中的变量范围是 相似的。 当一个不存在的变量设置到令牌上时, 这个变量在根令牌上被创建。 这就意味着每一个 变量都有默认的流程范围。为了生成令牌局部(token-local)变量,你不得不明确地创建它: ContextInstance.createVariable(String name, Object value, Token token); 11.4.1. 变量重载 变量重载意味着每一个执行路径能够有它自己的一个同名变量复本。它们互不相关而且 以后还可以有不同的类型。变量重载如果你通过同一个转换启动多个并发执行路径的话是令 人感兴趣的。那么唯一要辨别的是那些执行路径都有它们各自的变量集。 11.4.2.变量重写 变量重写(variable overriding)意味着嵌套执行路径的变量在更全局的执行路径里重写。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 139 页 / 共 199 页
  • 140. 通常, 嵌套执行路径关联到并发: 在分支和合并间的执行路径是到达合并的执行路径的孩子 (嵌套的) 。例如:如果你在流程实例范围内有一个变量'contact',那么你就能够在嵌套执行 路径'shipping' 和 'billing 上重写这个变量。 11.4.3. 任务实例变量范围 更多关于任务实例变量的信息,请查看12.4 任务实例变量部分。 11.5. 瞬态变量 当一个流程实例在数据库中被持久时,正常变量也作为流程实例的一部分被持久化。某 些情形下你可能想在代理 delegation) ( 类中使用一个变量, 但是你不想在数据库中存储它。 一个例子充当这样的从 jBPM 的外部传递到代理类的数据库连接。这就能被瞬态变量完成。 瞬态变量的生存期和流程实例 java 对象相同。 因为它们的天性, 瞬态变量不能同令牌关联。 所以只有一个为流程实例对象的瞬态变量 的映射。 瞬态变量使用它们自己的上下文实例中的方法集来访问,而且不需要在 processdefinition.xml 中声明。 Object ContextInstance.getTransientVariable(String name); void ContextInstance.setTransientVariable(String name, Object value); 11.6. 定制变量持久化 变量以 2 步(2-step)方案存储到数据库中: 用户 java 对象(user-java-object ) ( <-->转换器 converter)<--> 变量实例(variable instance) 变量被存储在变量实例中。变量实例的成员使用 hibernate 在数据库中被映射到字段。 在 jBPM 的缺省配置中,变量实例使用 6 种类型:  DateInstance(用 java.lang.Date 字段映射到数据库中的 Types.TIMESTAP)  DoubleInstance(用 java.lang.Double 字段映射到数据库中的 Types.DOUBLE)  StringInstance(用一个 java.lang.String 字段映射到数据库中的 Types.VARCHAR)  LongInstance(用一个 java.lang.Long 字段映射到数据库中的 Types.BIGINT)  HibernateLongInstance(这是 hibernate 表类型使用的长整数 id 字段。用一个 java.lang.Object 字段字映射作一个数据库中 hibernate 实体引用)  HibernateStringInstance(这是 hibernate 表类型使用的字符串 id 字段。用一个 java.lang.Object 字段作被映射作一个数据库中 hibernate 实体引用) 转换器(converter)在 java 用户对象和能够通过变量实例存储的 java 对象之间转换。 所以当流程变量使用如 ContextInstance.setVariable(String variableName, Object value)设置 时,这个值将使用转换器可选地被转换。然后被转换的对象将被存储在变量实例 (VariableInstance)中。转换器是下面的接口实现: public interface Converter extends Serializable { boolean supports(Object value); Object convert(Object o); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 140 页 / 共 199 页
  • 141. Object revert(Object o); } 转换器是可选的。转换器必须是jBPM类加载器可用的。 用 户 java 对 象 被 转 化 并 存 储 在 变 量 实 例 中 的 方 式 被 配 置 在 文 件 org/jbpm/context/exe/jbpm.varmapping.properties中。为了定制这个属性文件,在类路径的根 下面放置一个修改过的版本,就像 6.3 其他配置文件部分一样属性文件的每行指定了 2 至 3 相被空格分隔的类名:用户java对象的类名、可选的转换器的类名和变量实例的类名。当你 引 用 你 自 定 义 的 转 换 器 时 , 他 们 也 必 须 在 jBPM 类 路 径 中 而 且 hibernate 映 射 文 件 org/jbpm/context/exe/VariableInstance.hbm.xml必须被更新来包含定制的VariableInstance的子 类。 例如,看下 org/jbpm/context/exe/jbpm.varmapping.xml 文件中的下列 xml 片段。 <jbpm-type> <matcher> <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher"> <field name="className"><string value="java.lang.Boolean" /></field> </bean> </matcher> <converter class="org.jbpm.context.exe.converter.BooleanToStringConverter" /> <variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" /> </jbpm-type> 这个片段指定了 java.lang.Boolean 类型的所有对象必须使用 BooleanToStringConverter 转换器被转化并且结果对象(一个 String 字符串)将被存储在 StringInstance 灵类型的变量 实例对象中。 如果没有转换被指定,如下: <jbpm-type> <matcher> <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher"> <field name="className"><string value="java.lang.Long" /></field> </bean> </matcher> <variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" /> </jbpm-type> 那就意味着放在变量中的 Long 型对象只是存储在 LongInstance 类型的变量实例中并没 有被转化。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 141 页 / 共 199 页
  • 142. 第 12 章 任务分配 jBPM 的业务核心具有支持流程执行持久化的能力。这个属性对于任务管理和个人任务 列表这种情况非常有用。jBPM 允许指定一个软件片段去描述能够具有人员参与的等待状态 任务的整个流程。 12.1. 任务 任务是流程定义的部分并且他们定义了在流程执行期间任务实例怎样被创建和分派。 任务可以在流程节点(task-nodes)和流程定义(process-definition) 。那样的话任务节 点代表一个将被用户和流程执行完成的任务应该等待直到参与者(actor)完成这个任务。当 用户完成这个任务时,流程执行才能继续。当在任务节点(task-node)中被指定了多个任务 时,默认的行为是等待完成所有的任务。 任务也能够被指定在流程定义中。 在流程定义上指定的任务能够通过名称查找以及从任 务节点内引用或用于动作内部。实际上,被命名的所有任务(也包含在任务节点里的)能够 在流程定义中通过名称被查找到。 在整个流程定义中任务名称必须唯一。 任务可以赋予一个优先级。 这个优先级将被用作 任务创建的每一个每个任务实例的初始优先级。TaskInstances 后期可以改变这个优先级。 12.2. 任务实例 一个任务实例能够被分配一个 actorId(java.lang.String)。所有任务实例被存储在数据库 中的一个表里(JBPM_TASKINSTANCE) 。通过使用一个给定的 actorId 查询这个表的所有 任务实例,你能得到那个特定的用户的任务列表。 jBPM 任务列表机制能够将其他的任务和 jBPM 的任务组合,即使那些任务和流程执行 不相关。那样 jBPM 开发人员可以很容易地将 jBPM-process-tasks 和应用的任务组合在一个 中央的任务列表仓库(task-list-repository)中。 12.2.1. 任务实例生存期 任务实例生存期是容易理解的:在生成后,任务实例能够有选择的被开始。然后,任务 实例能够被结束,那就意味着任务实例被标记这完成。 注意由于为了灵活性,分配(assignment)不再是生存同期的一部分。所以任务实例能 够被分派或不分派。任务实例分配不会影响任务实例生存期。 任务实例典型的情况下是通过流程执行进入一个任务节点(使用 TaskMgmtInstance.createTaskInstance(...)方法)来创建。然后,一个用户接口构件将使用 TaskMgmtSession.findTaskInstancesByActorId(...)方法查询数据库获得用户列表。然后,从用 户 那 里 收 集 输 入 , UI 构 件 调 用 TaskInstance.assign(String) 、 TaskInstance.start() 或 TaskInstance.end(...)方法。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 142 页 / 共 199 页
  • 143. 任务实例用日期属性来维护它的状态:创建、开始和结束。那些属性可以被它们的各自 TaskInstance 上的 getters 访问。 现在, 已经完成的任务实例使用一个结束日期标记, 以至于他们不会再被随后的任务列 表的查询捕获。但他们还保持在 JBPM_TASKINSTANCE 表中。 12.2.2. 任务实例和图执行 任务实例是参与者的任务列表中的条目。任务实例能够发信号(signalling)。一个发信 号的任务实例是当它完成时,能够送信号到它的令牌来继续流程执行的一个任务实例, 。任 务实例能够阻塞(blocking),意味着相关的令牌(等于执行路径)在任务实例完成前不允许 离开这个任务节点。缺省情况下任务实例是发信号 signalling) ( 而非阻塞的(non-blocking)。 万一多个任务实例同这个任务节点相关联的,流程开发人员能够指定如何来完成任务实 例去影响流程继续(continuation) 。下列是能够赋予一个任务节点的 signal-property 的值的 列表:  last:这个是默认值。当上一个任务实例完成时继续进行执行。当在节点的入口上没 有任务被生成时,执行继续。  last-wait:继续进行执行当上一个任务实例被完成时。当在节点的入口上没有任务被 生成时,执行在任务节点里等待直到任务被创建。  first:继续进行执行当第一个任务实例完成时。 当没有任务在这个节点的入口上创建 时,执行继续。  first-wait: 继续进行执行当第一个任务实例完成时。当没有任务在这个节点的入口 上创建时,执行在任务节点里等待直到任务被创建。  unsynchronized: 执行总是继续。不管任务被创建或仍然未完成。  never: 执行从不继续,不管任务被创建或仍然未完成。 任务实例创建也许基于运行时的计算上。那样的话,增加一个 ActionHandler 在任务节 点的节点进入(node-enter)事件上并设置属性 create-tasks="false"。这里是一个这样的一个 动作处理实现的例子: public class CreateTasks implements ActionHandler { public void execute(ExecutionContext executionContext) throws Exception { Token token = executionContext.getToken(); TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance(); TaskNode taskNode = (TaskNode) executionContext.getNode(); Task changeNappy = taskNode.getTask("change nappy"); // now, 2 task instances are created for the same task. tmi.createTaskInstance(changeNappy, token); tmi.createTaskInstance(changeNappy, token); } } cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 143 页 / 共 199 页
  • 144. 像例子中显示的那样任务能够被创建能够在在这个任务节点上指定。他们本来也应该 被指定在流程定义里并且从 TaskMgmtDefinition 上捕获。TaskMgmtDefinition 用任务管理 信息扩展 ProcessDefinition。 在完成时标记任务实例的 API 方法是 TaskInstance.end()。可选择的,你可以指定一个转 换在 end 方法中。这样的为期不远任务实例的完成会触发执行的继续,任务节点留下特殊的 转换。 12.3. 分派 一个流程定义包含任务节点。 一个任务节点包含 0 或多个任务。 任务是一个静态的流程 定义部分的描述。在运行时,任务引起任务实例的生成。在任务实例相当于一个个人任务列 表的入口。 对于jBPM,任务分派的push (personal task list) 和 pull (group task list)模型(往下看) 可以应用在组合中。流程能够计算一个任务并将这个任务推进他/她的任务列表中。或可选 的,任务能够被分配给一个参与者池(pool of actors) ,在那种情况下池中的每个参与者都能 够将这个任务推进他/她的任务列表中。 12.3.1. 分派接口 分派的任务实例通过 AssignmentHandler 接口完成: public interface AssignmentHandler extends Serializable { void assign( Assignable assignable, ExecutionContext executionContext ); } 当一个任务实例创建时分派处理程序实现被调用。 在那个运行时, 任务实例能够被分配 给一个或多个参与者。AssignmentHandler 实现应该调用 Assignable 方法(setActorId or setPooledActors) 来 分 配 一 个 任 务 。 这 个 Assignable 既 是 一 个 TaskInstance 也 是 一 个 SwimlaneInstance(等于流程角色) 。 public interface Assignable { public void setActorId(String actorId); public void setPooledActors(String[] pooledActors); } TaskInstances 和 SwimlaneInstances 能够被分配给一个特定的用户或一例参与者池。分 配 TaskInstance 给用户,需要调用 Assignable.setActorId(String actorId)方法。而分配 TaskInstance 给候选参与者池,要调用 Assignable.setPooledActors(String[] actorIds)方法。 每一个流程定义的任务能够同一个分派处理程序实现相关联来在运行时执行分派。 当多个流程任务应该被分配给相同的人或参与者组时,可以考虑使用 swimlane。 为 了 允 许 重 用 AssignmentHandlers 的 生 成 , 每 个 AssignmentHandler 的 使 用 可 以 在 processdefinition.xml中配置。21.2 委托部分有更多关于如何给assignment handlers增加配置 的信息。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 144 页 / 共 199 页
  • 145. 12.3.2. 分派数据模型 管理任务实例分派的数据模型和参与者的泳道实例如下。每个 TaskInstance 有一个 actorId 和一个 pooled actors 集合。 图 12-1 分派模型类图 actorId 对任务负责,而池化的参与者集则代表一个他们是否处理这个任务的职责的候 选人集合。actorId 和 pooledActors 是可选的同时也是可以组合的。 12.3.3. 个人任务列表 个人任务列表表示所有已经分配给特定的人的任务实例。这可以使用 TaskInstance 上的 属性 actorId 来显示。所以为了放一个 TaskInstance 到某人的个人任务列表上, 你只需使用下 列一个方法:  在流程的任务元素的 actor-id 属性上指定一个表达式  在你的代码的任何地方使用 TaskInstance.setActorId(String)  在一个 AssignmentHandler 中使用 assignable.setActorId(String) 一个给定的用户要捕获个人任务列表,使用 TaskMgmtSession.findTaskInstances(String actorId)。 12.3.4. 群组任务列表 池化的参与者(pooled actors)表示任务实例的候选人。这就意味着任务被提供给许 多用户并且一个候选人不得不介入并处理任务。 用户不能立即他们的群组任务列表开始一个 任务。那也就将导致一个潜在的多个人开始相同的任务的冲突的可能。为了限制这个冲突, 用户可以只处理他们群组任务列表中的任务实例并且移动他们进入个人任务列表。 用户只允 许用他们个人任务列表上的任务来开始工作。 为了在某人的群组任务列表上放一个 taskInstance ,你必须放置用户的 actorId 或一个 groupIds 到 pooledActorIds 中。指定这个池化的用户,使用下列中的一个:  在流程的 task 元素的 pooled-actor-ids 属性中指定一个表达式  在你的代码的任何地方使用 TaskInstance.setPooledActorIds(String[])代码 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 145 页 / 共 199 页
  • 146.  在一个 AssignmentHandler 中使用 assignable.setPooledActorIds(String[]) 为给定的用户捕获群组任务列表,处理以下内容:制造一个包含用户的 actorId 和发表 于这个用户的群组的所有的 ids 的集合。用 TaskMgmtSession.findPooledTaskInstances(String actorId)或 TaskMgmtSession.findPooledTaskInstances(List actorIds)你可以搜索不在个人任务 列表中((actorId==null))的任务实例并且在池化的 actorIds 中可以 匹配。 这样做的背后的动机是我们想从 jBPM 任务分派中分离身份构件。jBPM 只存储 String 作为 actorIds 而不需要知道用户间的关系、群组以及其他的身份信息。 actorId 将总重载池化参与者。所以有一个 actorId 和 pooledActorIds 列表的 taskInstance, 将只需暴露在参与者的个人任务列表中。pooledActorIds 允许用户通过删除 taskInstance 的 actorId 属性来将一个任务实例放回到群组中。 12.4. 任务实例变量 任务实例可以有它自己的变量集和任务实例,也能“查看”流程变量。任务实例通常在 一个执行路径(也就是令牌,token)上创建。这会创建一个父子关系在令牌和任务实例间, 这同令牌他们自己的父子关系相类似。 正常的范围规则应用在任务变量和相关的令牌的流程 变量间。更多的关于范围的信息能够在11.4 变量范围部分找到。 这就意味着任务实例能够“看到”它自己的变量加上所有的它关联的令牌的变量。 控制器可以用于在任务实例范围和流程范围变量之间创建填入和提交变量。 12.5. 任务控制器 在任务实例创建时, 任务控制器可能填入任务实例变量而且当任务实例完成时, 任务控 制器能够提交任务实例的数据进入流程变量。 注意不会强迫您使用任务管理器。任务实例也能“看到”和它的令牌相关的流程变量。 使用任务控制器: • 创建任务实例的变量的拷贝,防止流程完成前中间媒介更新任务实例变量影响流程 变量,然后拷贝被提交给流程变量。 • 任务实例变量不是和流程变量一对一相关的。例如,假设流程变量'sales in januari'、 'sales in februari'和'sales in march'。然后流程实例的表单也许需要显示 3 个月的销售 平均值。 任务计划从用户那里收集输入。 但有许多用户接口能够用于向用户表示这个任务。 例如: web 应用、swing 应用、即时消息、邮件表单等等。所以任务控制器成为流程变量(流程上 下文)和用户接口间的桥梁。任务控制器提供一个到用户接口应用的的流程变量的视图。 当任务实例被创建时, 任务控制器负责提取信息从洛变量并创建任务变量。 任务变 量作为用户接口表单的输入。用户输入可以存储在任务变量中,当用户结束任务时,一个任 务控制器负责更新流程变量基于任务实例数据。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 146 页 / 共 199 页
  • 147. 图 12-2 任务控制器 在一个简单的场景中,在表单参数和流程变量间有一对一的映射。 任务控制器被指定在 task 元素里。那样的话,缺省的 jBPM 任务控制器可以被使用并且它获得元素内部变量元素 列表。变量元素表示流程变量如何在任务变量中被复制。 下个例子显示你怎么才能基于流程变量创建独立的任务实例变量复本: <task name="clean ceiling"> <controller> <variable name="a" access="read" mapped-name="x" /> <variable name="b" access="read,write,required" mapped-name="y" /> <variable name="c" access="read,write" /> </controller> </task> name 属性引用了流程变量的名称。 mapped-name 是可选并且引用任务实例变量的名称。 如果 mapped-name 属性被忽略了,mapped-name 缺省使用 name 属性。注意 mapped-name 也用作 web 应用中的任务实例表单的域的标签。 Access 属性指定是否在任务实例创建时复制变量,及是否要在任务结束时写回到流程 变量。这个信息可以被用于用户接口来生成适当的表单控制。access 属性是可选的而且缺省 的访问是“读,写”。 一个 task-node 可以有多个任务,start-state 有一个任务。 如果简单的流程变量和表单参数之间的一对一映射有太多限制的话, 你也可以写自己的 TaskControllerHandler 实现。这是 TaskControllerHandler 接口: public interface TaskControllerHandler extends Serializable { void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token); void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token); } cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 147 页 / 共 199 页
  • 148. 这里是如何在 task 中配置自定义的任务控制器实现: <task name="clean ceiling"> <controller class="com.yourcom.CleanCeilingTaskControllerHandler"> -- 写你自己的任务控制器程序的配置— </controller> </task> 12.6. 泳道 泳道(swimlane)是一个流程角色(role) 。它是指定流程中通过相同的参与者(actor) 来完成的多个任务的一个机制。 所以在为给定的泳道创建第一个任务实例后, 参与者将同一 个泳道上随后所有任务记在流程中。泳道因此会有一个分派(assignment) ,而且所有引用泳 道的任务不需要指定一个分派(assignment) 。 当第一个任务在给定泳道上创建时,泳道的 AssignmentHandler 被调用。Assignable 传 递给 AssignmentHandler 的是 SwimlaneInstance。重要的是要知道所有的在给定的泳道上的 任务实例完成的分派将传播到泳道实例。 这个行为缺省被实现的, 因为获得任务的去实现某 个流程角色的人将有特定的流程的知识。 所以那个泳道所有随后的任务实例的分派为那个用 户自动地完成。 泳道是从 UML 活动图中借用的术语。 12.7. 开始任务中的泳道 泳道能够和开始任务相关联去获得流程启动程序。 一个任务能够在一个开始状态中被指定。 那个任务同一个泳道相关联。当一个新的任务 实 例 为 这 样 一 个 任 务 创 建 时 , 当 前 的 认 证 参 与 者 将 使 用 Authentication.getAuthenticatedActorId()方法被捕获并且那个参考者将被存储在开始任务的 泳道中。 例如: <process-definition> <swimlane name='initiator' /> <start-state> <task swimlane='initiator' /> <transition to='...' /> </start-state> ... </process-definition> 变量也能够像使用任何其他的任务一样被增加到开始任务中来定义及任务相关联的形 式。查看 12.5 任务控制器部分。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 148 页 / 共 199 页
  • 149. 12.8. 任务事件 任务能让动作和他们相关联起来。 4 种标准事件类型定义任务: 有 task-create、task-assign、 task-start 和 task-end。 task-create 在任务实例创建时被触发。 task-assign 在任务实例正在被分配时被触发。注意在这个事件上被执行的动作里,你能 使用 executionContext.getTaskInstance().getPreviousActorId()访问上一个参与者。 task-start 当 TaskInstance.start()被调用时触发。 这个可以用来显示用户真正地开始做这个 任务实例。开始一个任务是可选的。 task-end 当 TaskInstance.end(…)被调用时触发。这个标记任务的完成。如果任务关联流 程执行,那么这个调用可能触发流程执行的恢复。 既然任务能够让事件和动作同他们相关联, 那么异常程序也能指定在任务上。 更多的关 于异常处理的信息,查看10.7 异常处理部分。 12.9. 任务定时器 像节点一样,定时器能够被指定在任务上。查看14.1 定时器部分。 关于任务的定时器特殊的事情是那个任务定时器 cancel-event 可以被定制。缺省的,任 务上的定时器在任务结束(等于完成)时将被取消。但是用定时器上的 cancel-event 属性, 流程开发人员可以定制那个,例如 task-assign 或 task-start。cancel-event 类型可以被组合通 过指定它们在这个属性里的以逗号分隔的列表。 12.10. 定制任务实例 任务实例能够被定制。做这个最容易的方式是去创建一个 TaskInstance 的子类。然后创 建一个 org.jbpm.taskmgmt.TaskInstanceFactory 实现并在 jbpm.cfg.xml 中配置它通过设置配置 属性 jbpm.task.instance.factory 到完全限定类名。如果你使用一个 TaskInstance 的子类,也要 为子类(使用 hibernate extends="org.jbpm.taskmgmt.exe.TaskInstance")创建 一个 hibernate 的 映射文件。然后在 hibernate.cfg.xml 文件中增加映射文件到映射文件的列表。 12.11. 身份构件 用户、群组和权限的管理通常称为身份管理。jBPM 包含一个可选的身份构件,它能够 很容易地用公司自己的身份数据存储替换。 jBPM 身份管理构件包含组织结构模型的知识。任务分派通常使用组织结构来完成。所 以组织结构模型的前提是,描述用户、群组、系统和他们之间的关系。一般来说,权限和角 色更多地被包含在组织结构模型中。 各种学术研究数次的失败, 验证了没有能够适用于每个 组织的组织结构模型。 jBPM 处理这个问题的方式是通过在流程中定义一个参与者当作真实的参与人。参与者 通过它的叫做 actorId 的 ID 被标识。jBPM 只知道 actorId 而且它们用最大灵活性的 java.lang.Strings 来表示。所以任何的关于组织结构模型和数据结构的内容都是 jBPM 核心引 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 149 页 / 共 199 页
  • 150. 擎范围之外的事。 作为 jBPM 的扩展我们将提供(未来)一个构件去管理简单的用户-角色(user-roles) 模型。这种用与角色间的多对多关系和定义在 J2EE 和 servlet 规范中的是同一模型,而且它 将在新的开发中将是一个起始点。 对更细节的问题感兴趣相关的贡献的话, 大家可以去检出 jboss jbpm jira 上面的问题跟踪。 注意当用户-角色模型用于 servlet、 和 portlet 规范时, ejb 处理任务分派不是足够强大的。 那个模型在用户和角色间是多对多的关系。这个不包括关于组(teams)和涉及流程的用户 的组织架构的信息。 12.11.1. 身份模型 java::serurity Principal Entity Permission +parent User +user Membership +Group Group * * * +children 图 12-3 身份模型类图 黄色的类是下面设计的表达式分派处理程序的相关类。 User 代表一个用户或一个服务。 Group 是任何种用户的群组。 Groups 群组能在组 (team) 、 商务单元(business unit)和整个公司间的关系模型中嵌套。Groups 在层级群组间类型是有 差别的, 例如:发色的 groups. Memberships 代表用户和群间的多对多关系。 一个 membership 用于代表公司是中的一个位置。Membership 的名字能够用于显示用户完整填写在群组中的 角色。 12.11.2. 分派表达式 身份构件伴随着一个任务分派期间计算参与者一个计算表达式的实现。这有一个在流程 定义中使用分派表达式的例子: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 150 页 / 共 199 页
  • 151. <process-definition> ... <task-node name='a'> <task name='laundry'> <assignment expression='previous --> group(hierarchy) --> member(boss)' /> </task> <transition to='b' /> </task-node> ... 分派表达式的语法像下面这样: first-term --> next-term --> next-term --> ... --> next-term where first-term ::= previous | swimlane(swimlane-name) | variable(variable-name) | user(user-name) | group(group-name) and next-term ::= group(group-type) | member(role-name) 12.11.2.1. first-term 表达式从左到右进行解析。first-term 在身份模型中指定一个 User 和 Group。随后的名 词从用户或群组中计算 next-term。 previous 意味着任务已经被分配到当前已经认证的参与者。这意味着参与者执行流程中 的前一步。 swimlane(swimlane-name) 意味着用户或群组从指定的泳道实例上处理。 variable(variable-name)意味着用户或群组从指定的变量实例上处理。变量实例能够包含 java.lang.String,那样的话用户或群组从身份构件处捕获。或者是变量实例包含一个 User 或 Group 对象。 user(user-name)意味着给定的用户被身份构件处理。 group(group-name)意味着给定的群组被身份构件处理。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 151 页 / 共 199 页
  • 152. 12.11.2.2. next-term group(group-type)获得用户的群组。意味着前一名词一定已经有了一个 User,它用给定 的 group-type 在所有的 memberships 中为用户搜索群组。 member(role-name)获得一个群组的执行给定角色的用户。previous terms 必须有一个 Group。这个名词用一个 group 的 membership 搜索用户,找到匹配给定的 role-name 的 membership 的名称。 12.11.3. 移除身份组件 当你想为组织结构信息来使用你自己的数据源时 (例如你自己公司的用户数据库或 ldap 系 统 ) 你 可 以 仅 是 扯 掉 jBPM 身 份 构 件 。 唯 一 的 事 情 需 要 你 做 的 是 确 保 你 从 , hibernate.cfg.xml 文件中删除了这些行。 <mapping resource="org/jbpm/identity/User.hbm.xml"/> <mapping resource="org/jbpm/identity/Group.hbm.xml"/> <mapping resource="org/jbpm/identity/Membership.hbm.xml"/> ExpressionAssignmentHandler 依赖身份构件所以你就不能像原来那样使用它了。那样的 话你想重新使用 ExpressionAssignmentHandler 并且绑定它到你的用户数据存储上, 你可以从 ExpressionAssignmentHandler 上扩展并覆盖 getExpressionSession 方法。 protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 152 页 / 共 199 页
  • 153. 第 13 章 文档管理 这仍然是一个试验性的属性。 为了使用这个属性,你需要取消 hibernate.cfg.xml 文件中的下列行的注释: <mapping resource="org/jbpm/context/exe/variableinstance/JcrNodeInstance.hbm.xml"/> jBPM的文档管理支持基于Java内容资料库(Java Content Repository)。它是一个用来将 文档管理系统集成进入Java的标准java规范。基本的思想是jBPM支持JCR节点像流程变量一 样存储。 为了存储一个节点、会话、资料库(repository)和从节点提取的路径,如下: Session session = node.getSession(); Repository repo = session.getRepository(); Workspace wspace = session.getWorkspace(); // THE NODE REPOSITORY AND WORKSPACE NAME GOT TO CORRESPOND WITH A JBPM SERVICE NAME repository = repo.getDescriptor(Repository.REP_NAME_DESC); workspace = wspace.getName(); path = node.getPath(); 重 要 提 示 : jbpm 上 下 文 件 服 务 的 名 称 必 须 和 资 料 库 名 称 (repository.getDescriptor(Repository.REP_NAME_DESC))相一致。这是为了当节点变量从 jBPM 数据库中加载时存储 jbpm 流程变量的引用和资料库之间相匹配。当 JCR 流程变量被 检索时,在 jbpm 上下文中的每个服务名将匹配被存储的资料库和工作空间名称。在 jbpm 上下文服务和 JCR 会话/资料库名的匹配像下面这样:  如果有一个名叫“jcr”的 jbpm 上下文服务,那么它将被处理  一个服务名同资料库名配对相等  一个以资料库名起始、 以工作空间名称结尾配对的服务并且通过使用资料库名的服 务处理配置。 这个属性的典型用例是文档审批流程。一个文档需要被审批并更新。那个文档(例如, word 文档),能够被存储在一个 JCR 内容资料库节点(JCR-content-repository-node)里。 这个节点包含所有文档的版本号。 以便在流程的后面部分, 人们仍然能查阅文档的历史版本。 这个属性只使用 Jackrabbit 测试过。关于库依赖性的更多内容请参考 JCR 实现文档。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 153 页 / 共 199 页
  • 154. 第 14 章 定时器 本章描述了 jBPM 如何和定时器工作一起工作。 在流程的事件上,定时器能够被创建。当一个定时器到期时,一个动作能够被执行或一 个事务能够被处理。 14.1. 定时器 最简单的指定一个定时器方式是通过在节点上增加一个定时器元素。 <state name='catch crooks'> <timer name='reminder' duedate='3 business hours' repeat='10 business minutes' transition='time-out-transition' > <action class='the-remainder-action-class-name' /> </timer> </state> 在一个节点上指定的定时器,在离开节点后不会被执行。转换和动作两者都是可选的。 当定时器被执行时,下列的事件会按顺序发生:  一个定时器类型的事件被触发  如果动作被指定了,那么动作将被执行  如果转换被指定了,那么信号将通过给定的转换发往恢复执行。 每个定时器必须有唯一的名称。如果在元素中没有指定名称,那么将使用节点名称作为 定时器的名称。 定时器动作能够支持任何的动作元素,例如动作(action)或脚本(script)。 定时器被动作(actions)创建或撤消。这两个动作元素创建定时器(create-timer)和撤 消定时器(cancel-timer)。实际上,上面所列的定时器元恰好是短表示法。create-timer 动 作在 node-enter 上,cancel-timer 动作在 node-leave 上。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 154 页 / 共 199 页
  • 155. 14.2. 调度部署 流程执行创建并撤消定时器。定时器存储在一个定时器仓库中。一个独立的定时器执行 器必须检查定时器仓库并且当他们到期后执行这个定时器。 调度服务 定时器运行器 调度时器 执行到期的定时器 撤消定时器 定时器运行器 流程执行 定时器仓库 图 14-1 定时器构件概况 下列的类图显示了调度器部署中所涉及到的类。 SchedulerService 和 TimerExecutor 接口 被指定为使定时器执行的机制是可插拨的。 Runnable 流程执行 SchedulerMain SchedulerInstance TimerRunner SchudulerService TimerExector SchedulerServlet SchudulerServiceImpl TimerExectorImpl JBPM_TIMER数 据 库 表 图 14-2 调度器类概况 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 155 页 / 共 199 页
  • 156. 第 15 章 异步连续 15.1. 概念 jBPM 基于面向图的程序设计(GOP)。基本上,GOP 指定一个能够处理并发执行路径 的简单状态机。但在执行里算法被指定在 GOP 中,所有的状态转换在客户端线程的单个操 作中完成。如果你不熟悉“第 4 章 面向图的程序设计”中定义的执行算法,请首先读下。 缺省情况下, 这个在客户端线程中执行状态转换是一个好的方案, 因为它天生适合服务端事 务。在一个事务中流程执行从一个等待状态移动到另一个等待状态。 但在某些情况下,一个开发人员可能想在流程定义中微调这个事务划分。在 jPDL 中, 它可能指定流程执行应该使用属性 async="true"来继续异步执行。async="true"可以在所有节 点类型和所有动作类型中进行指定。 15.2. 示例 正常的情况下,节点总是在令牌(token)进入这个节点后才被执行。所以这个节点是 在 客 户 端 线 程 中 被 执 行 的 。 我 们 将 通 过 看 两 个 例 子 探 讨 下 异 步 连 续 ( asynchronous continuations)。第一个例子是有三个节点的流程的一部分。节点'a'是一个等待状态、节点'b' 是一个自动步骤而节点'c'也是一个等待状态。这个流程不包含任何的异步行为,它在下面图 中被表示。 第一个框中, 显示开始情形。 令牌指向节点'a', 意味着执行路径正等待一个外部的触发。 那个触发必须通过给令牌发送一个信号(signal)给定。当信号到达时,令牌将通过节点 a 传递到节点 b。在令牌到达节点 b 后,节点 b 被执行。再调用节点 b 是一个非等待状态的自 动步骤(例如发送邮件) 。所以第二个框是节点 b 被执行时的一个处理快照。既然节点 b 是 流程中的一个自动步骤,那么节点 b 的执行将包含通过到节点 c 的转换的令牌传播。节点 c 是一个等待状态所以第三个框显示了信号方法返回后的最终情形。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 156 页 / 共 199 页
  • 157. 图 15-1 示例 1:无异步连续的流程 尽管持久化在 jBPM 中并非强制的,但最普遍的情况在事务中调用信号。让我们看下事 务的更新。首先,令牌被更新指向节点 c。这些更新通过 hibernate 作为 JDBC 连接上的 GraphSession.saveProcessInstance 的结果被生成。其次如果自动动作访问并更新一些资源, 那些事务更新应该被组合或成为同一事务的部分。 现在,我们来看下第二个例子, 第二个例子是第一的变体而且在节点 b 中引入了一个 异步连续。节点 a 和节点 c 同第一个例子相同,即他们还是等待状态。在 jPDL 中,节点通 过设置属性 async="true"被标记为异步的。 增加 async="true"到节点 b 的结果是流程执行将被分成两个部分。第一个部分将执行流 程到达 b 点所在处,节点 b 将被执行。第二部分将执行节点 b 而且执行将停止在等待状态 c 上。 事务此后将分裂成两个独立的事务。一个事务为每个部分(One transaction for each part)。当它在第一个事务中要求一个外部触发(Token.signal 方法的调用)离开节点 a 时, jBPM 将自动触发并执行第二个事务。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 157 页 / 共 199 页
  • 158. 图 15-2 示例 2:异步连续流程 对于动作,原理相似。动作使用属性 async="true" 标记异步的,被执行流程的外部线程 执行。如果持久化配置(这是缺省的) ,动作将在一个独立的事务中执行。 在 jBPM 中,异步连续使用异步消息系统被实现。当流程执行到达应该异步执行的一点 时,产生一个命令消息并发送它到命令执行器。命令执行器是一个独立的构件,依赖于消息 收据,它恢复它获得的挂起的流程执行。 jBPM 能够被配置使用 JMS provider 或内置的异步消息系统。内置的消息系统功能很有 限,但是它允许这个特性能够在 JMS 不可用的环境中被支持。 15.3.工作执行器 工作(job)执行器是恢复异步流程执行的构件。它等待工作消息从异步消息系统到达 并执行他们。这两个用于异步连续的工作消息是 ExecuteNodeJob 和 ExecuteActionJob。 工作消息被流程执行产生。在流程执行期间,每个节点或执行不得不被异步执行,一个 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 158 页 / 共 199 页
  • 159. Job(POJO)将被派出到 MessageService。消息服务同 JbpmContext 关联而且它只收集所有 不得不被发送的消息。 消息将作为 JbpmContext.close()的部分被发送。那个方法层叠 close()调用到所有的关联 服务。这个真正的服务能够在 jbpm.cfg.xml 中配置。Services、DbMessageService 中的一个 被缺省的配置而且将通知新工作消息可用的那个工作执行器。 图执行机制使用 MessageServiceFactory 和 MessageService 接口发送消息,这个将使异步 消息可配置(也在 jbpm.cfg.xml 中) 。在 Java EE 环境里,DbMessageService 能够使用 JmsMessageService 替换来平衡应用服务器的能力。 这里是执行器如何工作简单地描述: 工作(Jobs)记录在 数据库中。Jobs 是对象而且也能够被执行。定时器和异步消息都 是 jobs。对于异步消息,dueDate 在他们被插入时简单地设置成当前时间。工作执行器必须 执行 jobs。这个以 2 步来完成: 1) 工作执行器线程必须获得一个工作(job) 2) 获得工作(job)的线程必须执行它 获得工作和执行工作以两个独立的事务完成。 一个线程通过放它的名称进入工作的所有 者域获得工作。每个线程均有唯一的基于 ip 地址和序号的名称。Hibernate 的乐观锁在 Job 对象上被允许。所以两个线程试图获得一个并发的工作,它们中的一个将得到 StaleObjectException 异常并回滚。只有第一个将成功。成功获得工作的线程负责在独立的事 务中执行它。 一个线程在获得和工作执行期间将死掉。 为了在那种情形下进行清理,每工作执行器有 一个锁监控线程检查锁时间。被锁的大于 30 分钟的工作将被解锁以至于他们能够被另一个 工作执行。 为了使 hibernate 的乐观锁正确工作必需的隔离级别应该设置到 REPEATABLE_READ。 隔离级别将保证 update JBPM_JOB job set job.version = 2 job.lockOwner = '192.168.1.3:2' where job.version = 1 在一个恰好竞争的事务中只有一行被更新。 不可重复读(Non-Repeatable Reads)意味着下列的非正常会发生:一个事务重复读取 它前面已经读过的数据然而发现数据已经被另一个事务改变了, 因为那个事务先前读取的数 据已经被另一个提交了。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 159 页 / 共 199 页
  • 160. 不可重复读是乐观锁的一个问题因此隔离级别 READ_COMMITTED 并不足够,它导致 允许不可重复读的发生。所以如果你配置了多个工作执行器线程那可重复读 (REPEATABLE_READ)是必须的。 15.4. jBPM 内置异步消息 当使用 jBPM 内置的异步消息时,工作消息将在持久化他们到数据库前被发送。这个消 息持久化能够在和 jBPM 流程更新一样的相同的 transaction/JDBC 连接中完成。 工作消息将被存储在 JBPM_JOB 表中。 POJO 命令执行器(org.jbpm.msg.command.CommandExecutor)将从数据库表中读取消 息并执行他们。所以典型的 POJO 命令执行器看起来像这样: 1)读下个命令消息 2)执行命令消息 3)删除命令消息 如果命令执行消息失败,那么事务将回滚。在那之后,一个新的事务将开始增加错误消 息到数据库中。命令执行器过滤掉包含一个异常的所有消息。 • 从队列中移除第一个命令消息 (过滤掉发生异常的命令消息) • 执行命令,它意味着继续流程执行 回滚 • 给消息增加异常 回滚 没有异常的消息将保持在队列 中,所以稍后它们将重试 图 15-3 POJO 命令执行器事务 如果因为某些原因或其他的,事务增加异常到命令消息将会失败,它也会回滚。那样的 话,队列中剩下的没有异常的消息稍后将会重试。 限制:jBPM 内置的异常消息系统不支持多节点锁(multinode locking)。所以你不能部 署 POJO 命令器执行多次及让他们配置去使用同相同的数据库。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 160 页 / 共 199 页
  • 161. 15.5. 异步架构 JMS 异步继续属性,打开了 jBPM 使用场景的新世界。通常,jBPM 用于业务流程建模,它 现在能够用于技术技术更高的远景上。 想象下你有一个应用要相当多的异步处理。 那样通常情况下很难建立将软件的消息产生 和消息消费片绑定在一起。使用 jBPM 现在建立一个异步架构的整体画面成为可能,让你的 所有的代码置入 POJO 中而且可以在整个流程文件中增加事务划分。jBPM 将现在照顾到绑 定发送端到接收端而不需要你自己写所有的 JMS 或 MDB 代码。 15.6. 属性用法 将要做的:增加多队列有支持。以便为每个当作异步标记的节点或动作指定一个队列变 成可能。它也将循环地为一系列的队列产生大量的消息。因为所有的这一切牡 JMS 和内置 的消息系统应该是可配置的,所以这需要一些如何来做所有这些配置的想法。流程定义不应 该必须依赖这两个可能的实现中的任何一个。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 161 页 / 共 199 页
  • 162. 第 16 章 商务日历 本章描述了 jBPM 的商务日历。商务日历知道关于业务小时和用于任务和定时器的到期 的计算。 商务日历能够通过在基准日期(base datea)上加或减去一个持续时间(duration to)来 计算到期日期。如果基准日期被忽略,那么会使用当前('current')日期。 16.1. 到期日期 正像提到的那样,到期日期是由持续时间(duration)和基准日期(base date)。如果这 个基准日期被忽略,持续时间是相对于计算到期日期时的那个日期(时间) 。格式是: duedate ::= [<basedate> +/-] <duration> 16.1.1. 持续时间 持续时间被指定用绝对时(absolute)或工作时(business) 。让我们看下语法: A duration is specified in absolute or in business hours. Let's look at the syntax: duration ::= <quantity> [business] <unit> 里面的 <quantity> 是一个可以使用 Double.parseDouble(quantity)来解析的文本。<unit> 是{second, seconds, minute, minutes, hour, hours, day, days, week, weeks, month, months, year, years}中的一个。然后增加一个可选的指示 business 意味着只有工作时才能被放入持续时间 的数量中。如果没有指示 business,持续时间将作为一个绝对的时间周期来进行解释。 16.1.2. 基准日期 持续时间(duration)被指定绝对(absolute)时或工作(business)时。让我们看下语 法: basedate ::= <EL> +/- 其中的<EL>是任何的 JAVA 表达式语言表达式, 可以解析成一个 JAVA Date 或 Calendar 对象。引用其他的对象类型变量,即使一个日期格式的 String 类型的变量(如'2036-02-12') , 也会抛出一个 JbpmException 异常。 注意:这个基准日期(basedate)支持一个普通定时器、任务上提醒和任务内部定时器 的 duedate 属性。但绝不支持这些元素内部的 repeate 属性。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 162 页 / 共 199 页
  • 163. 16.1.3. 示例 下列是所有可能用法的例子: <timer name="daysBeforeHoliday" duedate="5 business days">...</timer> <timer name="pensionDate" duedate="#{dateOfBirth} + 65 years" >...</timer> <timer name="pensionReminder" duedate="#{dateOfPension} - 1 year" >...</timer> <timer name="fireWorks" duedate="#{chineseNewYear} repeat="1 year" >...</timer> <reminder name="hitBoss" duedate="#{payRaiseDay} + 3 days" repeat="1 week" /> 16.2. 日历配置 目录 org/jbpm/calendar 下的 jbpm.business.calendar.properties 文件指定了哪些是工作时。 配置文件可以定制而且修改过的复本能够放置在类路径的根下。 这是在 jbpm.business.calendar.properties 文件中的一个发货的工作时清单的例子: hour.format=HH:mm #weekday ::= [<daypart> [& <daypart>]*] #daypart ::= <start-hour>-<to-hour> #start-hour and to-hour must be in the hour.format #dayparts have to be ordered weekday.monday= 9:00-12:00 & 12:30-17:00 weekday.tuesday= 9:00-12:00 & 12:30-17:00 weekday.wednesday= 9:00-12:00 & 12:30-17:00 weekday.thursday= 9:00-12:00 & 12:30-17:00 weekday.friday= 9:00-12:00 & 12:30-17:00 weekday.saturday= weekday.sunday= day.format=dd/MM/yyyy # holiday syntax: <holiday> # holiday period syntax: <start-day>-<end-day> # below are the belgian official holidays holiday.1= 01/01/2005 # nieuwjaar holiday.2= 27/3/2005 # pasen holiday.3= 28/3/2005 # paasmaandag holiday.4= 1/5/2005 # feest van de arbeid holiday.5= 5/5/2005 # hemelvaart holiday.6= 15/5/2005 # pinksteren cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 163 页 / 共 199 页
  • 164. holiday.7= 16/5/2005 # pinkstermaandag holiday.8= 21/7/2005 # my birthday holiday.9= 15/8/2005 # moederkesdag holiday.10= 1/11/2005 # allerheiligen holiday.11= 11/11/2005 # wapenstilstand holiday.12= 25/12/2005 # kerstmis business.day.expressed.in.hours= 8 business.week.expressed.in.hours= 40 business.month.expressed.in.business.days= 21 business.year.expressed.in.business.days= 220 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 164 页 / 共 199 页
  • 165. 第 17 章 邮件支持 本章描述了 jBPM jPDL 中随包发行的邮件支持。 17.1. jPDL 中的邮件 从流程中发送邮件有四种指定的方式。 17.1.1. 邮件动作 邮件动作能够用来发送邮件而不是在流程图中显示一个节点。 允许你在在流程的任何地方来指定动作,你可以像这样指定一个邮件动作: <mail actors="#{president}" subject="readmylips" text="nomoretaxes" /> Subject 和 text 属性也能作为一个元素来指定,像这样: <mail actors="#{president}" > <subject>readmylips</subject> <text>nomoretaxes</text> </mail> 每个域都可以包含 JSF 那样的表达式。例如: <mail to='#{initiator}' subject='websale' text='your websale of #{quantity} #{item} was approved' /> 更多的关于表达式的信息,查看21.3 表达式部分。 有两个属性用来指定收集人:actors和to。to属性应该解析成一个分号分隔的邮件地址列 表。actors属性应该解析成一个分号分隔的actorId的列表。那些actorId将使用地址解析来解析 成邮件地址。 <mail to='admin@mycompany.com' subject='urgent' text='the mailserver is down :-)' /> 更多的关于如何指定收件人的信息,查看17.3 指定收件人部分。 邮件可以定义成模板而且在流程中你可以重写模板的属性,如: <mail template='sillystatement' actors="#{president}" /> 更多的关于模板的信息,查看17.4 邮件模板部分。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 165 页 / 共 199 页
  • 166. 17.1.2. 邮件节点 就跟邮件动作(mail actions)一样,邮件发送也能被建模成节点。那样的话,运行时行 为是相同的,但是邮件将在流程图中显示成一个节点。 邮件节点所支持的属性和元素同邮件动作完全相同。 <mail-node name="send email" to="#{president}" subject="readmylips" text="nomoretaxes"> <transition to="the next node" /> </mail-node> 邮件节点完全应该有一个离开转换。 17.1.3. 任务分派邮件 当任务获得被分配的参与者时通知邮件能够发送。在一个任务上使用 notify="yes"属性, 如: <task-node name='a'> <task name='laundry' swimlane="grandma" notify='yes' /> <transition to='b' /> </task-node> 设置notify为yes、true或on将导致jBPM给分配到任务上的参与者发送邮件。邮件是基于 模板(查看17.4 邮件模板部分)并且包含一个到web应用的任务页面的链接。 17.1.4. 任务提醒邮件 同使用分派(assignments)一样,邮件(emails)能够作为任务提醒被发送。jPDL 中 的 reminder 元素基于定时器。最常用的属性是 duedate 和 repeat。唯一的不同是没有动作 (action)指定。 <task-node name='a'> <task name='laundry' swimlane="grandma" notify='yes'> <reminder duedate="2 business days" repeat="2 business hours"/> </task> <transition to='b' /> </task-node> 17.2. 邮件表达式 域to、recipients、subject和text能够被包含像JSF的表达式。更多的关于表达式的信息, 查看21.3 表达式部分。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 166 页 / 共 199 页
  • 167. 表达式中的变量:swimlanes、 process 变量、transient 变量 beans 被配置 jbpm.cfg.xml 中。 这些表达式能够同本章后面解释的地址解析进行组合。 例如:假设在你的流程中有一个 swimlane叫president,然后看下面的邮件规范: <mail actors="#{president}" subject="readmylips" text="nomoretaxes" /> 那将为特定的流程执行发送邮件到扮演 president 的那个人。 17.3. 指定收件人 17.3.1. 多个收件人 在 actors 和 to 域中,多个收件人可以使用分号(;)或冒号(:)来进行分隔。 17.3.2. 密送邮件 有时除正常的收件人外你还想发送邮件到一个 BCC 目标。当前,有两个支持的方法: 第一你可以在流程定义中指定一个 bccActors 或 bcc 属性(根据 actors 和 to)。 <mail to='#{initiator}' bcc='bcc@mycompany.com' subject='websale' text='your websale of #{quantity} #{item} was approved' /> 第二个方法是总是发送一个 BCC 邮件到你在中心配置(jbpm.cfg.xml)中的一个属性中 配置的某个位置上: <jbpm-configuration> ... <string name="jbpm.mail.bcc.address" value="bcc@mycompany.com" /> </jbpm-configuration> 17.3.3. 地址解析 在所有的 jBPM 中,actors 被 actorId's 引用。这是一个作为流程参与人标识符的字符串。 一个地址解析翻译 actorId's 成为邮件地址。 如果你想应用地址解析使用属性 actors 而如果你要直接指定邮件地址而不想应用地址 解析使用属性 to。 一个地址解析器应该实现下列的接口: public interface AddressResolver extends Serializable { Object resolveAddress(String actorId); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 167 页 / 共 199 页
  • 168. } 一个地址解析器应该返回 3 个类型中的 1 个:字符串(String) 、字符串集合(Collection of String)或字符串数组(array of String)。 所有字符串应该为给定的 actorId 代表邮件地址。 地址解析器实现应该是使用名字 jbpm.mail.address.resolver 配置在 jbpm.cfg.xml 中的 bean。如: <jbpm-configuration> ... <bean name='jbpm.mail.address.resolver' class='org.jbpm.identity.mail.IdentityAddressResolver' singleton='true' /> </jbpm-configuration> jBPM的身份构件包括一个地址解析器。那个地址解析器将查找给定actorId的User。如 果用户存在,那个用户的邮件被返回,否则为null。更多关于身份构件信息能够在12.11 身 份构件中找到。 17.4. 邮件模板 代替在 processdefinition.xml 中指定邮件,邮件能够指定模板文件中。当一个模板被使 用时,processdefinition.xml 文件中的每个域仍然能够被覆盖。邮件模板应该在 XML 文件中 像这样来指定: <mail-templates> <variable name="BaseTaskListURL" value="http://localhost:8080/jbpm/task?id=" /> <mail-template name='task-assign'> <actors>#{taskInstance.actorId}</actors> <subject>Task '#{taskInstance.name}'</subject> <text><![CDATA[Hi, Task '#{taskInstance.name}' has been assigned to you. Go for it: #{BaseTaskListURL}#{taskInstance.id} Thanks. ---powered by JBoss jBPM---]]></text> </mail-template> <mail-template name='task-reminder'> <actors>#{taskInstance.actorId}</actors> <subject>Task '#{taskInstance.name}' !</subject> <text><![CDATA[Hey, Don't forget about #{BaseTaskListURL}#{taskInstance.id} Get going ! ---powered by JBoss jBPM---]]></text> </mail-template> cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 168 页 / 共 199 页
  • 169. </mail-templates> 像你在这个例子(BaseTaskListURL)中看到的一样,extra 变量能够定义在邮件模板中, 它在表达式中是有效的。 包含模板的资源应该在 jbpm.cfg.xml 中像这样进行配置: <jbpm-configuration> ... <string name="resource.mail.templates" value="jbpm.mail.templates.xml" /> </jbpm-configuration> 17.5. 邮件服务器配置 最简单的配置邮件服务器的方法在 jbpm.cfg.xml 文件中使用 jbpm.mail.smtp.host 这个配 置属性,如: <jbpm-configuration> ... <string name="jbpm.mail.smtp.host" value="localhost" /> </jbpm-configuration> 作为选择,当更多的属性需要被指定时,应该用关键字' resource.mail.properties '给定资源引 用的属性文件,如: <jbpm-configuration> ... <string name='resource.mail.properties' value='jbpm.mail.properties' /> </jbpm-configuration> 17.6. From 地址配置 jPDL 邮件中 From 地址的缺省值使用的是 jbpm@noreply。 邮件的 from 地址可以在 jBPM 的配置文件 jbpm.xfg.xml 中使用关键字'jbpm.mail.from.address'来配置,如: <jbpm-configuration> ... <string name='jbpm.mail.from.address' value='jbpm@yourcompany.com' /> </jbpm-configuration> 17.7. 自定义邮件支持 jBPM 中 所 有 的 邮 件 支 持 是 以 一 个 类 为 中 央 的 : org.jbpm.mail.Mail , 这 是 一 个 ActionHandler 实现。无论何时邮件在流程 xml 中指定,都将代理到邮件类。它可能继承自 Mail 类然后再为你的特殊需求定制某些功能。 为了配置你的类使用邮件代理, jbpm.cfg.xml在 文件中像这样来指定'jbpm.mail.class.name'配置字符串: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 169 页 / 共 199 页
  • 170. <jbpm-configuration> ... <string name='jbpm.mail.class.name' value='com.your.specific.CustomMail' /> </jbpm-configuration> 定制的 mail 类在解析期间被读取并且动作将在引用配置了(或缺省的)邮件类名的流 程中被配置。所以如果你改变这个属性,所有已经部署的流程将仍然引用旧的邮件名。但他 们将能够通过使用一条简单的 update 语句很容易地被更新到 jbpm 数据库。 17.8. 邮件服务器 如果你需要一个易于安装的邮件服务器,那么就检出JBossMail Server 或 Apache James 吧。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 170 页 / 共 199 页
  • 171. 第 18 章 Web Services 支持 本章描述了 jBPM jPDL 中开箱即支持的 web services。 18.1. 调用 jPDL Web Services jPDL 中有一个 WS 子项目。这个子项目的目的是允许 jPDL 引擎作为一个 web sevice 被调用。 当前的实现允许你从流程定义启动流程实例并提供一个运行实例触发器来继续他们 的执行(execution)。它还不太精细,但还是能够作为实现更高级用途的起始。 持续的调整是因为可能有更多伴随 web services 方面的内容出现。 18.1.1. 部署 当前,在发布中没有包括可部署的成品。幸运的是建立自己的部署是非常简单的。从 jbpm仓库中检出jbpm.3 的基准代码,导航到jpdl/ws子目录,然后调用ant构建它。你将会在 目标目录中发现jbpm-jpdl-ws.war包。使用JBoss AS,它简单到就是在你的服务器实例的部署 文 件 中 放 入 这 个 文 件 。 如 果 你 使 用 Java 6 那 么 你 需 要 使 用 一 些 库 。 更 多 的 信 息 查 看http://jbws.dyndns.org/mediawiki/index.php?title=Install_JBossWS。 18.1.2. 测试部署 一旦 web services 模块被正确部署后你就可以使用像 SoapUI 这样的工具来测试它。导 入生成的 WSDL,发送一些消息然后看看会发生什么。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 171 页 / 共 199 页
  • 172. 第 19 章 日志 日志的目标是保持追踪流程执行的历史。在运行时流程执行数据的改变、所有 数据 (delta's)被存储在日志中。 本章所涉及的流程日志,不要同软件日志混淆。软件日志跟踪软件程序的执行(通常是 为了除错) 。流程日志跟踪流程实例的执行。 流程日志信息有各种用例。最明示的是通过流程执行的参与人查询流程历史。 另一个用例是业务活动监控(Business Activity Monitoring (BAM)) 。BAM 将查询或分 析流程执行的日志来找出关于业务过程的有用的统计信息。 例如: 流程的每一步平均花费多 少时间?流程中哪里有瓶颈?……这样的信息是一个组织实现真正的流程管理的关键。 真正 的业务流程管理是关于组织如何管理他们的流程、 如何通过通过信息技术来支持以及如何在 一个迭代流程改善其他的方面。 再一个用途是撤消(undo)功能。 流程日志能够用于实现撤消。既然日志包含运行时 信息的数据(delta's) ,那样日志就能够反向将流程带回到前一个状态。[ Since the logs contain the delta's of the runtime information, the logs can be played in reverse order to bring the process back into a previous state.] 19.1. 创建日志 当流程执行运行时日志被 jBPM 产生。但是用户也能插入流程日志。一个日志项是一个 继 承 自 org.jbpm.logging.log.ProcessLog 的 java 对 象 。 流 程 日 志 项 被 增 加 到 一 个 LoggingInstance 上。LoggingInstance 是 ProcessInstance 的可选的扩展。 不同日志种类被 jBPM 生成: 图执行日志 (graph execution logs) 上 文 (context logs) 、 下 和任务管理日志(and task management logs)。更多的关于那些日志所包含的特定的数据, 参考 javadocs 文档。一个好的开始点是类 org.jbpm.logging.log.ProcessLog ,因为从那个类 中你可以向下导航这个继承树。 LoggingInstance 将收集日志项。当 ProcessInstance 被存储时,所有 LoggingInstance 中 的日志将被刷新到数据库中。为了避免每一个事务都要从数据库中进行检索 ProcessInstance 日志域(logs-field)并没有同 hibernate 进行映射。 每一个 ProcessLog 在执行(Token)路 径的上下文中产生, 随后 ProcessLog 引用那个 token。个 Token 也当作 Token 中 ProcessLog 这 索引的顺序索引(idnex-sequence)生成器。这个对于日志检索是很重要的。那样,随后的 事务生成的日志将有一个连续的顺序号。(喔,那里有许多的序号噢 :-s)。 下列是增加流程日志的 API 方法。 public class LoggingInstance extends ModuleInstance { ... public void addLog(ProcessLog processLog) {...} ... } 日志信息的 UML 图看起来像这样: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 172 页 / 共 199 页
  • 173. 图 19.1 jBPM 日志信息类图 CompositeLog 是一个特殊种类的日志项。它作为若干子日志的父日志,因此这是创建 日志中层次结构的手段。下面是插入日志的 API。 public class LoggingInstance extends ModuleInstance { ... public void startCompositeLog(CompositeLog compositeLog) {...} public void endCompositeLog() {...} ... } CompositeLogs 应该总是放在 try-finally 块中进行调用以保证日志层次结构是一致的。 例如: startCompositeLog(new MyCompositeLog()); try { ... } finally { endCompositeLog(); } 19.2. 日志配置 日志部署在哪并不重要, jbpm.cfg.xml 配置文件中的 jbpm-context 部分移除日志行就 在 足够了。 <service name='logging' factory='org.jbpm.logging.db.DbLoggingServiceFactory' /> 万一你想过滤日志,你需要写一个自定义的 DbLoggingService 子类 LoggingService 的实现。 你还需要创建一个定制的日志 ServiceFactory 并在 factory 属性中指定它。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 173 页 / 共 199 页
  • 174. 19.3. 日志检索 正像前面讲的,日志不能通过定位 LoggingInstance 到它的日志从数据库中被检索。取 而代之的是,流程实例的日志总可以从数据库中被查询。LoggingSession 有两个方法来实现 这个目标: 第一个方法为流程实例检索所有的日志。日志将通过 token 分组 MAP。这个 map 将同 流程实例中的每个 Token 的 ProcessLogs 列表相关联。列表将以相同的顺序包含 ProcessLogs 当他们创建时。 public class LoggingSession { ... public Map findLogsByProcessInstance(long processInstanceId) {...} ... } 第二方法为特定的 Token 检索日志。返回的列表将以相同的顺序包含 ProcessLogs 当他 们创建时。 public class LoggingSession { public List findLogsByToken(long tokenId) {...} ... } 19.4.数据仓库 有时你可能想应用数据仓库技术到 jbpm 流程日志上。数据仓库意味着你将创建一个独 立的数据库来包含为各种目标而使用的流程日志。 有多种为流程日志信息创建数据仓库的原因。 有时它可能是一个在线的产品数据库中非 重载荷的查询。 其他情况下它可能做一些扩展性的分析。数据库仓库甚至可能修改数据库模 式以优化它。 本节,我们只想计划下 jBPM 上下文中的数据库仓库技术。这个目标太分散,限制了可 以包含在 jBPM 中能覆盖所有需求的一个通用的解决方法。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 174 页 / 共 199 页
  • 175. 第 20 章 业务流程仿真 jBPM 包含一个仿真组件,这个是 jBPM 业务流程仿真的杠杆。这个仿真组件文档还在 开发中。 关于仿真的属性和如何使用这个仿真的教程的信息在下面这个地址: http://www.camunda.com/jbpm_simulation/introduction.html。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 175 页 / 共 199 页
  • 176. 第 21 章 jBPM 流程定义语言(JPDL) JPDL 指定一个 XML 模式和机制来打包所有的流程定义相关的文件进入一个流程包中。 21.1. 流程包 流程包是一个 zip 文件。流程包中的中心文件是 processdefinition.xml。在那个文件中的 主要的信息是流程图。processdefinition.xml 也包含关于动作和任务的信息。流程包也能包含 其他的相关文件,例如类、任务的 ui 表单(ui-forms)等等。 21.1.1. 部署流程包 有三种方法来完成流程包的部署: 使用流程设计器工具、 使用一个 ant 任务或通过编程。 使用流程设计器工具部署流程包在新手工具箱中被支持。 在流程包文件夹上点击右键找 到“Deploy process archive”选项。新手工具箱服务器上包含 jBPM web 应用,它有一个叫 ProcessUploadServlet 的上传流程包的 servlet。这个 servlet 能够上传流程包和部署他们到缺 省配置的 jBPM 实例上。 使用下面的方式使用 ant 任务完成部署流程包: <target name="deploy.par"> <taskdef name="deploypar" classname="org.jbpm.ant.DeployProcessTask"> <classpath --make sure the jbpm-[version].jar is in this classpath--/> </taskdef> <deploypar par="build/myprocess.par" /> </target> 为了立即部署多个流程包,使用嵌套的 fileset 元素。这个文件属性本身是可选的。Ant 任务的其他属性是:  cfg: cfg 可选, 缺省值是'hibernate.cfg.xml'。hibernate 配置文件包含到数据库的 jdbc 连接和映射文件。  properties:properties 可选,它覆盖 hibernate.cfg.xml 文件中发现的*all* 属性。  createschema:如果设置为 true,那么 jbpm 数据库模式将流程部署前创建。 流程包也能使用类 org.jbpm.jpdl.par.ProcessArchiveDeployer 通过编程部署。 21.1.2. 流程版本控制 当我们已经部署了一个流程定义,许多执行还没有完成然而有一个流程定义的新版本将 要部署时会发生什么呢? 流程实例总是执行那些已经启动的流程定义。 jBPM 允许同名的多个流程定义共存于 但 数据库中。所以典型的是,一个流程实例那时会以最新的可用版本启动然后它将以那个相同 的流程定义保持执行完成它的生命周期。当一个较新的版本被部署时, 新创建的实例将以最 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 176 页 / 共 199 页
  • 177. 新的版本启动,而旧的流程实例按照旧的流程定义继续执行。 如果流程包括引用的Java类,它有两种方式在jBPM运行时环境上生效:通过确保这些 类对jBPM的类加载器可见。这通过意味着你可以放你的委托类在一个.jar文件中紧接着 jbpm-[version].jar包。那样的话,所有流程定义将看到相同的类文件。Java类也可以包括在 流程包中。当你包括委托类在流程包中(那么他们对jbpm类加载器不可见)时,jBPM将在 你的流程定义中翻译 (version)这些类。更多的关于流程加载的信息可能在21.2 委托部分找 到。 当一个流程包部署完时,它在 jBPM 数据库中创建一个流程定义。流程定义可以根据流 程定义名被版本化。当命名的流程包部署完时,部署者将分配一个版本号。为了分配这个号 码,部署者将查阅同名的流程定义的最高版本号并增加 1。未命名的流程定义总是分配版本 号-1。 21.1.3. 变更已部署流程定义 在流程定义已经部署进 jBPM 数据库后再变更它会有许多的潜在的陷阱。因此,非常不 鼓励这么做。 实际上,流程定义有一个完整的多种可能变化。那些流程定义中某些是无害的,但一些 其他的变化的影响远远超越预期和期望的那样。 所以请考虑迁移流程实例为一个新的定义来越过这个方案。 万一你考虑它,这些是你要考虑到的点: 使用 hibernate 更新: 你可以加载流程定义、改变它并用 hibernate 的会话保存它。 Hibernate 的会话可以使用方法 JbpmContext.getSession()访问。 二级缓存:在你已经更新了一个存在的流程定义后流程定义将需要从二级缓存中移除。 也可以查看7.9 二级缓存部分。 21.1.4. 迁移流程实例 一个变更流程定义候选的方案可能是把这个执行转换成一个新的流程定义。 请注意这并 非琐碎的因为长期的业务流程的天性。 当前,这是一个试验区域所以因为这个仍然没有更多 随包的支持。 因为你知道在流程定义数据和流程实例数据 (运行时数据)有明确区别。使用这个方案, 你在 jBPM 数据库中创建一个独立新流程定义(通过例如部署一个新版本的相同流程)。然 后运行时信息被转换成新流程定义。 这可能会牵涉到转换导致旧流程中的令牌指向的节点在 新版本中已经被移除了。 所以在数据库中只有新数据被创建了。但是一个流程的执行通过两 个流程实例对象来传播。 对于这个工具和统计计算可能变得有点难以理解。当资源允许我们 时,我们将来将为这个属性增加支持。例如可能增加一个从一个流程实例到它的前辈指示。 21.1.5. 流程转换 。可以协助你把 jBPM 2.0 流程包转换成 jBPM 3.0 兼容的流程包的转换类已经可用了。 建立一个输出目录来保存转换后的流程包。从 jBPM 3.0 发布的构建目录中键入下列的命令 行: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 177 页 / 共 199 页
  • 178. java -jar converter.jar indirectory outdirectory "indirectory"替换成 jBPM 2.0 流程包驻留的目录,"outdirectory"替换成创建来存储新转 换的流程包的输出目录。 21.2.委托 委托是在流程执行中用于包括用户定制代码机制。 21.2.1. jBPM 类加载器 jBPM 加载器是加载 jBPM 类的类加载器。意思是,在类加载器的类路径中有库 jbpm-3.x.jar。为了让类对 jBPM 类加载器可视,放他们在一个 jar 文件中并放这个 jar 文件除 在 jbpm-3.x.jar 的旁边。例如在这个 web 应用的 WEB-INF/lib 文件夹下。 21.2.2. 流程类加载器 委托类使用它们各自的流程定义的流程类加载器被加载。流程类加载器是一个把 jBPM 类加载器当作父亲的类加载器。 流程类加载器增加一个特定流程定义的所有类。 你可以增加 类到一个流程定义通过在流程包里把他们放在/classes 目录下。注意这仅仅是当你想版本化 增加到流程定义中的类时才有用的,如果没有必要进行版本化,更加高效的是使用 jBPM 的 类加载器。 如果资源名没有以斜线(/)开头,资源从流程包的/classess 目录中加载。如果你想加载 classes 目录外面的类,那么使用双斜线(//)开头。例如加载定位在流程包文件根上的 processdefinition.xml 旁 边 的 资 源 data.xml , 你 可 以 用 clazz.getResource("//data.xml") 或 lassLoader.getResourceAsStream("//data.xml")或者是那些变体中的任何一个。 21.2.3. 委托配置 委托类包含从流程执行里调用的用户代码。最常见的例子是动作。就动作来说,一个 ActionHandler 接口的实现能够在流程的事件上被调用。委托在 processdefinition.xml 中指定 的。当指定一个委托时提供了 3 种数据:  1)类名(必须的) :委托类的完全限定类名。  2)配置类型(可选的) :指定实例化并配置委托对象的方法。缺省情况默认的构造 器被使用并且配置信息被忽略。  3)配置(可选的) :配置类型所要求的委托对象的配置使用的格式。 下面描述所有的配置类型: cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 178 页 / 共 199 页
  • 179. 21.2.3.1. 配置类型域 这是缺省的配置类型。config-type 域将首先初始化一个委托类对象然后像配置中指定的 那样在域中设置值。配置是 xml,那里的元素名必须和类的域名一致。元素的内容文本被放 在响应域中。如果有必要而且可能的话,元素的内容文本被转换成域的类型。 支持的转换类型: • String 当然不需要转换了。但是要去掉 • 原始类型如 int、long、float、double 等待 • 原始类型的基本的包装类 • Lists、sets 和 collections。那样的话 xml 内容的每个元素采用一个集合元素 并且解析、递归地应用转换。如果元素的类型和 java.lang.String 不同可以通过使用 完全限定类型名指定一个类型属性来标明。例如:下面 片段将注入一个 String 类型 的 ArrayList 到域'numbers': <numbers> <element>one</element> <element>two</element> <element>three</element> </numbers> 元素的文本能够被字符器构造器转换成任何对象。为了使用另一种类型,在域元 素中(本例中的'numbers')指定元素类型。 这里有另一个 map 的例子: <numbers> <entry><key>one</key><value>1</value></entry> <entry><key>two</key><value>2</value></entry> <entry><key>three</key><value>3</value></entry> </numbers> • Maps。在本例中,每一个域元素的元素有一个子元素 key 和 value。Key 和 元素都使用转换规则被递归地解析。和使用 collections 相同,如果没有指定类型属 性假设转换到 java.lang.String。 • org.dom4j.Element • 任何其他类型,使用字符器构造器。 下列类的中例子: public class MyAction implements ActionHandler { cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 179 页 / 共 199 页
  • 180. // access specifiers can be private, default, protected or public private String city; Integer rounds; ... } 这是一个有效的配置: ... <action class="org.test.MyAction"> <city>Atlanta</city> <rounds>5</rounds> </action> ... 21.2.3.2. 配置类型 bean 和配置类型域相同但是然后这一属性通过 setter 方法来设置,而不是直接在域上。相同 的转换被应用。 21.2.3.3. 配置类型构造器 这个例示器(instantiator)将处理委托 xml 元素的完整内容并在委托类构造器中当作文 本传递它。 21.2.3.4. 配置类型配置属性 首先,缺省构造器被使用,然后这个例示器(instantiator)将处理完整的委托 xml 元素 的内容,然后传递它当作文本在方法 void configure(String);。同 jBPM 2 中的一样) ( 21.3. 表达式 对于委托中的一些,支持 JSP/JSF EL 类表达式语言。在动作、分派和决策条件中,你 可以写一个表达式,如 expression="#{myVar.handler[assignments].assign}" 。 表达式语言的基础能够在J2EE教程中找到。 jPDL 表达式语言同 JSF 表达式语言类似。意味着 jPDL EL 基于 JSP EL,但它使用#{...} 表示法并且它包括了方法绑定的支持。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 180 页 / 共 199 页
  • 181. 依赖于上下文,流程变量或任务实例变量连同下列的隐含对象一起能够被用作开始变 量: • taskInstance (org.jbpm.taskmgmt.exe.TaskInstance) • processInstance (org.jbpm.graph.exe.ProcessInstance) • processDefinition (org.jbpm.graph.def.ProcessDefinition) • token (org.jbpm.graph.exe.Token) • taskMgmtInstance (org.jbpm.taskmgmt.exe.TaskMgmtInstance) • contextInstance (org.jbpm.context.exe.ContextInstance) 这个属性在JBoss SEAM环境中变得真的非常强大。因为jBPM和JBoss SEAM的集成,所 有的后备bean、EJB和其他的一种原料就在你的流程定义的内部变得可用。感谢加文 ,绝 对让人吃惊!:-) 21.4. jPDL xml 模式 jPDL 模式是用在流程包中的 processdefinition.xml 文件里使用的模式。 21.4.1. 验证 当解析 jPDL XML 文档时,jBPM 将使用 jPDL 模式来验证你的文档,当两个条件遇到 时:首先模式已经在 XML 文档中被引用,如: <process-definition xmlns="urn:jbpm.org:jpdl-3.2"> ... </process-definition> 然后,xerces 解析器肯定是在类路径上。 jPDL 模 式 能 够 在 ${jbpm.home}/src/java.jbpm/org/jbpm/jpdl/xml/jpdl-3.2.xsd 或 http://jbpm.org/jpdl-3.2.xsd 上找到。 21.4.2. 流程定义 名称 类型 多样性 描述 name 属性 可选 流程的名字 swimlane 元素 [0..*] 流程使用的泳道,泳道代表流程角色而且它们用 于任务分配 start-state 元素 [0..1] 流程开始状态。注意流程没有 start-state 是合法的, 但是不能执行。 {end-state|state|node 元素 [0..*] 流程定义的节点,注意没有节点的流程是合法的, |task-node|process-st 但是不能执行。 ate|super-state|fork|j cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 181 页 / 共 199 页
  • 182. oin|decision} event 用于动作充当容器的流程事件 {action|script|create- 元素 [0..*] 全局定义的动作,能够被事件和转换引用。注意 timer|cancel-timer} 为了能够被引用这些动作必须指定名称。 task 元素 [0..*] 能够在动作中使用的全局定义的任务。 exception-handler 元素 [0..*] 一个异常处理列表,应用于这个流程定义中的委 托类抛出所有在异常。 表 21-1 21.4.3. 节点 名称 类型 多样性 描述 {action|script|create-timer|cancel-timer} 元素 1 表示节点行为的定制动作 common node elements 查看 通用节点元素 表 21-2 21.4.4. 通用节点元素 名称 类型 多样性 描述 name 属性 required the name of the node async 属性 { true | false }, 如果设置为true,这个节点将被异步执行。查看第 缺省值为 false 15 章 异步连续 transition 元素 [0..*] 离开转换。每个转换离开一个节点必须有一个唯一 名称。最大数量为 1 的离开转换允许没有名称。第 一个被指定转换称为缺省转换。在没有指定一个转 换而离开节点时缺省转换被处理。 event 元素 [0..*] 支持的事件类型: {node-enter|node-leave} exception- 元素 [0..*] 一个异常处理列表,应用于这个流程定义中的委托 handler 类抛出所有在异常。 timer 元素 [0..*] 指定一个定时器用来监控在这个节点中执行的持续 时间。 表 21-3 21.4.5. 开始状态 名称 类型 多样性 描述 name 属性 可选 节点名 task 元素 [0..1] 开始一个新流程实例的任务或者捕捉流程的发起者。查 看12.7 开始任务中的泳道 event 元素 [0..*] 支持的事件类型: {node-leave} cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 182 页 / 共 199 页
  • 183. transition 元素 [0..*] 离开转换。每个离开节点的转换必须有一个不同的名称。 exception 元素 [0..*] 一个异常处理列表,应用于这个流程定义中的委托类抛出所 -handler 有在异常。 表 21-4 21.4.6. 结束状态 名称 类型 多样性 描述 name 属性 必须 end-state 的名称 event 元素 [0..*] 支持的事件类型: {node-enter} exception 元素 [0..*] 一个异常处理列表,应用于这个流程定义中的委托类抛 -handler 出所有在异常。 表 21-5 21.4.7. 状态 名称 类型 多样性 描述 common node elements 查看 通用节点元素 表 21-6 21.4.8. 任务节点 Table 21.7. 名称 类型 多样性 描述 signal 属性 可选 {unsynchronized|never|first|first-wait|last|last-wait},默认是 last。Signal 指定任务完成对流程执行连续的影响。 create-tasks 属性 可选 {yes|no|true|false},默认为 true。当运行时诞已经决定哪个 任务将被创建时可以设置为 false。 那样的话, node-enter 在 上增加一个 action,在 action 上创建这个任务并设置 create-tasks 为 false。 end-tasks 属性 可靠 {yes|no|true|false},默认为 false。如果设置 remove-tasks 为 true 的话,在 node-leave 上所有仍然打开的任务将被 结束。 task 元素 [0..*] 当执行到达任务节点时任务应该被创建。 common 查看通用节点元素 node elements 表 21-7 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 183 页 / 共 199 页
  • 184. 21.4.9. 流程状态 名称 类型 多样性 描述 binding 属性 可选 定义子流程被解释的时刻。{late|*}默认是部署时解释 sub-process 元素 1 同这个节点关联的子流程 variable 元素 [0..*] 指定如何在开始时把数据从父流程复制到子流程以 及在子流程的完成时再从子流程回到父流程。 common node 查看通用节点元素 elements 表 21-8 21.4.10. 超级状态 名称 类型 多样性 描述 {end-state|state|node|task 元素 [0..*] 超级状态(superstate)的节点。超级状态可 -node|process-state|super 嵌套。 -state|fork|join|decision} common node elements 查看通用节点元素 表 21-9 21.4.11. 分支 名称 类型 多样性 描述 common node elements 查看通用节点元素 表 21-10 21.4.12. 合并 名称 类型 多样性 描述 common node elements 查看通用节点元素 表 21-11 21.4.13. 决策 名称 类型 多样性 描述 handler 元素 要么指定 org.jbpm.jpdl.Def.DecisionHandler 实现的名称 'handler' 元 素,要么 在转换上 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 184 页 / 共 199 页
  • 185. 指定条件 transition 在离开 离开转换。 每个节点的离开转换有一个条件。 这个决策 conditions 一个决 (decision)将使用这些条件查找计算结果为true的转 策节点 换。第一个转换代表otherwise分支。因此首先,所有条 的转换 件转换被计算。如果有一个计算为true,那个转换被处 上的属 理。如果没有条件转换计算为true,那么默认转换(也 性或元 就是第一个转换)将被处理。查看 条件元素 素文本 common 查看通用节点元素 node elements 表 21-12 21.4.14. 事件 名称 类型 多样性 描述 type 属性 必须 表示相对于事件被放置的元素事件类型 the event type that is expressed relative to the element on which the event is placed {action|script|create-ti 元素 [0..*] 这个事件上要执行的动作列表 mer|cancel-timer} 表 21-13 21.4.15. 转换 名称 类型 多样性 描述 name 属性 可选 转换名。注意每个离开节点的转换必须有一 个不同的名称。 to 属性 必须 目标节点的分层名称。更多的关于分层命名 的信息查看 10.6.3 分层命名 condition 属性或元 必须 守护条件表达式。这些条件 属性(或子元素) 素文本 能够用于决策节点,或在运行时在令牌上计 算可用的转换。 {action|script|create-ti 元素 [0..*] 处理转换时将要执行的动作。注意转换的动 mer|cancel-timer} 作不需要放入事件里(因为只有一个事件)。 exception-handler 元素 [0..*] 一个异常处理列表,应用于这个流程定义中 的委托类抛出所有异常。 表 21-14 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 185 页 / 共 199 页
  • 186. 21.4.16. 动作 名称 类型 多样性 描述 name 属性 可选 动作的名称。当动作被给定名称后,它们可以从流程定 义中查找到,这对于运行时动作以及仅一次声明动作是 有用的。 class 属性 引用名或 实现 org.jbpm.graph.def.ActionHandler 接口的完全限 表达式 定类名。 ref-name 属性 Ref-name 所引用动作的名称。 如果指定一个引用动作, 则本动作 或类 不需要再做处理。 expression 属性 expression 解释一个方法的jPDL表达式。查看21.3 表达式 、类或 ref-name accept-pro 属性 可选 {yes|no|true|false}. 默认是 yes|true。如果设置为 false, pagated-ev 那么动作只在这个动作元素的触发事件上执行。 更多信 ents 息,查看10.5.4 事件传播 config-type 属性 可选 {field|bean|constructor|configuration-property}。指 定动作对象如何被构造以及本元素的内容如何象配置 信息那样被动作对象使用。 async 属性 {true|false 默认为 false,这意味着动作将在执行的线程中被执行。 } 如果设置为 true,一个消息将被发送到命令执行器,并 且执行器构件将在一个独立的事务中异步执行动作。 内容 可选 动作的内容可以被作为你定制动作实现的配置信息, 这 {content 是考虑到可重用的委托类的创建。 关于委托配置的更多 } 信息,查看21.2.3 委托配置。 表 21-15 21.4.17. 脚本 名称 类型 多样性 描述 name 属性 可选 脚本动作的名称。当动作被给定名称后,它们可以在流 程定义中查找到,这对于运行时动作以及仅一次声明动 作是有用的。 accept-pro 属性 可选 {yes|no|true|false}。默认是yes|true。如果设置为false, pagated-ev [0..*] 那么动作仅在本动作元素的触发事件上被执行。 更多的 ents 信息,查看10.5.4 事件传播 expression 元素 [0..1] beanshell脚本。如果你没有指定variable元素,可以写表 达式作为脚本元素的内容(忽略expression元素标签) 。 variable 元素 [0..*] 脚本所需内部(in)变量。如果没有指定内部(in)变 量,则当前令牌的所有变量将被装载到脚本评价, 当你 想要限制装载到脚本中的变量数量时使用使用内部 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 186 页 / 共 199 页
  • 187. (in)变量。 表 21-16 21.4.18. 表达式 名称 类型 多样性 描述 内容 bean shell 脚本 表 21-17 21.4.19. 变量 名称 类型 多样性 描述 name 属性 必须 流程变更名称 access 属性 可选 默认是 read、write。它是一个逗号分隔的访问列表。迄 今为止, 使用的访问限定符只有 read, write 和 required。 mapped-name 属性 可选 默认是变量的名称。用来指定变量名称被映射的名称, mapped-name 的含义依赖于这个元素所被使用的上下 文。对于一个脚本,将是一个脚本变量名称;对于一个 任务控制器,将是任务表单参数的标签;对于一个 process-state,将是在子流程中使用的变量名称。 表 21-8 21.4.20. 句柄 名称 类型 多样性 描述 expression 属性 expression 一个jPDL表达式,返回结果使用toString()方法转 或 class 换为字符串,结果字符串应该与一个离开转换匹 配。查看21.3 表达式 class 属性 class 或 实现 org.jbpm.graph.node.DecisionHandler 接口的 ref-name 完全限定类名。 config-type 属性 可选 {field|bean|constructor|configuration-property}。 定 指 动作对象将如何构建以及本元素的内容怎样象配 置信息那样被动作对象所使用。 {content} 可选 Handler的内容能够用于定制的handler实现的配置 信息。这允许重用委托类的创建。更多的关于招 手配置的信息,查看21.2.3 委托配置 表 21-19 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 187 页 / 共 199 页
  • 188. 21.4.21. 定时器 名称 类型 多样性 描述 name 属性 可选 定时器的名称。如果没有指定名称,则采用封入 节点名称。注意,每个定时器应该有一个唯一的 名称。 duedate 属性 必须 指定在定时器创建和执行期间的持续时间(可选 择营业时间表示) 关于语法请查看 16.1.1 持续时 。 间 repeat 属性 可选 {duration | 'yes' | 'true'}当一个定时器在到期时间时 被执行后, “repeat” 可选项指定了在离开节点之前 重复定时器执行之间的持续时间。如果指定了yes 或true,那么则对repeate使用相同的到期日期。关 于语法请查看 16.1.1 持续时间 transition 属性 可选 当定时器执行、触发定时器事件后以及执行动作 (如果有的话)时所使用的转换名称 cancel-event 属性 可选 这个属性只用在任务的定时器中,它指定了定时 器将被取消的事件。 默认是 task-end 事件, 但是也 可 以 被 设 置 为 如 task-assign 或 task-start 。 cancel-event 类型可以通过给他们指定一个用逗号 分割的列表粒子组合。 {action|script| 元素 [0..1] 当定时器被触发时应执行的动作 create-timer|c ancel-timer} 21.4.22. 创建定时器 名称 类型 多样性 描述 name 属性 可选 定时器的名称。这个名称可以用作使用一个 cancel-timer 动 作撤消定时器。 duedate 属性 必须 指定在定时器创建和执行期间的持续时间(可选择营业时间 表示)。关于语法请查看 16.1.1 持续时间 repeat 属性 可选 {duration | 'yes' | 'true'}当一个定时器在到期时间时被执行后, “repeat” 可选项指定了在离开节点之前重复定时器执行之间 的持续时间。如果指定了yes或true,那么则对repeate使用相 同的到期日期。关于语法请查看 16.1.1 持续时间 transition 属性 可选 当定时器执行、触发定时器事件后以及执行动作(如果有的 话)时所使用的转换名称 表 21-21 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 188 页 / 共 199 页
  • 189. 21.4.23. 撤消定时器 名称 类型 多样性 描述 name 属性 可选 将要被撤消的定时器的名称 表 21-22 21.4.24. 任务 名称 类型 多样性 描述 name 属性 可选 任务的名称。命名的任务能够被引用以及通过 TaskMgmtDefinition 查找到。 blocking 属性 可选 {yes|no|true|false} 默认为 false。如果 blocking 设置 为 true,当任务没有完成时不能离开节点。如果设 置为 false(默认) ,令牌上的信号允许继续执行并离 开节点。默认设置是 false,因为通常是由用户接口 来强制阻塞。 signalling 属性 可选 {yes|no|true|false},默认为 true。如果设置 signalling 为 false,则任务没有触发令牌继续的能力。 duedate 属性 可选 用绝对或时间表示的持续时间像第 16 章 业务日历 (任务执行的的延迟时间)。请见业务日历中的解 释。 swimlane 属性 可选 引 用 一 个 swimlane , 如 果 在 任 务 上 指 定 了 一 个 swimlane,则 assignment 将被忽略。 priority 属性 可选 {highest,high,normal,low,lowest}之一。作为选择,可 以 为 priority 指 定 任 何 整 数 , 供 参 考 : (highest=1,lowest=5)。 assignment 元素 可选 描述一个当任务创建时分配任务给一个参与者的委 托。 event 元素 [0..*] 支 持 的 事 件 类 型 : {task-create|task-start|task-assign|task-end}。为了任务 分配, 我们特别的为 TaskInstance 添加了一个非持久 化的属性 previousActorId。 exception-handler 元素 [0..*] 一个异常处理器列表,用于这个流程节点中的委托 类所抛出的所有异常。 timer 元素 [0..*] 指定一个监视这个任务持续时间的一个定时器。对 于 任 务 定 时 器 特 殊 的 是 可 以 指 定 cancel-event , cancel-event 默认是 task-end,但是它可以被自定义 如 task-assign 或 task-start。 controller 元素 [0..1] 指定流程变量怎样被转换为任务表单参数。任务表 单参数通过用户界面向用户表现一个任务表单。 表 21-3 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 189 页 / 共 199 页
  • 190. 21.4.25. 泳道 名称 类型 多样性 描述 name 属性 必须 泳道的名称。泳道能够被引用并通过 TaskMgmtDefinition 被查找到。 assignment 元素 [1..1] 指定泳道的分配。这个分配在这个泳道上的第一个任务实例 被创建时被执行。 表 21-24 21.4.26. 分派 名称 类型 多样性 描述 expression 属性 可选 由于历史原因,这个属性的表达式不是jPDL表达式, 而是对jBPM身份构件的一个分配表达式。更多的关 于 如 何 写 jBPM 身 份 构 件 表 达 式 的 信 息 , 请 查 看12.11.2 分派表达式。注意,这个实现依赖于jBPM 身份构件。 actor-id 属性 可选 一个actorId,可以与pooled-actors连同使用。actor-id 被解释为一个表达式,因此你可以引用一个固定的 actorId,如actor-id=”bobthebuiler”;或者你可以引用 一个可以返回一个字符串的属性或方法, 如actor-id=” myVar.actorId”,这将调用任务实例变量“myVar”上 的getActorId方法。 pooled-actors 属性 可选 一个逗号分隔的 actorId 列表。能够同 actor-id 连同使 用。一个固定的合并的 actor 的集合能够被指定,如: pooled-actors="chicagobulls, pointersisters" 这 个 pooled-actors将作为一个表达式被解释。因此你可以 引用一个属性或可以返回String[]、 Collection或一个逗 号分隔的合并actor列表。 class 属性 可选 org.jbpm.taskmgmt.def.AssignmentHandler 的完全限定 类名 config-type 属性 可选 {field|bean|constructor|configuration-property}。指定分 配处理器对象(assignment-handler-object)对象将如 何创建以及这个元素的内容怎样象配置信息那样被 分配处理器对象所使用。 {content} 可选 assignment 元 素 的 内 容 可 以 被 作 为 分 配 处 理 器 (AssignmentHandler)实现的配置信息,这是考虑到 可重用的委托类的创建。更多的关于委托配置的信 息,请查看21.2.3 委托配置。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 190 页 / 共 199 页
  • 191. 21.4.27. 控制器 名称 类型 多样性 描述 class 属性 可选 org.jbpm.taskmgmt.def.TaskControllerHandler 实现的完全 限定类名 config-type 属性 可选 {field|bean|constructor|configuration-property}。。指定分配 处理器对象 (assignment-handler-object) 对象将被怎样创 建以及本元素的内容怎样象配置信息那样被分配处理器 对象所使用。 {content} controller 元素的内容要么是指定的任务控制处理器的配 置信息 (如果指定了 class 属性)要么必须是一个 variable , 元素列表(如果没有指定任务控制器) 。 variable 元素 [0..*] 如 果 没 有 通 过 class 属 性 指 定 任 务 控 制 处 理 器 , 则 controller 元素的内容必须是变量列表。 表 21-26 21.4.28. 子流程 名称 类型 多样性 描述 name 属性 必须 子流程的名称。可以是一个EL表达式,和计算的字 符串一样长。尤其在流程状态使用后期绑定时将非 常有用。要知道如何测试子流程,查看23.3 测试子 流程部分 version 属性 可选 子流程的版本。如果没有指定version属性,将使用 当部署父流程状态时给定的流程最新的版本。 binding 属性 可选 当部署父process-state(缺省行为)或当真正调用子 流程(binding="late")时显示是否子流程版本被检 测。当版本和binding="late"都给定时,将按你的 要求使用版本,但当父process-state 部署时不再去 尝试找子流程。 21.4.29. 条件 名称 类型 多样性 描述 向后兼容的内 必须 条件元素的内容是计算结果为布尔值的 jPDL 表达 容,这个条件 式。决策采用第一个表达式解析为 true 的转换(按 也能使用表达 在 processdefinition.xml 中的顺序)。如果没有条件 式键入,但那 解析为 true,采用缺少的离开转换(也就是第一个) 个 属 性 从 3.2 开始不赞成使 用 表 21-28 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 191 页 / 共 199 页
  • 192. 21.4.30. 异常处理程序 名称 类型 多样性 描述 exception-class 属性 可选 指定与本异常处理程序相匹配的 java throwable 完 全限定名。如果此属性没有指定,则它匹配所有异 常(java.lang.Throwable) 。 元素 [1..*] 当异常被异常处理程序捕获时将要执行的动作列表 表 21-29 第 22 章 安全 jBPM 的安全属性还处于 alpha 阶段。本章归档了可插拨的认证和授权。那么框架的什 么内容完成了,什么部分没有还完成呢。 22.1. 要做的事 在框架部分上,我们仍然需要在流程正在被执行时通过 jbpm 引擎定义一系列的被校验 的权限。当前你能检查你自己的权限,但仍没有一个 jbpm 缺省权限的设置。 只完成了一个缺省的认证实现。但预想了其他的认证实现。对于授权,预想了有许多的 授权实现,但他们还没有做出来。 如果没有认证和授权,框架提供插入你自己的认证和授权的机制。 22.2. 认证 认证让流程了解代表谁的代码正在运行。 假如 jBPM 的这个信息在 jBPM 环境中生效的 话,将使 jBPM 总是执行在特定的环境中,如 web 应用、EJB、swing 应用或一些其他的环 境下,它总是让周围的环境来执行认证。 在一些情形下,jBPM 需要知道谁正在运行代码。例如在流程日志中增加谁信息来知道 谁什么时候在做什么。另一个例子是基于当前被认证的参与者进行参与者计算。 在 每 个 情 形 下 jBPM 要 知 道 谁 正 在 运 行 代 码 , 需 要 调 用 中 心 方 法 org.jbpm.security.Authentication.getAuthenticatedActorId() 。 那 个 方 法 将 委 托 org.jbpm.security.authenticator.Authenticator 的实现。通过指定一个认证器的实现,你可以配 置 jBPM 如何从环境中检索当前认证的参与者。 缺省的认证器是 org.jbpm.security.authenticator.JbpmDefaultAuthenticator。那个实现维护 一 个 ThreadLocal 的 认 证 的 actorId 栈 。 认 证 的 块 能 够 使 用 方 法 JbpmDefaultAuthenticator.pushAuthenticatedActorId(String) 和 JbpmDefaultAuthenticator.popAuthenticatedActorId() 来 标 记 。 确 定 总 是 放 这 些 界 限 在 一 个 try-finally 块中,对于认证器实现的入栈(push)和出栈(pop)方法,在基 Authentication 类中提供了方便的方法。JbpmDefaultAuthenticator 维护一个 sctorIds 栈而不只是一个的 actorId 的原因是简单的:它允许 jBPM 代码去区分在代表用户执行的代码和代表 jbpm 引擎 执行的代码。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 192 页 / 共 199 页
  • 193. 查看 javadocs 获得更多的信息。 22.3. 授权 授权是确认是否一个认证的用户被允许去执行一个安全的操作。 jBPM 引 擎 和 用 户 代 码 可 以 核 实 是 否 一 个 用 户 被 允 许 使 用 API 方 法 org.jbpm.security.Authorization.checkPermission(Permission)去执行一个给定的操作。 Authorization 类也将委托调用一个可配置的实现。对于不同策略的 pluggin 接口是 org.jbpm.security.authorizer.Authorizer。在包 org.jbpm.security.authorizer 有一些显示认证器实 现意图的例子。大多数是没有被完全实现而且他们也没有被测试。 另外仍然要去做的是通过 jBPM 引擎定义一系列的 jBPM 权限并校验那些权限。 一个例 子 可 以 通 过 在 TaskInstance.end() 方 法 中 调 用 Authorization.checkPermission(new TaskPermission("end", Long.toString(id)))方法校验当前已认证的用户有足够特权来结束一个 任务。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 193 页 / 共 199 页
  • 194. 第 23 章 工作流的 TDD 文件 23.1.工作流 TDD 介绍 既然开发面向流程的软件和开发其他的软件没有什么区别, 我们相信流程定义应该是容 易测试的。本章显示如何使用纯 jUnit 而没有任何扩展来单元测试你创作的流程定义。 开发周期应该尽可能的短。变化应使软件源代码立即可校验。更好的,不用任何中间的 构建步骤。下面所给的例子将显示如何开发并测试 jBPM 流程而不需要中间的步骤。 大多数流程定义的单元测试是执行环境。每个环境在 jUnit 测试方法中被执行的并且将 输入外部触发器(读:信号)进入流程执行然后在每个信号后检验是否流程是期待的状态。 让我们看一个这样的测试的例子。 我们处理一个简化的使用下面的图形表示的拍卖流程 的版本。 auction 图 23-1 拍卖测试流程 现在,让我们写一个执行主环境的测试: public class AuctionTest extends TestCase { // 解析流程定义 static ProcessDefinition auctionProcess = ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par"); // 获得容易断言的节点 static StartState start = auctionProcess.getStartState(); static State auction = (State) auctionProcess.getNode("auction"); static EndState end = (EndState) auctionProcess.getNode("end"); // 流程实例 ProcessInstance processInstance; // 执行主路径 Token token; cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 194 页 / 共 199 页
  • 195. public void setUp() { // 为给定的流程定义创建一个新的流程实例 processInstance = new ProcessInstance(auctionProcess); // 执行的主路径是根令牌 token = processInstance.getRootToken(); } public void testMainScenario() { // 在流程实例创建后,执行主路径位于 start 状态 assertSame(start, token.getNode()); token.signal(); // 在信号后,执行的主路径已经移动支 auction 状态 assertSame(auction, token.getNode()); token.signal(); // 在信号后,执行的主路径已经移动支 end 状态然后流程结束 assertSame(end, token.getNode()); assertTrue(processInstance.hasEnded()); } } 23.2. XML 源 在你开始写执行案例前,你需要一个 ProcessDefinition。最简单获得 ProcessDefinition 对象的方式是通过解析 xml。如果你有代码生成,键入 ProcessDefinition。解析并激活代码 生成。然后你会获得各种解析方法。有三种基本的去写能够解析成 ProcessDefinition 对象的 xml 的方法: 23.2.1. 解析流程包 流程包是一个在文件中包含流程 xml 的叫 processdefinition.xml 的 zip 文件。jBPM 流程 设计器读写流程包。例如: ... static ProcessDefinition auctionProcess = ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par"); ... cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 195 页 / 共 199 页
  • 196. 23.2.2.解析 xml 文件 在其他的情况下,你可能想手写 processdefinition.xml 文件然后使用如一个 ant 脚本打包 zip 文件。那样的话,你可能使用 JpdlXmlReader ... static ProcessDefinition auctionProcess = ProcessDefinition.parseXmlResource("org/jbpm/tdd/auction.xml"); ... 23.2.3. 解析 XML 字符串 最简单的的选项是在单元测试行内从纯字符串解析 xml 。 ... static ProcessDefinition auctionProcess = ProcessDefinition.parseXmlString( "<process-definition>" + " <start-state name='start'>" + " <transition to='auction'/>" + " </start-state>" + " <state name='auction'>" + " <transition to='end'/>" + " </state>" + " <end-state name='end'/>" + "</process-definition>"); ... 23.3. 测试子流程 将要做的(查看 test/java/org/jbpm/graph/exe/ProcessStateTest.java 文件)。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 196 页 / 共 199 页
  • 197. 第 24 章 可插拨架构 jBPM 功能特性分裂成模块。每个模块都有一个定义和执行(运行时)部分。中心模块 是图模块,由 ProcessDefinition 和 ProcessInstance 组成。流程定义包含一个图和流程实例表 示的一个图执行。jBPM 所有其他的功能特性被分组成可选的模块。可选的模块能够扩展图 模块的额外特性,如上下文(流程变量) 、任务管理、定时器等等。 ProcessDefinition ProcessInstance (org::jbpm:grap:def) (org::jbpm:grap:exe ContextDefinition ContextInstance (org::jbpm:context:def) (org::jbpm:context:exe TaskMgmtDefinition ContextInstance (org::jbpm:taskmgmt:def) (org::jbpm:taskmgmt:exe jBPM工作流引擎的功能特性分裂成了几个模块, 核心模块是图模块,由ProcessDefinition和ProcessInstance组成。 所有的其他模块是可选的而且也是由一个defintion和一个instance组成。 可选的定义能够被增加到ProcessDefinition。当一个流程实例被创建时, 每个可选defintion的响应的instance被创建。 这样的模块化方案叫可插拨的架构并且也允许用户去集成定制的能力 进入jBPM工作流引擎中。 图 24-1 可插拨架构 在 jBPM 中的可插拨架构也是给 jBPM 引擎增加定制能力唯一的机制。 定制流程定义信 息能够通过向流程定义上增加一个 ModuleDefinition 实现来增加。当流程实例被创建时,它 将为 ProcessDefinition 的每个 ModuleDefinition 创建一个实例。ModuleDefinition 被用作 ModuleInstances 的工厂。 大多数去扩展流程定义信息的整合的方法中通过增加信息到流程包并实现 ProcessArchiveParser。ProcessArchiveParser 能够解析增加到流程包中的信息,创建定制的 ModuleDefinition 并增加它到 ProcessDefinition 中。 public interface ProcessArchiveParser { void writeToArchive(ProcessDefinition processDefinition, ProcessArchive archive); cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 197 页 / 共 199 页
  • 198. ProcessDefinition readFromArchive(ProcessArchive archive, ProcessDefinition processDefinition); } 为了处理它的工作,定制的 ModuleInstance 必须在流程执行期间被相关的事件通知。定 制的 ModuleDefinition 可能增加 ActionHandler 的实现到流程的事件上作为这些流程事件的 回调处理程序。 做为选择,定制的模块可能使用 AOP 来绑定定制的实例到流程执行上。JBoss AOP 是 非常好的适合这个工作因为它成熟的、容易学习而且也是 jBoss 栈的组成部分。 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 198 页 / 共 199 页
  • 199. 写在最后 搞工作流有一段时间了,从 lotus 到 j2ee,在这过程中从社区中获得了很多的知识,但 贡献甚少,最近想使用并研究下 jBPM 这个开源的工作流引擎,特抽出时间将 3.2.3 版的开 发手册翻译下,以回馈社区。 谢谢大家的支持! 大家有时间可以上 QQ群 43910861 进行交流,也可以上我的blog 噢!http://zhangym.javaeye.com 本文档内容并不完美,没有经过严格的校稿,如有问题大家可以联系我 (jeffming@126.com),对文档的讹误和措辞进行修改。 张玉明 2008-10 cncsi hlj brnc jeffming@126.com http://zhangym.javaeye.com/ 第 199 页 / 共 199 页