精通-JBPM6-全-

精通 JBPM6(全)

原文:zh.annas-archive.org/md5/cf6cb5bbd16534c2cc0f36e1f4b6d456

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

jBPM 是一个领先的开源 BPM 和流程平台,其开发由红帽公司赞助,遵循 Apache 软件许可(ASL)许可。jBPM 产品已经存在近 10 年;其最突出的优点在于灵活性、可扩展性和轻量级,它是一个模块化、跨平台的纯 Java 引擎,符合 BPMN2 规范。

它提供了一个强大的管理控制台和开发工具,支持用户在业务流程生命周期(开发、部署和版本控制)中的使用。它集成了广泛采用的框架和技术(SOAP、REST、Spring、Java EE CDI 和 OSGi),并为 Git 和 Maven 提供现成的支持。

它可以适应不同的系统架构,可以作为完整的 Web 应用程序或作为服务部署;它可以紧密嵌入到传统的桌面应用程序中,也可以松散集成到复杂的事件驱动架构中。在其默认配置下,jBPM 可以由企业级应用程序服务器红帽 EAP 6.x 或前沿的红帽 WildFly 8 服务器托管。

精通 JBPM6 带您通过一种实用的方法来使用和扩展 jBPM 6.2。本书提供了详细的 jBPM 6.2 概述;它涵盖了引擎支持的 BPM 符号,并解释了高级引擎和 API 主题,尽可能多地关注几个实际的工作示例。

本书向用户展示了针对常见实时问题(如 BAM,即业务活动监控)和生产场景的解决方案。

本书涵盖的内容

第一章,业务流程建模 – 连接业务与技术,为用户提供了对 BPM 环境的概述,介绍了 jBPM 世界,并揭示了业务逻辑集成平台的整体图景。

第二章,构建您的第一个 BPM 应用程序,首先通过为读者提供实际的产品安装和配置教程,直接将用户带入 jBPM 工具栈,然后处理初学者主题,如业务流程建模和部署。

第三章,使用流程设计器,深入探讨了基于 Web 的 jBPM 工具,向用户展示主要的 jBPM Web 设计器功能:用户表单、脚本和流程模拟。

第四章,运营管理,介绍了新的 jBPM 工件架构,重点关注 Maven 仓库(模块和部署)、引擎审计和日志分析、作业调度以及一个完整的 BAM 定制示例(包含仪表板集成)。

第五章,BPMN 结构,说明了 jBPM 实现的 BPMN2 结构,并通过注释上下文中准备好的源代码示例提供了使用它们的见解和注意事项。

第六章,核心架构,通过详细阐述如何利用几个源代码示例来利用引擎功能,涵盖了所有 jBPM 模块(例如,人类任务服务、持久化、审计和配置)。

第七章,定制和扩展 jBPM,以实用方法探讨引擎定制区域;它向用户提供有关如何定制持久化、人类任务服务、序列化机制和工作项处理架构的解释。

第八章,将 jBPM 与企业架构集成,描述了 jBPM 如何通过 SOAP、REST 或 JMS 作为客户端或服务器与外部应用程序集成。它提供了有关如何在 Java EE 应用程序中利用其服务的见解。

第九章,生产中的 jBPM,探讨了在处理服务可用性、可扩展性和安全性时 jBPM 系统的功能;它提供了有关在生产环境中调整引擎性能的技巧和技术。

附录 A,未来,简要介绍了业务流程建模的趋势和未来。

附录 B,jBPM BPMN 结构参考,是 jBPM 支持的 BPMN 结构的快速参考。

您需要为这本书准备以下内容

在运行代码示例之前,您需要安装以下软件:

jBPM 需要 JDK 6 或更高版本。可以从www.oracle.com/technetwork/java/javase/downloads/index.html下载 JDK 6 或更新的版本。此页面上也有安装说明。要验证您的安装是否成功,请在命令行中运行java –version

sourceforge.net/projects/jbpm/files/jBPM%206/jbpm-6.2.0.Final/下载jbpm-6.2.0.Final-installer-full.zip。只需将其解压缩到您选择的文件夹中。用户指南(docs.jboss.org/jbpm/v6.2/userguide/jBPMInstaller.html)包括如何简单快速开始使用说明。

jBPM 设置需要 Ant 1.7 或更高版本(ant.apache.org/srcdownload.cgi)。

额外所需的软件如下:

运行示例的首选开发 IDE 是 Eclipse Kepler 发行版,它可以在 BPMN 安装过程中自动下载并预先配置。

本书面向对象

本书主要面向 jBPM 开发者、业务分析师和流程建模师,以及在某种程度上必须接触 jBPM 平台功能的项目经理。本书假设你具备业务分析和建模的先验知识,当然,还需要 Java 知识;对 jBPM 的基本知识也是必需的。

习惯用法

在这本书中,你会发现许多不同风格的文本,用于区分不同类型的信息。以下是一些这些风格的示例及其含义的解释。

文本中的代码词汇如下所示:“在roles.properties文件中为用户指定一个角色。”

代码块如下设置:

ReleaseId newReleaseId = ks.newReleaseId("com.packt.masterjbpm6", "pizzadelivery", "1.1-SNAPSHOT");
// then create the container to load the existing module
Results result = ks.updateToVersion (newReleaseId);

当我们希望引起你对代码块中特定部分的注意时,相关的行或项目将以粗体显示:

<bpmn2:scriptTask id="_2" name="prepare order" scriptFormat="http://www.java.com/java">

任何命令行输入或输出都如下所示:

ant install.demo

新术语重要词汇将以粗体显示。你在屏幕上看到的,例如在菜单或对话框中的文字,将以如下方式显示:“从左侧导航菜单面板中选择管理 | 数据提供者链接。”

注意

警告或重要提示将以如下框显示。

小贴士

小贴士和技巧如下所示。

读者反馈

我们的读者反馈总是受欢迎的。告诉我们你对这本书的看法——你喜欢什么或可能不喜欢什么。读者反馈对我们开发你真正能从中获得最大价值的标题非常重要。

要向我们发送一般反馈,只需发送一封电子邮件到<feedback@packtpub.com>,并在邮件主题中提及书名。

如果你在一个领域有专业知识,并且你对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在你已经是 Packt 图书的骄傲拥有者,我们有许多事情可以帮助你从购买中获得最大价值。

下载示例代码

你可以从www.packtpub.com下载示例代码文件,这些文件存储在你购买的所有 Packt 出版物的账户中。如果你在其他地方购买了这本书,你可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给你。

错误清单

尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然会发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问 www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。

要查看之前提交的勘误表,请访问 www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将在勘误部分显示。

盗版

在互联网上对版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

如果您发现疑似盗版材料,请通过 <copyright@packtpub.com> 联系我们,并提供链接。

我们感谢您在保护我们作者以及为我们提供有价值内容方面的帮助。

问题

如果您在本书的任何方面遇到问题,请通过 <questions@packtpub.com> 联系我们,我们将尽力解决。

第一章. 业务流程建模 - 桥接业务与技术

“所有业务都做 IT”(信息技术)。这是我最近看到的一条非常出色但具有未来意义的推文。近年来,信息技术对商业的影响是压倒性的。它们就像我们宇宙中的两个大型星系,相互碰撞和融合。这种合并的当前状态可以用一个词来定义 - 协作。业务人员与信息技术(IT)人员协作,并使用 IT 服务不断改进和提供具有商业可行性和盈利性的产品/服务给他们的客户。

协作本质上需要有效的沟通,业务流程建模恰好适合这种场景。业务流程建模并不新鲜;业务人员一直在使用它。模型是在脑海中开发的,然后,它们被写成文本或以图表的形式描绘出来。随着 IT 拥抱业务,这些模型演变成了标准的流程图和活动图。然而,存在模糊性;业务作为需求提供的图表和文本被技术人员解释,他们有自己的表示、架构模型、需求文档和设计。这是重复的工作和人员,通常在业务利益相关者和信息技术人员之间有一系列的会议,他们进行谈判,并就业务需求达成结论。在这种背景下,业务流程建模带来了哪些改进?业务流程建模引入了业务和信息技术人员之间的共同成果的概念。

业务模型由业务分析师准备并与技术人员共享。他们协作并改进模型,最终达成一个可执行的成果。此外,技术人员如往常一样通过自动化他们的参与来减少工作量;也就是说,我们正在大量转向软件系统,在这些系统中,业务人员可以配置业务流程并执行它,而无需信息技术人员的干预。

总结这一哲学,让我们跳到更大的背景,讨论业务流程管理BPM)的细节。本章涵盖了以下概念:

  • 业务流程管理概念

  • 标准 - 业务流程模型和符号(BPMN 2.0)

  • BPM 在行业中的应用案例

  • BPM 世界中的设计模式

  • jBPM 的简要介绍

  • 业务逻辑集成平台,更全面的视角

业务流程管理

BPM 涉及业务流程的设计、建模、执行、监控和优化。一个帮助完全实现这些目标的专用软件系统被称为业务流程管理系统BPMS)。实际上,在业务中使用的 IT 基础设施的大部分实际上都是业务流程的一部分或几部分,BPMS 应该具备管理业务管理流程完整生命周期的能力。此外,jBPM 提供了一个完整的 BPMS。

业务流程管理

业务流程由一组活动组成,这些活动组织起来以完成特定的业务目标,这些目标从创建产品提供服务不等。业务流程模型还提供了业务流程的视觉表示。业务流程中的活动(也称为任务)通过连接来表示业务流程的执行流程;此外,这些活动还可以进行分类。

jBPM 通过其流程设计器帮助用户定义和建模业务流程。实际上,业务用户可以在线设计业务流程,高效的版本控制和历史记录功能有助于使建模业务流程的活动协作化。还提供了在运行时模拟业务流程可能行为的方法。jBPM 还提供了在业务变更规定流程改进时将流程定义迁移到更新版本的能力。

定义的业务流程模型部署到 BPM 软件中,在这些流程定义的实例被创建以执行流程。jBPM 提供了执行业务流程的能力,并具有完整的操作管理功能,如跟踪、控制和维护所有流程实例的生命周期历史记录。

人机交互管理

在解释 BPMS 时,我们需要详细讨论的一个重要概念是人机交互管理HIM)。构成业务流程的活动可以广泛地分为自动手动。自动活动是指可以在没有任何人工干预的情况下由软件系统完成的活动。例如,在银行交易流程中,客户必须提供交易详情,如需要转账的银行账户号码和要存入的金额。这是一项手动活动。相比之下,如果需要将短信警报作为流程的一部分发送到客户的手机,则不需要人工干预;软件可以检索与客户账户注册的移动电话号码并自动发送短信。短信警报是银行交易流程中的自动活动。

人机交互管理

因此,人类任务实际上是业务流程中的人类交互,或者从更广泛的角度来看,是与现有软件系统的人类交互。通常,人类活动是在软件系统之外执行的物理任务,其结果或结论随后作为输入输入到软件系统中。从系统的角度来看,我们可以这样说,相关的人类活动将为业务流程提供输入,并且存在业务流程只有在手动决策之后才能继续的场景。例如,在银行交易业务流程中,客户必须提供一个一次性密码才能继续流程。

人类交互管理系统HIMS)应具备处理人类活动生命周期的能力,这包括通知与该活动相关的用户、从用户那里收集信息或结果,以及跟踪任务的历史记录。从人类交互中收集的信息用于流程执行和决策制定。jBPM 具有内置的人类任务服务,并且可以与其他任何 HIMS 集成。jBPM 的内置人类任务服务符合 WS-Human Task 规范。

jBPM 还提供了一种表单建模功能,帮助业务分析师设计可以附加到 jBPM 用户任务的用户界面表单,通过这些表单可以收集关于用户任务完成情况(如果有)的信息。

业务活动监控

业务活动监控BAM)为业务活动提供在线监控能力,并使企业能够通过切割和重组活动事件数据来得出关键性能指标。BAM 指的是一种通用的软件系统,可以监控业务流程,但将 BAM 与基于 BPM 的软件系统结合相对容易且强大。通常,BAM 软件具有使用仪表板显示业务数据的能力,使业务用户能够创建关于性能指标和趋势分析的客户报告和图表。

jBPM 核心引擎存储流程和任务历史记录,并提供 API 以执行 BAM 操作。此外,jBPM 工具集包括仪表板构建器,使用户能够从业务流程历史记录中创建自定义仪表板。

考虑一个银行交易流程,其中客户从其账户向另一个账户进行交易。通过分析流程历史记录日志,业务用户可以创建关于一天中交易量最大的高峰时段的趋势分析报告。这种分析可用于优化相关的 IT 成本。

另一个更常见的应用场景是,通过避免流程瓶颈来优化业务流程。流程瓶颈可能由各种问题引起,包括资源短缺(人员供应不足)或系统表现不佳。资源短缺问题可能相当明显,但系统效率低下可能并不明显。例如,假设在银行交易流程中的一次性密码OTP)活动经常延迟。假设它需要超过一分钟,这会促使用户再次执行交易(或使用重发选项),导致客户不满。这也导致了通过发送多个 OTP 造成的资源浪费;换句话说,延迟的 OTP 会进一步混淆客户,银行也失去了其信誉。这些问题通常是在勤奋的客户通知银行这些问题时被注意到的。BAM 可以使这些问题的修正更加主动,从而提高客户满意度。

业务流程模拟

业务流程模拟为业务用户提供了一种能力,让他们能够看到他们创建的业务流程模型在运行时会如何工作。模拟是一个与建模紧密相关的术语。模型设计者真的很希望有一个模拟器来测试他的模型并了解其模型的运行时影响。业务流程模拟是分析业务流程运行行为的能力。它有助于优化业务流程模型的质量、性能和资源利用。

将业务流程模拟与测试这些流程的更常规模型进行比较,我们发现用户在将它们部署到质量保证QA)环境中后,必须手动测试这些流程。进一步来说,为了实现自动化验证,用户将不得不依赖程序员编写自动化测试用例。在这两种情况下,验证过程都没有与建模过程本地执行,延迟的结果意味着需要进行迭代以进行修正,这增加了流程改进的成本。

业务流程模拟为业务用户提供了一种能力,让他们能够看到他们创建的业务流程模型在运行时会如何工作。用户可以提供模拟信息,这包括过程输入、中断以及用于模拟模式的资源信息。在执行模拟的过程中,软件会收集关于运行过程的统计信息,用户可以通过分析这些信息来优化他们的业务模型。

jBPM 是 Business Process Simulation Interchange Standard (BPSim) 的早期采用者和实施者。BPSim 提供了一个标准规范,允许使用计算机可解释的表示或元模型来分析业务流程模型。使用 jBPM,最终用户可以通过流程设计器提供模拟的元数据,并执行模拟以查看执行路径和与各种执行路径相关的性能指标。

业务流程模型和符号

业务流程模型和符号 (BPMN) 是广泛接受的业务流程建模标准,并为在 业务流程图 (BPD) 中指定业务流程提供了一个图形符号。它基于与 统一建模语言 (UML) 的活动图非常相似的流程图技术。BPMN 由 对象管理组 (OMG) 维护,当前版本为 2.0(2011 年 3 月发布)。

BPMN 的主要目标是提供一个标准符号,便于业务利益相关者理解。这些包括创建和改进流程的业务分析师、负责实施这些流程的技术开发人员以及监控和管理流程的运营经理。因此,BPMN 作为一种通用语言,弥合了业务流程设计和实施之间经常出现的沟通差距。BPMN 还作为组织之间为了实现共同商业目标而合作的沟通媒介,以共享功能流程和程序。

BPMN 与其他流程定义标准(如 Business Process Execution Language (BPEL))之间的主要区别之一是 BPMN 支持人类交互。人类交互支持为业务流程建模提供了完整性,因为人类是任何商业组织中的主要参与者。作为一个视觉编程符号的规范,BPMN 对业务流程模型元素的图表表示给予了极大的重视。因此,BPMN 图表的读者可以轻松识别基本元素类型并理解业务流程。BPMN 符合性确保了共同的视觉表示,尽管它允许在不会大幅改变图表的基本外观和感觉的情况下进行变化。

BPMN 2.0 规范的详细说明可在规范文档中找到。

注意

BPMN 规范文档可在 www.bpmn.org/ 找到。

符合标准 BPMN 规范定义了四种类型的符合性:

  • 流程建模一致性:声称符合一致性的工具必须支持 BPMN 核心元素、人类交互、池和消息流。

  • 流程执行符合性:声称符合一致性的工具必须支持并解释规范中所述的操作语义和活动生命周期。

  • BPEL 流程执行一致性:一种特殊的流程执行一致性,支持 BPMN 映射到 WS-BPEL。

  • 编排建模一致性:声称一致性的工具必须支持编排图及其元素。编排图侧重于不同群体在活动中的协作以及它们之间的消息流。

jBPM 实现部分声明了前两种一致性,即流程建模一致性和流程执行一致性。尽管 jBPM 支持流程建模中的所有核心元素,但它不支持规范中描述的所有元素。

核心元素

BPMN 图的主要组成部分,BPMN 元素,可以广泛地分为五类:

  • 流程对象:这些对象定义了业务流程的行为

  • 数据:这代表与业务流程相关的数据

  • 连接对象:这些对象用于将流程对象连接到彼此

  • 泳道:这是用来分类流程对象的

  • 工件:这些提供了关于流程的额外信息

流程对象

流程对象是 BPMN 的核心;它们用于定义业务流程将如何表现。以下是一些主要的流程对象类型:

  • 事件:事件是在业务流程过程中发生的事情。这些事件影响模型的流程,通常对业务流程有触发和影响。一些事件示例包括开始、停止和错误。开始事件触发流程实例的开始,并使用显式触发器、消息或计时器来触发。事件可以是来自流程的信号(抛出)或等待事件(捕获)。

  • 活动:活动是在业务流程中执行的动作。它们可以是原子的(称为任务)或复合的(子流程)。活动的一个例子是用户任务或服务任务。用户任务表示人类交互,动作必须手动执行以完成此任务。任务可以在触发时完成,也可以等待完成(等待状态);例如,服务任务被触发并完成,而人类任务被触发并等待用户完成动作。

  • 网关:网关用作业务流程中执行路径的分支、合并、分叉和连接的控制。一个例子是并行网关,它可以用来将执行路径分割成多个输出分支,所有输出分支同时激活。并行网关也可以用来合并分支;它们等待所有输入分支完成,然后触发输出流程。

数据

业务流程执行会产生数据;例如,在银行交易流程中,交易详情是用户关于交易提供的数据。这些数据必须保存或传输到另一个活动以进行进一步处理。以下元素是 BPMN 中数据建模的核心。

  • 数据对象:数据对象是作为业务活动的一部分创建的数据。它们可用于信息目的,以表明活动产生了此类数据。

  • 数据输入:数据输入指定活动完成所需的数据。例如,在银行交易流程中,发送 OTP 的任务需要客户的详细信息来发送短信。因此,此活动的输入是从先前活动的输出或从与流程实例全局关联的数据映射而来的。

  • 数据输出:数据输出是活动产生的数据,必须映射到全局流程变量或作为后续活动的输入。

  • 数据存储:数据存储为活动提供一个机制,用于检索或更新超出流程范围持久存储的信息。

连接对象

流程对象通过连接对象相互连接。以下是一些连接对象的类型:

  • 序列流:序列流是用于表示连接的基本元素。它用于连接流程对象并定义活动的执行顺序。

  • 消息流:消息流用于表示跨越组织边界的信息流(组织内的活动组或部门,或用户的角色)。

  • 关联:关联用于表示数据或文物与流程对象的关联。

泳道

泳道是一种组织和分类活动的视觉机制,是使用 BPMN 的跨职能图表的基础。它们代表一个组织、一个角色或一个系统。它们基本上有以下两种类型:

  • :池表示活动的更高层次分类。例如,组织可以表示为一个池。池由多个泳道组成。泳道的例子可以是组织内的部门。

  • 泳道:泳道表示池内的分类。泳道包含流程对象、连接对象和文物。

文物

文物是图形表示,提供关于流程或流程内元素的支持信息。它们不会干扰流程流程;换句话说,我们可以从流程执行的角度说它们是不透明的。BPMN 规范中声明的文物基本类型如下:

  • 分组:分组构造可以用来逻辑地分组任意一组流程对象,以表明它们逻辑上属于一起

  • 文本标注:文本标注可以用来将额外的文档与 BPMN 图中的任何元素关联起来

下面的图中展示了银行交易流程,其中事件、活动、数据对象、流道和网关的核心元素被标注出来:

工件

在行业中应用的业务流程管理

BPM 通常用于以下情况成立的行业:

  • 业务流程是分布式的,跨越多个应用程序或软件系统

  • 该流程涉及必须维护和随时间更新的复杂规则

  • 需要通过监控现有活动来持续改进业务流程

  • 业务是通过多个利益相关者的协作完成的

考虑到上述考虑因素,我们可以将业务流程管理(BPM)应用于任何以流程为导向的领域,例如医疗保健、保险、销售点、供应链管理以及银行和金融服务。一个功能齐全的 BPM 系统为新产品/服务提供或改进流程的组织在周转时间方面提供了巨大的优势。

考虑到本书的范围,我们可以在接下来的章节中简要介绍一些领域。这些用例的详细研究将在后续章节中进行,并且这些用例将在整本书中用于解释 jBPM 的各个方面。

供应链管理

供应链是一系列相互关联的业务流程和业务伙伴,它管理着从设计点到将产品和服务交付给最终客户的过程中的商品和信息流。供应链为组织提供了一个协调良好的渠道,以将他们的产品和服务交付给最终客户。供应链通常包括以下内容:

  • 供应商:他们提供原材料。

  • 制造商:他们制造产品。

  • 分销商:他们将产品分销给零售商销售。

  • 零售商:他们向最终客户销售产品。

  • 客户:他们购买并使用产品。因此,供应链是多个功能单元的协作,这些组可以属于同一组织或来自不同的业务单元。以成本效益的方式协调工作和满足最终用户期望是一个挑战,并且归因于供应链管理系统。正如你所看到的,该领域本质上是面向流程的,有很多复杂的规则和法规管理流程的每个阶段,并且这是应用完整 BPM 系统的最佳匹配领域。

在供应链领域可以提及的一些流程如下:

  • 制造流程管理:这涉及产品的制造和/或服务的提供

  • 订单管理:这满足了客户在订单履行方面的要求

  • 客户服务管理:这提供有关产品可用性、发货日期和订单状态的现实信息

订单管理是供应链领域的核心流程。下图中描述的流程是一个简单的流程,但其目的是为了展示业务流程带来的便利和清晰性,尤其是对跨职能业务流程而言。从流程图本身来看,任何外行人都可以理解组织中的跨职能单元以及当然的流程。因此,用户,无论是业务分析师还是运营用户,都可以更容易地理解整体情况,从而提高整体沟通和效率。

供应链管理

支付是一项活动,但其规模和复杂性相当于一个独立的过程。此外,如果我们应用责任规则,支付需要被明确抽象为一个可以跨组织流程复用的独立过程,以便处理支付。因此,我们可以做出设计决策,将支付作为一个子过程,这可以降低复杂性,更好地处理异常情况,并实现可重用性。

在这个特定的流程中,我还想让您注意的一个点是无库存中的停止事件;在流程终止中嵌入了一条消息。这可以用作向另一个组织流程处理无库存情况的信号;例如,在这种情况下,无库存将是触发需求管理流程的信号事件。

银行和金融服务

银行是另一个 BPM 应用广泛的领域。BPM 使银行能够自动化其业务流程,如账户开设、贷款处理、支付和交易。在银行业务领域,流程的可见性和对法规的遵守至关重要。银行持续对其流程和服务进行创新性改变,BPMS 为他们提供了快速适应流程改进计划的能力。

与银行相关的典型业务流程如下:

  • 账户开设和维护

  • 贷款处理

  • 支付和交易处理

银行和金融服务

前面的图显示了贷款处理的简化版本,类似于供应链管理领域的示例流程。它带来的清晰性是显而易见的。然而,在银行业务领域,更重要的是 BPM 向业务用户提供的灵活性、自动化和智能选项。业务用户可以定义流程并对其进行修改,从而为市场推广带来敏捷性;自动化帮助用户成功遵守法规合规;基于活动监控的分析帮助银行组织减少欺诈并提升客户体验。

业务流程建模中的设计模式

设计模式是对其相应领域内常见问题的解决方案。业务流程模型试图通过使用一组标准元素来映射通用业务流程和程序。下面提供的设计模式是针对业务流程建模中某些常见问题的解决方案。

本节重点介绍在业务流程建模领域中确定的设计模式。理解这些模式将有助于您在使用 BPMS 尝试映射问题时轻松识别解决方案。以下列表中的模式可以在 jBPM 设计工具提供的模式模板中找到。

顺序

顺序是业务流程建模中最基本的模式,通过它可以将要依次执行的任务建模。

jBPM 通过使用序列流连接器连接任务来支持这一点,这些连接器提供了任务的顺序排列。

顺序

前面的图示说明了银行领域贷款处理的一部分,它代表了一个顺序模式问题。收集贷款信息、验证和发送授权等活动必须按顺序执行;这些活动可以被分配给单独的人类用户,甚至可以分配给系统自动化任务。

并行分割

并行分割模式用于将执行分支分割成超过两个分支,使得每个输出分支都并行执行。

jBPM 通过使用并行网关来支持并行分割模式,其中所有输出分支同时激活。

并行分割

前面的图示展示了订单履行过程中的一个场景,其中适用并行分割模式。在订单处理过程中,在提供订单详情、发货和发票发送之后,支付可能成为并行处理路径。这里的并行分割模式正好适用,可以解决问题。

同步

同步模式是并行分割设计模式的另一端;它以这种方式合并两个或更多分支,即合并后的分支只有在所有要加入的输入分支执行完毕后才会运行。

同步

前面的图示展示了并行分割模式的示例延续,在完成发货和支付并行处理路径之后,有一个关闭订单的活动需要执行。请注意,关闭订单活动必须在支付和发货活动完成后才能执行。

简单合并

简单合并模式提供了一种将两个或更多分支合并到流程定义中的单个执行路径的方法。这在有两条或多条路径到达一组共同活动的情况下特别有用。我们可以通过使用简单合并模式来避免重复这些共同的活动集。

在 jBPM 中,可以通过使用 XOR 或排他网关来实现单合并,该网关在触发输出流程之前等待一个输入分支完成。

简单合并

前面的示例流程,处理在线银行客户更改密码的问题,非常适合简单合并的问题陈述。如图所示,为确保安全,用户有两个选择,要么提供借记卡详情,要么回答安全问题。在提供这两种选项中的任何一种之后,都会进行密码确认。排他网关(在确认密码活动之前)在完成任何并行活动后为确认密码活动提供控制。

排他选择

排他选择模式提供了一种将分支发散到多个分支(或执行路径)的解决方案,这样在完成输入分支后,执行流程会根据分支条件精确地转交给一个分支。

在 jBPM 中,可以使用基于数据的排他(XOR)网关来实现这种模式。XOR 网关根据提供的分支条件将执行分支到恰好一个分支。

排他选择

上一图说明了排他选择的场景。示例是制作在线交易流程的一部分。在根据排他网关的条件进行验证后输入密码,然后选择继续的路径。这种模式类似于流程图中的决策框。

隐式终止

隐式终止模式使用户能够从任何分支终止流程。流程引擎验证已完成的工作项并决定流程的终止。这很大程度上避免了混乱,否则我们必须以这种方式设计流程,即这些路径在单个终止点汇合。随着流程中路径数量的增加,这种设计的复杂性也会增加。

隐式终止

上一图显示了一个有两个终止点或终止事件附加的流程。在这里,无论是任务 B完成之后还是任务 C完成之后,流程都会终止。

如果 BPMN 实现不支持隐式终止,用户可以通过将路径合并到公共终止点来实现终止,这被称为显式终止。以下图显示了显式终止:

隐式终止

参考前面的图,我们可以看到使用网关来实现一个共同的终止点。前两种情况下流程执行的结果是相同的,但第二种解决方案(显式终止)更复杂。

延迟选择

延迟选择使业务流程能够在与操作环境的交互基础上选择路径。执行控制等待在决策网关中;选择第一个任务启动的路径作为执行路径。

延迟选择

前面的图显示了在线银行场景中延迟选择模式的实现。该流程是为了使客户能够将其电子邮件与其账户关联,以接收更新、账户报表等。注册后,将向客户发送电子邮件至注册的客户 ID 以完成注册。如果客户在指定的时间内(此处为24 小时)没有响应,则注册失败。

无同步的多重实例

通过使用多重实例MI)功能,我们可以创建一个任务的多个实例。这些实例彼此独立。在多个实例执行之后,不需要同步执行流程,这与合并不同。

jBPM 允许我们建模一个多重实例子流程,可以用来实现 MI 模式。以下图示了一个示例:

无同步的多重实例

在执行任务 A之后,任务 B的执行是通过使用定义B 多重实例子流程的集合表达式来完成的。根据集合表达式中提供的项目数量执行任务 B的多个实例。任务 C任务 B(或任务 B的实例)执行完成之前执行。这种模式特别适用于需要以fire-and-forget方式运行多个任务的情况。

例如,在一个流程中,我们必须向一组用户(例如事件的订阅者)发送电子邮件。在这里,集合表达式将是订阅者的列表。多重实例任务(发送电子邮件任务)将向每个订阅者发送电子邮件。

同步合并

同步合并提供了一种控制合并分支执行流程的方式。当所有传入的“活跃”分支都完成后,执行流程才会合并。

jBPM 通过使用包容网关来实现这种模式。包容网关在分裂时,根据分支条件激活一个或多个分支。在合并时,它等待所有活跃的传入分支完成。

同步合并

前面的流程说明了同步合并场景。根据审批级别的条件,可能需要一或多个审批级别。第二个包容网关确保在所有活动审批完成后才进行到下一个活动的控制。

任意循环

随意循环模式以非结构化的方式解决了在流程模型中重复任务的需求,无需显式构造,如循环运算符。这种模式有助于以可视化的格式表示需要循环的流程模型。

任意循环

前面的图示表明,可以使用连接器和网关循环连接任务。

介绍 jBPM

jBPM 是一个开源的 BPM 套件,拥有完整的工具栈,支持从建模和执行到业务流程的管理和维护的各个方面。它是在 Apache License 2.0 许可下发布的,并由 JBoss 社区开发和积极支持。

工具栈专注于服务于以下两种类型的用户:

  • 模拟应用程序并使用应用程序的业务用户

  • 帮助业务用户使模型可执行并使应用程序完全功能化的技术人员

下面的图将为您提供 jBPM 工具栈及其提供的功能集的整体视图。我们将简要讨论功能组件。每个组件的详细讨论将在后续章节中呈现。

介绍 jBPM

KIE 工作台

Kie 是“知识即一切”的缩写,Kie 工作台是 Drools 世界中所有工具的组合,用于捕获和管理知识。它结合了项目编写、数据模型、规则、决策表、测试服务、流程编写、流程运行时执行、流程模拟和人工任务管理等功能。

Kie 工作台提供了一个 Web 前端和完整的集成环境,用于执行所有 BPM 相关活动。它使用户管理大量工具变得更加容易。其底层架构高度模块化,并且您可以将此功能集的每个部分独立集成到您的 Web 应用程序中,前提是它符合 UberFire 标准。

注意

UberFire 是一个基于 Web 的工作台框架,灵感来源于 Eclipse RPC。它是基于插件的,运行时是 UberFire 兼容插件的组合。

更多信息,请参阅 www.uberfireframework.org/docs/

Kie 工作台的重要性在于它还提供了一种哲学、一个指南和一个过程,用于开发基于知识的系统。它指导您以结构化的方式开发基于知识的系统。

知识生命周期,正如社区所称呼的,是由以下步骤组成的循环:

  1. 发现:推动您公司业务所需的企业知识。

  2. 作者:正式化您的业务知识。

  3. 部署:了解如何配置您的环境。

  4. 工作:减少文书工作。

  5. 改进:提升您的业务性能。

知识生命周期显示了 JBoss 社区在提供现成、基于知识的应用程序开发环境方面的成熟度。

流程设计器

流程设计器是一个基于 Web 的丰富用户界面,允许我们建模符合 BPMN2 规范的业务流程。其目标是提供一个直观的工作台,使业务和技术用户都可以建模和模拟可执行的业务流程。

运营管理

建模的输出是可执行的业务流程。管理工具允许用户管理流程的执行、监控执行并报告执行情况。

jBPM 提供基于 Web 的工具,用于流程管理、监控和报告。实际上,基于 Web 的工具使用核心引擎 API。这些 API 通过表示状态转移REST)、Java 消息服务JMS)和上下文与依赖注入CDI)接口公开,以便它们可以与其他企业级软件系统集成。

Eclipse 工具集

虽然作为一个 BPM 套件,jBPM 专注于面向业务用户,提供大量基于 Web 的工具,但社区坚定地保持脚踏实地,为依赖于构建复杂业务功能的技术用户提供明确的工具。

基于 Eclipse 的工具集是一组 Eclipse IDE 的插件,允许技术用户将 jBPM 环境集成到软件开发环境中。Eclipse 工具集提供以下功能:

  • 创建新 jBPM 项目的向导

  • BPMN 2.0 流程的图形模型器

  • 运行时支持(用于选择要使用的 jBPM 运行时)

  • 调试:在执行过程中可以检查和可视化正在运行的流程的当前状态

  • 流程实例视图,提供显示所有正在运行的流程实例及其状态的详细信息

  • 显示审计日志的审计视图

  • 与 Kie 工作台存储库同步,使 Eclipse 工具集用户和基于 Web 的用户之间能够协作

核心引擎

jBPM 的核心是一个用 Java 构建的业务流程执行引擎。它轻量级且易于嵌入任何 Java 应用程序作为依赖项(作为依赖库)。核心引擎被设计为一个基于标准的、可插拔的、高度可扩展的组件。它支持本地的 BPMN 2.0 规范。

知识库

知识库是我们存储所有流程定义和相关工件的地方。BPM 是一个持续的过程;业务流程持续演变,跟踪更新的流程并提供多个流程版本非常重要。jBPM 使用 Drools Guvnor 作为知识库,并提供以下功能:

  • 流程和相关工件(如流程、规则、数据模型和表单)的持久化存储

  • 部署选定的流程

  • 认证和授权

  • 知识工件分类和搜索

  • 场景测试以确保我们在更新流程时不会破坏任何东西

  • 用于业务流程协作开发的特性,如评论和变更通知

业务逻辑集成平台

同样重要的是要理解 jBPM 是 JBoss 提供的业务逻辑集成平台BLIP)的一部分,该平台包括以下内容:

  • Drools Guvnor(业务规则管理器)

  • Drools Expert(规则引擎)

  • jBPM(流程管理)

  • Drools Fusion(事件处理/时态推理)

一起,它们为企业解决方案的知识型应用开发和管理的完整解决方案。

BILP 可以被视为一个快速企业应用开发平台,从该平台中,应用可以通过建模规则、业务流程流程、事件、数据模型和表单来构建,几乎不需要技术用户的帮助。技术和其工具具有双重焦点:它们明确服务于业务用户,使这些用户能够通过建模直接表达需求,并帮助他们参与应用开发过程,在技术用户的指导下,使模型完全功能化,并实现具有所有品质(非功能性需求)的软件应用。

BLIP 将以下三个范式集成到业务语言驱动的应用中:

  • 业务规则:这代表业务域中的知识

  • 业务流程:这代表在业务域中执行的活动

  • 事件流处理:这将为知识添加时态推理

这些范式虽然作为三个不同的流开发,从定义本身就可以明显看出,但在业务解决方案的背景下是相互关联的。难怪在大多数解决方案架构中,这三个构成了业务建模的基石。

这些范式集成到一个统一平台中,其中一个模块的所有功能都被其他模块利用。Drools Expert 中的决策(从知识库)能力被 Drools Fusion 使用;Drools Fusion 的事件处理能力可以被 BPM 套件 jBPM 使用;而 jBPM 则使用 Drools Expert 内部执行业务规则任务。

下图展示了这种集成以及平台中模块之间的交互。Drools Guvnor 用于设计流程、事件、规则和相关工件;存储它们;并提供集成测试设施。作为一个集成平台,BLIP 允许规则、流程和事件之间的交互。

业务逻辑集成平台

如前图所示:

  • Drools Expert可以根据其规则推理启动业务流程或创建事件

  • Drools Fusion可以根据事件传入的模式基于其时间推理能力触发规则或信号流程;而 jBPM 可以作为其活动之一触发规则或根据业务活动生成事件

我们已经讨论了 jBPM 的概述;现在,让我们在接下来的章节中简要讨论平台中的其他组件。

Drools Guvnor

我们已经在 jBPM 的核心概念部分讨论了知识库,而你猜对了,jBPM 使用 Drools Guvnor 作为其知识库。

Drools Guvnor 提供以下功能:

  • 为知识工件(如流程定义、规则定义、事件和相关工件)提供辅助编写

  • 对使用这些工件进行访问控制和安全保护

  • 版本管理

  • 辅助部署和分发工件到运行时环境

  • 集成测试

Drools Expert

Drools Expert 是一个规则引擎。知识以规则的形式存储在知识库中,这些规则是在声明性语言Drools 规则语言DRL)中定义的。该引擎将传入的数据或事实与这些规则匹配,以做出决策并执行与推理/决策相关的操作。

Drools Fusion

Drools Fusion 是一个事件处理引擎,用于从多个流或事件云中检测和选择感兴趣的事件(业务兴趣)。Fusion 与 Drools Expert 紧密集成。

Drools Fusion 提供以下功能:

  • 时间推理,它允许基于事件的时间因素定义和推理业务规则

  • 对推理中要采取的行动进行调度和延迟

协同工作

既然我们已经讨论了工具,我们现在可以继续可视化这些工具如何协同工作,以创建一个完全由规则驱动的业务解决方案。以下图提供了使用这些工具的可能的解决方案架构的鸟瞰图。

协同工作

一起,它们使业务分析师能够通过将业务场景映射到流程定义、决策的规则定义以及包括时间逻辑在内的规则来编写和管理应用程序。

  1. 设计工具使业务分析师能够将解决方案所需的业务场景映射到规则、流程、事件或相关工件。

  2. 应用程序/用户可以通过使用 jBPM 提供的操作管理工具与解决方案进行交互,或者他们可以使用 RESTful Web 服务接口从他们自己的应用程序中访问它。

  3. 运行时集成了业务处理、规则推理和时间推理能力,以推断决策并为业务场景采取行动。

  4. 知识库模块提供应用程序的存储、版本管理、部署和测试。

从前面的图中,我想强调的另一件重要事情是个体工具所使用的平台技术。用于流程设计和操作管理的基于 Web 的用户界面是使用 UberFire 框架开发的;基于 Eclipse 的工具是基于 Eclipse RCP;运行时是基于 Kie 框架;而 Guvnor 是基于基于虚拟文件系统存储的,默认使用 Git。关于这些平台技术的细节,以及如何定制和扩展 jBPM,将在第七章 定制和扩展 jBPM 中讨论。

红帽还提供了一种商业支持的版本,称为 Red Hat JBoss BPM Suite。

摘要

本章的目标是给你展示 BPM 世界的全景。我们探讨了基本概念、标准、模式、BPM 在工业中应用的使用案例及其益处。我们还探讨了 jBPM 工具栈和 BLIP 的更大图景。这些概念和工具将在本书的后续章节中详细讨论。

在下一章中,我们将讨论如何安装 jBPM 工具栈并使用 jBPM 创建我们的第一个以业务流程为中心的应用程序。

第二章. 构建您的第一个 BPM 应用程序

让我们现在使用 jBPM 工具栈构建我们的第一个 BPM 应用程序。本章将指导您了解以下主题:

  • 安装 jBPM 工具栈

  • 破解默认安装配置

  • 建模和部署 jBPM 项目

  • 在独立 Java 项目中嵌入 jBPM

向本书目标迈进的一步,本章为您提供了 jBPM 工具栈的动手灵活性,并提供了有关破解配置和探索的信息。

安装 jBPM 工具栈

jBPM 发布版附带一个安装 zip 文件,其中包含 jBPM 环境的基本要素和用于构建演示运行时的工具,以便轻松手动管理 jBPM 运行时环境。

下载 jBPM:

  1. 访问 jboss.org/jbpm | 下载 | 下载 jBPM 6.2.0.Final | jbpm-6.2.0.Final-installer-full.zip.

    使用最新稳定版本。本书内容遵循 6.2.0 版本的发布。

  2. 解压并提取安装程序内容,您将找到一个 install.html 文件,其中包含用于安装具有内置项目的演示 jBPM 运行的辅助文档。

注意

jBPM 安装需要安装 JDK 1.6+ 并将其设置为 JAVA_HOME,安装工具使用 ANT 脚本(ANT 版本 1.7+)完成。

安装工具基本上是一个 ANT 脚本,这是一种简单的安装方法,并且可以轻松定制。要操作工具,ANT 脚本由作为工具命令的 ANT 目标组成。以下图将使您更容易理解脚本中可用的相关 ANT 目标。每个框代表一个 ANT 目标,并帮助您管理环境。基本目标包括安装、启动、停止和清理环境。

要运行 ANT 目标,请安装 ANT 1.7+,导航到安装文件夹(通过使用您的操作系统中的 shell 或命令行工具),然后使用以下命令运行目标:

ant <targetname>

提示

下载示例代码

您可以从 www.packtpub.com 下载示例代码文件,该网站包含您购买的所有 Packt 出版物的示例代码。如果您在其他地方购买了这本书,您可以访问 www.packtpub.com/support 并注册,以便将文件直接通过电子邮件发送给您。

安装 jBPM 工具栈

jBPM 安装程序附带一个默认的演示环境,它使用基本的 H2 数据库作为其持久化存储。jBPM 的持久化使用 Hibernate 完成;这使得 jBPM 能够支持包括以下列表中的数据库在内的多种流行数据库:

注意

Hibernate 或 Hibernate ORM 是一个对象关系映射框架,并被 jBPM 用于将数据持久化到关系数据库。更多详情,请参阅 hibernate.org/.

支持的数据库 详情
DB2 www-01.ibm.com/software/in/data/db2/
Apache Derby db.apache.org/derby/
H2 www.h2database.com/html/main.html
HSQL 数据库引擎 hsqldb.org/
MySQL www.mysql.com/
Oracle www.oracle.com/database/
PostgreSQL www.postgresql.org/
微软 SQL 服务器数据库 www.microsoft.com/en-in/server-cloud/products/sql-server/

对于安装演示,请使用以下命令:

ant install.demo

安装命令将安装网络工具和 Eclipse 工具,这些工具对于建模和操作 jBPM 是必需的。

ant start.demo

此命令将启动应用程序服务器(JBoss),其中包含部署了网络工具(Kie 工作台和仪表板)和所有已安装插件的 Eclipse 工具。

注意

请参考install.html,安装器存档中包含的安装文档,以了解安装过程中可能出现的常见错误;它将指导您找到解决方案。安装文档相当详细;请参阅它以获得对安装过程的更深入了解。

安装到此结束!现在,JBoss 应用程序服务器应该正在运行,并且 Kie 工作台和仪表板构建器已部署。

您现在可以通过使用以下 URL 访问 Kie 工作台演示环境,并使用名为admin的演示管理员用户和密码admin进行登录:

http://localhost:8080/jbpm-console

定制安装

演示安装是一个沙盒环境,它允许轻松安装,并减少了您从获取发布版到能够使用堆栈进行操作之间的时间。尽管这非常必要,但您完成初始设置并认真对待 jBPM 时,可能希望安装一个更接近生产环境的 jBPM 环境。我们可以为此目的定制安装程序。以下各节将指导您了解可用的定制选项。

更改数据库供应商

jBPM 演示沙盒环境使用嵌入式 H2 数据库作为持久化存储。jBPM 为 MySQL、PostgreSQL 等更广泛使用的数据库提供开箱即用的支持。按照以下步骤实现使用这些数据库的 jBPM 安装:

  1. 更新安装根目录下可用的build.properties文件,以选择所需的数据库而不是 H2。默认情况下,MySQL 和 PostgreSQL 的配置可用。对于其他数据库的支持,在配置之前请检查 hibernate 文档。

  2. 更新db/jbpm-persistence-JPA2.xml,并将hibernate.dialect属性更新为我们数据库供应商的适当 Hibernate 方言。

  3. 在我们打算部署 jBPM 网络工具的应用服务器中安装相应的 JDBC 驱动程序。

手动安装数据库模式

默认情况下,数据库模式是通过使用 Hibernate 自动生成功能自动创建的。然而,如果我们想手动安装数据库模式,相应的 DDL 脚本在db\ddl-scripts目录中可供所有主要数据库供应商使用。

创建您的第一个 jBPM 项目

jBPM 提供了一种非常结构化的方式来创建项目。这种结构考虑了为拥有多个部门的大型组织创建和维护应用程序。这种结构推荐使用,因为它是一种干净且安全的方式来管理业务流程工件。以下图像详细说明了 jBPM 网络工具(或 Kie 工作台)中项目的组织结构。

创建您的第一个 jBPM 项目

jBPM 工作台假设一个组织有一个业务流程管理套件。一个组织可以有多个组织单元,这些组织单元内部将包含多个项目,并形成项目的根,正如其名称所暗示的,它代表了一个组织的部分。这种分类可以在任何商业组织中可视化,有时也被称为部门。在理想的分类中,这些组织单元将在功能上不同,因此将包含不同的业务流程。使用工作台,我们可以创建多个组织单元。

下一个分类是存储库。存储库是存储业务模型工件,如业务流程、业务规则和数据模型的地方。存储库可以映射到组织内的一个功能分类,如果这些存储库运行多个项目,则可以设置多个存储库;这些项目工件的处理必须相互隔离(例如,出于安全考虑)。

在一个存储库中,我们可以创建一个项目,在一个项目中,我们可以定义和建模业务流程工件。这种结构和抽象对于管理和维护基于 BPM 的应用程序非常有用。

现在我们来详细说明这些步骤。

安装完成后,您需要登录到 Kie 工作台。现在,正如之前解释的,我们可以创建一个项目。因此,第一步是创建一个组织单元:

  1. 通过菜单栏逐级点击,进入作者 | 管理 | 组织单元 | 管理组织单元

    这将带您进入组织单元管理器屏幕;在这里,我们可以看到已存在的组织单元和存储库及其关联列表。

  2. 点击添加以创建一个组织单元,并给出组织单元的名称以及负责管理组织单元项目的用户。创建您的第一个 jBPM 项目

  3. 现在,我们可以添加一个仓库,通过菜单进行导航,并转到创建 | 管理 | 仓库 | 新建仓库

  4. 现在,为仓库提供名称,选择组织单元,并创建仓库。创建您的第一个 jBPM 项目

创建仓库会导致(内部)创建一个 Git 仓库。工作台中 Git 仓库的默认位置是$WORKING_DIRECTORY/.niogit,可以通过以下系统属性进行修改:-Dorg.uberfire.nio.git.dir

  1. 现在,我们可以为组织单元创建一个项目。转到创建 | 项目创建 | 项目资源管理器。现在,从项目分类的面包屑中选择您的组织单元(这里为Mastering-jBPM)。创建您的第一个 jBPM 项目

  2. 点击新建项并选择项目。现在,我们可以通过输入相关项目名称来创建项目。创建您的第一个 jBPM 项目

  3. 这将带您进入以下图中所示的新项目向导:创建您的第一个 jBPM 项目

  4. 这提供了项目名称和项目简要总结的详细信息,更重要的是,提供了项目组 ID、工件 ID 和版本 ID。此外,完成新项目的创建。

对于那些了解 Maven 及其工件结构的您,现在应该已经对项目是如何构建的有所了解。是的!创建的项目是一个 Maven 模块,并且作为单个模块部署。我们将在接下来的章节中详细介绍这一点。

业务流程建模

因此,我们准备好使用 jBPM 创建我们的第一个业务流程模型。

  1. 转到新建项 | 业务流程。业务流程建模

    为业务流程提供名称;在这里,我们尝试创建一个非常原始的流程作为示例。

  2. 现在,工作台将显示用于建模业务流程的过程模型器。如果您认为需要更多空间进行建模,请点击工具栏中的缩放按钮(以下图像中用红色突出显示):业务流程建模

基本上,工作台可以分为五个部分:

  • 工具栏(在顶部):它为您提供了一组用于可视建模和保存模型的工具。

  • 对象库(在画布的左侧):它为您提供所有标准 BPMN 构造图块,您可以将它们拖放到画布上以创建模型。

  • 工作区(在中间):您获得一个工作区或画布,您可以在上面绘制流程模型。画布非常直观;如果您点击一个对象,它将显示围绕它的工具集,以绘制下一个或引导到下一个对象。

  • 属性(在画布的右侧):它提供了与业务流程及其每个构造关联的所有属性的属性值。

  • 问题(在底部):它显示了您当前正在建模的业务流程中的错误。验证是在保存时进行的,并且我们有自动保存选项的设置。

以下截图显示了包含所有描述部分的流程模型器:

业务流程建模

因此,我们可以开始建模我们的第一个流程。我扮演一个希望建模内容写作简单流程的业务分析师的角色。这是一个非常简单的流程,只有两个任务,一个是写作的人工作业,另一个是审查。

我们可以通过转到属性面板并设置演员来附加与任务关联的演员。在这个例子中,为了简单起见,我将它设置为admin,即默认用户。

业务流程建模

现在,我们可以通过使用保存按钮来保存项目;它会要求输入一个签入注释,这将为刚刚保存的流程版本提供注释。流程建模是一个持续的过程,如果正确使用,签入注释可以帮助我们跟踪流程更新的目标。

构建和部署项目

即使创建的项目规模很小,只是一个示例项目,这也是完全功能性的!是的,我们已经完成了一个业务流程,其功能将非常有限,但即使功能有限(如果有),它也可以部署和运行。

前往工具 | 项目编辑器,然后点击构建 & 部署,如图所示:

构建和部署项目

要查看列出的部署,请转到部署 | 部署以查看部署单元,如图所示:

构建和部署项目

这显示了 jBPM 作为使用业务流程的快速应用构建器的有效性。我们可以在几分钟内创建、建模和部署一个项目。

运行第一个流程

在这里,我们开始使用 jBPM 进行操作管理。现在,我们假设自己是操作员工的角色。我们已经部署了一个流程,需要创建一个流程实例并运行它。

  1. 前往流程管理 | 流程定义。我们可以在下面的截图中看到已部署的流程定义的详细信息:运行第一个流程

  2. 点击新建实例并启动流程。这将启动一个流程实例。运行第一个流程

  3. 前往流程管理 | 流程实例以查看流程实例的详细信息并对流程实例执行生命周期操作。

    示例编写流程包含两个人工任务。在流程实例启动时,Write 任务分配给管理员。可以通过任务管理功能管理分配的任务。

  4. 前往任务 | 任务列表运行您的第一个流程

任务列表中,我们可以查看人工任务的详细信息,并执行人工任务的生命周期操作,如分配、委派、完成和取消任务。

在独立 Java 应用程序中嵌入 jBPM

jBPM 的核心引擎是一组轻量级库,可以嵌入到任何 Java 独立应用程序中。这为企业架构师提供了在现有应用程序中包含 jBPM 并利用 BPM 功能的灵活性。

本节将介绍如何以编程方式启动运行时引擎和启动流程实例,并将指导您编写 BPMN 流程的自动化测试。

使用 Eclipse 工具建模业务流程

运行安装脚本后,jBPM 将安装 Web 工具和 Eclipse 工具。Eclipse 工具基本上包括以下内容:

  • jBPM 项目向导:帮助您轻松创建 jBPM 项目

  • jBPM 运行时:选择 jBPM 运行时版本的一种简单方法;这会将特定版本的 jBPM 的库集与项目关联

  • BPMN 模型器:用于建模 BPMN 流程

  • Drools 插件:在 Eclipse 中提供调试和操作管理功能

使用 Eclipse 创建 jBPM 项目

Eclipse Web 工具位于安装根目录中。启动 Eclipse 并创建一个新的 jBPM Maven 项目:

  1. 前往文件 | 新建项目 | jBPM 项目(Maven)。

  2. 提供项目名称和位置详细信息;现在,jBPM 项目向导将执行以下操作:

    • 为您创建一个默认的 jBPM 项目,并设置完整的初始配置

    • 附加所有运行时库

    • 创建一个示例项目

    • 为业务流程设置单元测试环境

  3. 以下截图显示了 jBPM 项目向导。使用 Eclipse 创建 jBPM 项目

  4. Eclipse 工作台与 Web 工具工作台相当相似;以下是一个截图使用 Eclipse 创建 jBPM 项目

与 Web 工具类似,它包含工具箱、工作区、显示 BPMN 构造图元的调色板以及属性浏览器。

我们可以通过转到新建项目向导并选择jBPM | BPMN2 流程来创建一个新的 BPMN 流程。输入流程文件名并点击完成;这将创建一个默认的 BPMN2 模板文件。BPMN2 模型器通过从调色板拖放 BPMN 构造并使用工具集连接它们来帮助可视化建模流程。

以编程方式部署流程

对于以编程方式部署和运行业务流程,您必须遵循以下步骤:

注意

KIE知识即一切 的缩写。

  1. 创建知识库:创建 Kie 服务,它是一个中心,提供对 Kie 提供的服务访问:

    KieServices ks = KieServices.Factory.get();
    

    使用 Kie 服务,创建 Kie 容器,它是知识库的容器:

    KieContainer kContainer = ks.getKieClasspathContainer();
    

    使用输入名称创建并返回知识库:

    KieBase kbase = kContainer.getKieBase("kbase");
    
  2. 创建运行时管理器:运行时管理器通过知识会话和任务服务管理运行时构建,为流程和用户任务创建一个可执行的环境。

    创建用于创建持久化服务、与存储层通信的 JPA 实体管理器工厂:

    EntityManagerFactory emf = Persistence.createEntityManagerFactory(
    "org.jbpm.persistence.jpa");
    

    创建运行时构建器,它是用于创建运行时环境的 dsl 风格辅助工具:

    RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get()
    .newDefaultBuilder().entityManagerFactory(emf)
    .knowledgeBase(kbase);
    

    使用运行时环境,创建运行时管理器:

    RuntimeManager RuntimeManager = RuntimeManagerFactory.Factory.get()
    .newSingletonRuntimeManager(builder.get(), "com.packt:introductory-sample:1.0");
    
  3. 创建运行时引擎:使用运行时管理器,创建一个完全初始化并准备好运行的运行时引擎:

    RuntimeEngine engine = manager.getRuntimeEngine(null);
    
  4. 启动过程:使用运行时管理器,创建一个知识会话并启动过程:

    KieSession ksession = engine.getKieSession();
    ksession.startProcess("com.sample.bpmn.hello");
    

    这将创建并启动一个流程实例。

    从运行时管理器,我们还可以访问人工任务服务并与其 API 交互。

    前往 窗口 | 显示视图 | 其他 | Drools | 流程实例 以查看创建的流程实例:

    以编程方式部署流程

编写自动化测试用例

jBPM 运行时附带一个测试实用工具,它作为自动化测试用例的单元测试框架。单元测试框架使用并扩展了 JUnit 测试框架的功能,基本上提供了 JUnit 生命周期方法和在测试执行后测试和拆除运行时管理器的 jBPM 运行时环境。辅助方法管理知识库和知识会话,获取工作项处理器和断言以断言流程实例和各个阶段。

为了创建 JUnit 测试用例,创建一个扩展 org.jbpm.test.JbpmJUnitBaseTestCase 的类

我们可以使用前面的步骤初始化 jBPM 运行时,并使用 org.jbpm.test.JbpmJUnitBaseTestCase 提供的辅助方法进行断言。

例如,我们可以如下断言过程的完成:

assertProcessInstanceCompleted(processInstance.getId(), ksession);

介绍性示例项目的代码附在此书的下载中。

变更管理 – 更新已部署的流程定义

我们已经建模了一个业务流程并将其部署;应用程序最终用户将通过使用业务流程来创建流程实例并实现他们的目标。现在,随着组织的演变,我们需要对流程进行更改;例如,组织决定增加一个部门。因此,我们必须更新相关的业务流程。

在技术上,在 jBPM 中,我们无法在已部署的流程定义中进行更新;我们需要有一个解决方案。jBPM 建议三种流程迁移策略。

  • 进行:我们将介绍新的流程定义并淘汰旧的定义。淘汰应由应用程序处理,以确保所有对流程的实例调用都重定向到新的流程定义。

  • 终止:现有的流程被终止,我们可以使用更新的流程定义重新启动流程实例。如果更改与流程实例的状态不兼容,我们必须非常小心地处理这种方法。这可能会根据您的流程定义的复杂程度表现出突然的行为。

  • 转移:流程实例迁移到新的流程定义;也就是说,流程实例的状态和活动实例应该进行映射。jBPM 提供的开箱即用的支持提供了一个通用的流程升级 API,可以作为示例使用。

这些策略将在以下章节中详细讨论并举例说明。

摘要

本章将为您带来 jBPM 的“Hello world”动手实践体验。随着您的 jBPM 安装准备就绪,我们现在可以深入探讨 jBPM 功能组件的细节。

通过本章,我们结束了《精通 jBPM》的入门章节。在接下来的章节中,我们将详细讨论流程设计器、运营管理和核心引擎架构。

第三章:使用流程设计器

在前面的章节中,我们介绍了 BPM 的概念,并概述了 jBPM 工具栈及其家族,通过一些实际操作经验来构建我们的第一个 jBPM 应用程序。接下来的四章将详细说明我们迄今为止讨论的每个 jBPM 组件,而剩余的章节将指导你如何定制 jBPM,将其集成到应用程序架构中,并部署一个生产就绪的 BPM 应用程序。

对于一个手头有应用程序需求的分析师来说,第一步将是设计和建模业务流程。这一步通常并不直接,除了业务流程本身之外,还涉及多个工件。本章将带我们经历设计一个包含所有礼仪的业务流程的旅程,并将模拟业务流程以了解设计好的业务流程在运行时环境中的行为。

本章将指导你完成以下内容:

  • 使用 BPM 进行业务流程建模

  • 数据对象建模

  • 在 BPM 活动中使用脚本和逻辑

  • 建模与人类活动相关联的用户界面表单,以便从人类参与者那里获取输入

  • 模拟业务流程以了解其运行时特性

  • 使用基于 Web 和 Eclipse 的工具来处理同一个项目,以及业务用户和技术人员之间的协作

我们将讨论如何通过使用基于 Web 的流程设计器来完成上述操作。也讨论了基于 Eclipse 的工具,但只是简要介绍,仅突出建模操作中的差异。

基于 Web 的工具

基于 Web 的 jBPM 工具是 Drools Guvnor 知识库提供的用户界面框架的扩展。该框架提供了一个平台来创建、维护和运行基于知识的应用程序的多个知识资产。jBPM 扩展(命名为 jBPM-WB,即 jBPM 工作台的简称)利用平台的功能来创建和维护应用程序,并利用平台的能力提供与业务流程相对应的知识资产的用户交互。这种抽象有助于 jBPM 工作台轻松集成家族中其他软件的资产,并为业务逻辑集成平台创建一个集成开发环境。

在本节中,我们将关注 jBPM 中知识资产(即业务流程定义、流程和任务表单以及数据模型)的资产编辑器。

流程建模

jBPM 提供的流程设计器是一个集成环境,用于设计、验证和模拟业务流程。以下屏幕截图突出了流程设计器的六个不同部分:

流程建模

jBPM 流程设计器的一部分

它们如下所示:

  • 1: 对象库

  • 2: 流程画布

  • 3: 属性编辑器

  • 4: 工具栏

  • 5:问题可视化屏幕

  • 6:元数据

以下各部分将在以下子节中详细介绍。

对象库

对象库是构建业务流程所使用的构造的调色板。对象库主要包含符合 BPMN 的构造。该库按下拉菜单排列,构造类别作为标题。创建业务流程的用户可以从调色板中选择一个构造,并将其拖放到流程画布中。以下截图显示了默认对象:

对象库

除了 BPMN 构造之外,对象库还包含工作流模式。我们已在第一章业务流程建模 – 连接业务与技术中讨论了这些工作流模式,它们是常见过程设计场景的解决方案模板。

开发者可以选择通过添加扩展和自定义任务来自定义对象库;我们将在专门用于定制的章节中讨论这些内容。

流程画布

流程画布是我们的工作区;我们通过拖放对象库提供的 BPMN 构造,连接和自定义它们来创建业务流程。流程设计器帮助我们创建一个技术上可部署的业务流程,并通过排列和放置对象、调整对象大小和更改颜色模式来建模图表的美学。以下截图显示了使用流程设计器设计的示例业务流程:

流程画布

一个有用的工具,社区将变形菜单称为流程画布中的一个伟大功能。一旦拖放并选择对象,我们就可以在对象周围看到一个工具菜单。菜单中可用的工具将因对象而异。有三个菜单,分别位于对象的顶部、底部和右侧;它们具有独特的工具集合。

菜单的右侧部分包含帮助用户主要在流程上工作的工具,而无需始终依赖对象库的拖放功能。用户可以向画布中添加任务或网关对象,包括连接器,添加附件,或添加数据对象。以下截图突出了菜单的右侧部分。

流程画布

菜单的顶部部分包含以下实用工具:

  • 将任务添加到流程字典(这将在本章后续部分详细讨论)

  • 查看节点源,有助于查看对象的 BPMN 源流程画布

以下截图显示了突出显示的顶部菜单和节点源窗口的视图,该窗口显示了任务对象的源。

底部菜单包含将 BPMN 对象转换为类似对象的工具。例如,我们放置了一个服务任务,必须将其更改为人类任务。我们必须删除任务,用另一个任务替换它,并恢复到它的连接。使用此功能,点击工具图标后,我们将获得可以交换的对象列表,通过选择另一个对象,我们可以更改对象。这是一个方便的工具,尤其是在维护一个非常大的业务流程流时。

以下图像显示了任务元素上可交换对象的菜单:

流程画布

属性编辑器

除了业务流程图的可见部分外,业务流程中的每个对象都有可以自定义的属性。例如,在人类任务中,我们必须分配一个参与者或一个组。属性编辑器帮助设计师设置这些细节。通过选择对象可以获得每个对象的属性编辑器面板。为了设置流程级别的属性,我们必须点击画布本身。

属性编辑器面板有以下四个部分:

  • 核心属性:核心属性部分包含对特定 BPMN 元素至关重要的属性。

  • 额外属性:正如其名所示,这些属性是除了核心属性之外的非强制属性。例如,任务的文档。

  • 图形设置:可以更改以改善业务流程美感的属性。设计师将具有更改背景、边框、字体颜色和字体大小的灵活性。

  • 模拟属性:模拟属性是创建流程模拟所需的设置。这些属性的详细信息将在流程模拟部分讨论。属性编辑器

前面的截图显示了流程的属性编辑器,显示了 BPMN 流程的核心、额外和模拟属性。

每个 BPMN 结构和其目的的核心和额外属性在第五章BPMN 结构中详细说明。

工具栏

工具栏包含一组辅助工具,有助于创建和维护业务流程。大多数工具对于任何流程图编辑器(如剪切、粘贴、保存、重命名、删除和缩放)都是通用的,并提供高级功能,如表单建模和流程模拟。

大多数工具从其名称本身就可以轻易理解,可能不需要详细讨论。我们将在以下部分讨论需要关注的特定功能。

问题可视化

在我们继续设计业务流程时,了解我们业务流程中的语法和语义错误非常重要。问题部分显示了我们的应用程序中的这些错误。

以下截图显示了刚刚开始的一个 BPMN 流程中的错误;也就是说,它只包含一个起始节点。错误表明起始节点没有出向连接,并且流程没有结束节点:

问题可视化

为用户提供的另一个条款是可视化特定流程中的问题。此条款将编辑器切换到验证模式。可以从工具栏访问(参见图表),点击开始验证将显示流程中的错误列表:

问题可视化

此外,如果错误在特定的对象中,该对象将被突出显示。点击突出显示的对象以获取错误列表。

设计师通过将业务流程与一组预配置的规则进行验证来显示这些错误;这些规则是可以定制的。(请参阅第七章,定制和扩展 jBPM以获取定制技术)。

元数据选项卡

元数据选项卡显示了业务流程的详细信息作为一个工件。它包含有关创建和修改该工件的用户、工件格式等信息。此外,还有一个关于工件的讨论条款,这在我们有较大的分布式团队的业务分析师创建和管理业务流程的场景中非常有用。

数据对象建模

业务领域建模的一部分是识别问题域中的所有实体、实体之间的关系及其属性。与这些实体相关的数据通常通过业务流程进行收集、更新和删除。因此,这些实体成为我们业务流程的一部分。

例如,客户是一个几乎在每一个业务领域都会出现的数据实体,客户创建(添加客户的过程)是一个非常常见的业务流程。JBPM 提供了数据建模功能,这些实体可以作为数据对象创建并包含在业务流程中。

让我们通过一个例子来讨论数据建模。我们考虑一个非常原始的客户创建流程,其中包含一个用于捕获客户详情的人工任务和一个用于调用客户创建服务的服务任务。以下图像显示了正在讨论的业务流程。

数据对象建模

要使用流程设计器创建Customer数据对象,请按照以下步骤操作:

  1. 登录到工作台。

  2. 前往创建 | 项目创建 | 工具 | 数据模型器,然后点击创建按钮。以下屏幕将弹出:数据对象建模

  3. 要创建客户数据对象,提供一个标识符、标签和包名以唯一标识此数据对象。

好吧,正如您可能已经注意到的,还有一个名为超类的(可选)字段。这需要在 jBPM 数据对象设计的技术方面进行一些解释。

我们建模的数据对象被创建为一个具有我们指定的属性并作为其依赖项包含在应用程序中的 Java 对象。超类指向 Java 的继承特性,我们可以在数据对象具有父子关系的情况下使用此特性。

在创建数据对象后,我们可以使用创建新字段选项(见以下截图)来添加与数据对象对应的属性。以下截图显示了添加到客户数据对象的名称年龄性别属性:

数据对象建模

保存数据对象。现在,我们必须在业务流程中利用这个数据对象。

在业务流程中,我们有一个人类任务,它将客户信息作为输入并映射到任务输出集中。为了做到这一点,在流程设计器中,将客户对象添加为人类任务输入和输出集中的类型

以下截图显示了捕获客户详情人类任务的属性编辑器,其中使用了客户数据对象:

数据对象建模

表单建模

继续讨论我们关于原始客户创建的例子,下一个问题将是如何为人类任务(捕获客户信息)提供用户界面。表单建模为此场景提供了一个解决方案。业务分析师可以设计一个表单,用于捕获与任务相关的信息。

要精确一点,通过表单收集的数据被映射到人类任务的数据输出集中。当我们运行创建客户业务流程时,“捕获客户信息”任务被分配给一个参与者(或一组),并将出现在收件箱中。为了完成任务,用户必须将详细信息输入到由引擎根据表单模型渲染的用户界面中。

创建表单

因此,让我们探索如何为业务流程中包含的任务建模表单。

  1. 创建客户流程中,选择捕获客户****信息任务。

  2. 在突出显示的工具菜单中,顶部包含编辑相关表单的工具。点击该工具,并选择图形建模;这带我们到表单建模屏幕:创建表单

对于建模表单,首先,我们必须创建数据来源,其中我们定义数据输入集到表单变量的映射以及表单变量到数据输出集的映射。在这种情况下,我们必须映射客户变量的数据输入和数据输出。

我们必须填写IdInput IdOutput Id字段,并选择表单的渲染颜色。此外,我们必须选择表单必须表示的数据模型,然后单击添加数据持有者以将数据模型添加为表单的数据来源。

以下截图显示了添加客户数据来源后的表单建模屏幕:

创建表单

现在,我们可以建模表单的外观和感觉。为此,我们选择按来源添加字段选项卡,其中列出了数据来源,并将客户数据对象中附加的字段添加到表单画布中(如下截图所示)。

选择一个字段,使用工具栏编辑字段属性,并在表单中移动字段,以改善美观。

创建表单

设置字段属性

对于表单中包含的每个字段,我们都可以指定一组属性,这些属性决定了其运行时行为和布局。

要使用属性面板(如下截图所示),选择字段,然后在工具菜单中单击编辑按钮。

属性包括以下内容:

  • 用于验证数据完整性的属性

  • 输入文本的大小

  • 支持的最大字符数

  • 是否必填

  • 只读或非只读

  • 是否为密码字段

  • 用正则表达式表示的数据模式

  • 用于指定外观和感觉的属性

  • 标签和字段的 CSS 样式

  • 如果数据类型有多个用户界面,则字段的类型;例如,字符串可以捕获在字段或文本区域中,或通过使用富文本编辑器

  • 用于数据绑定的属性

  • 用于提高可用性的属性

  • 设置默认值

  • 使用公式推断值

以下截图显示了字符串类型数据的属性编辑器:

设置字段属性

设置默认值

在数据输入屏幕中,一种通用的可用性和生产力提升技术是为字段提供默认值。表单模型器通过使用 Java 和XPath表达式支持此功能。

注意

XPath 是一种查询语言,可用于选择 XML 文档中的节点。请参阅en.wikipedia.org/wiki/XPath

假设在我们创建客户过程的示例中,我们必须将字段性别的默认值设置为男性,假设在问题域中男性客户比女性客户多。因此,在属性编辑器的默认值公式字段中,我们可以使用以下截图中的表达式:

设置默认值

另一个常见的场景是将日期字段默认设置为当前日期。

设置默认值

推断字段值

在数据录入屏幕中,我们还需要的一个功能是基于其他字段的值填充某些字段的值。例如,如果我们正在捕获产品的信息(比如说,一台笔记本电脑),将会有多个成本组件,如价格、税值和折扣。产品的总成本是从这三个组件中得出的。因此,我们可以通过使用一个 XPath 表达式来设置计算总成本的公式,该表达式引用了价格、税值和折扣字段(见以下截图)。

推断字段值

使字段只读即可完成这个技巧。

子表单

在一个表单中,通常包含多个部分。例如,在捕获客户信息时,我们还需要捕获地址信息。此外,有两种类型的地址:一种是永久地址,另一种是通信地址。考虑到表单设计中的可重用性,建模这种场景最优雅的方式是创建一个单独的地址表单,并在客户表单中包含两次。

表单模型器通过使用一个名为子表单的功能来支持这种场景,其中我们可以在一个表单中包含另一个表单。为了实现这一点,我们必须创建地址表单,并且必须转到客户表单按类型添加字段选项卡,添加简单子表单,并将默认表单的属性选择为已创建的Address.form

子表单

多个子表单

另一个要求是在表单中拥有相同项目的多个对象。考虑订单管理流程的场景。我们必须捕获订单信息。一个订单通常由两部分组成:一部分是订单本身,包含有关请求者是谁以及何时提出请求的信息,另一部分是请求的项目,包括产品和订购的数量。这可能有很多;也就是说,请求者可能在订单中有多个项目。捕获这些细节的典型表单将如以下截图所示:

多个子表单

此外,点击添加订单项后,我们将得到一行订单项。

多个子表单

我们如何在表单模型器中建模这种场景?

按照子表单的方法进行。为订单和项目创建数据模型。为订单和项目创建表单。在订单表单中,使用多个子表单表单类型。

如果我们选择多个子表单的属性编辑器,我们将获得许多选项来改善表单的外观和感觉,控制可以在表单中看到哪些操作,等等。尝试一下,感受一下它的效果。

处理模拟

到目前为止,我们已经讨论了很多关于业务流程建模以及 jBPM 为此提供的辅助工具。现在,我们可以将建模的流程部署以了解其运行时特性。等等!我们不是还需要分析和验证业务流程的特性吗?JBPM 提供了分析建模业务流程运行时特性的工具,这被称为业务流程模拟。

流程模拟帮助我们做到以下几方面:

  • 通过预执行优化业务流程设计

  • 理解参与业务流程的人类角色的资源利用率

  • 通过预测和分析理解业务流程的性能特性

  • 通过最小化变化引起的错误来持续改进业务流程设计

我们可以通过使用如下图像所示的示例业务流程流程来继续讨论流程模拟。流程流程描述了一个在线交易的业务流程,包括收集客户信息、发送一次性密码和验证密码。选择此流程设计进行说明的考虑是为了有多个流程路径:

流程模拟

流程路径

流程路径功能帮助流程设计者看到业务流程中所有可能的路径组合。这将帮助我们理解我们使用的逻辑中存在的任何缺陷。

我们可以从如下截图所示的设计工具栏中访问流程路径工具:

流程路径

选择流程路径菜单;它将计算并列出流程流程可能采取的路径。选择其中一条路径,然后点击显示路径以在流程画布中突出显示:

流程路径

前面的截图显示了带有高亮路径的流程。这个工具在设计复杂业务流程时特别有用。

模拟参数

除了业务流程中可用的信息外,流程模拟引擎还需要一些参数输入;这些参数共同描述或定义了业务流程模拟的场景。为创建场景而需要捕获的参数因业务流程中业务流程元素类型的不同而异。例如,我们需要捕获与模拟资源利用相关的人类任务的工作时间。

可以使用属性编辑器更改模拟属性,并在选择不同的流程元素时发生变化。以下截图显示了显示人类任务(提供一次性密码)的模拟属性的属性编辑器:

模拟参数

下文详细说明了为各种流程元素必须设置的模拟参数。

流程级别参数

流程级别的模拟属性如下:

  • baseTimeunit:在模拟场景中使用的单位时间。所有表示时间的值都将被视为以该单位表示,除非在本地覆盖。

  • baseCurrencyUnit: 在模拟场景中使用的基准货币单位,使用 ISO 4217(三位字母代码)标准表示。例如,美元表示为USD。与baseTimeUnit类似,所有表示货币的值都将被视为以该单位表示,除非在本地覆盖。

注意

ISO 4217 是一个国际标准,旨在为货币的表示提供国际上认可的标准代码。

任务级参数

任务级模拟属性如下:

  • unitCost(标记为每时间单位成本):这是执行任务必须支付的每时间单位成本。成本以baseTimeUnitbaseCurrencyUnit属性数量表示,默认值为零。

  • distributionType:这指定了在场景中执行多个业务流程实例期间此任务的处理时间的统计分布。

jBPM 支持的统计分布类型如下:

  • 均匀分布:均匀分布或矩形分布对所有处理时间最小值和最大值之间的值具有相同的概率

  • 正态分布:它是自然界中常见的一种分布,表示为对称的钟形曲线,由处理时间的平均值和标准差值指定

  • 泊松分布:用于估计给定时间内的到达次数,由平均处理时间指定

    注意

    要深入了解分布类型,请尝试理解概率分布;参见en.wikipedia.org/wiki/Probability_distribution

  • staffAvailability:可用于任务的资源数量,默认设置为 1。此参数仅适用于人工任务。

  • workingHours: 人力资源的工作时间。此参数也仅适用于手动(人工)任务。

流元素参数

附属于序列流的模拟属性是概率,即控制传递到此元素的概率。这附属于序列流过程元素。如果我们为从网关发出的两个序列流中的每一个指定 50%的概率值,这意味着两个序列流发生的概率是相等的。

运行模拟

现在,我们已经讨论了设置模拟属性,我们可以运行模拟。菜单与流程路径一起在流程设计工具栏中可用:

运行模拟

我们将被提示提供有关要模拟的实例数量和过程模拟之间的间隔等详细信息。模拟过程是异步的,一旦完成,模拟标签页将填充模拟结果。

模拟结果

模拟的总结信息位于模拟标签页的右侧。这包含模拟信息和用于在不同级别(如过程、过程元素和路径)导航模拟结果的层次结构。以下截图显示了示例过程的总结信息:

模拟结果

结果部分内容丰富,提供了多种类型的图表供选择,以多种方式表示数据。在那里尝试不同的选项。以下是在模拟结果中包含的主要数据集:

过程模拟结果:如果我们选择查看执行结果的过程(从显示的层次结构中),我们可以查看以下内容:

  • 执行时间结果:这显示了执行的最大值、最小值和平均值。

  • 活动实例:这显示了在模拟期间创建的活动(任务)实例。

  • 总成本:显示业务流程预计成本的最小值、最大值和平均值。

  • 以下图像显示了在线交易业务过程的执行时间结果:模拟结果

  • 过程元素级模拟结果:选择层次结构中的每个过程元素以显示过程元素级模拟结果。对于服务任务,这基本上显示了执行时间的最小值、最大值和平均值。此外,对于人工任务,它还显示了资源分配细节、资源等待时间以及成本因素。

  • 路径模拟结果:路径模拟结果显示了路径被执行的次数及其百分比贡献。

基于 Eclipse 的工具

在本章的前几节中,我们专注于使用 jBPM 提供的基于 Web 的工具建模过程和实用工具。基于 Web 的工具主要面向业务分析师。业务分析师能否独立创建应用程序?我们必须承认我们还没有达到那里,但当然,只剩下几个差距就可以达到那个目标。在今天的课程中,我们需要技术人员的帮助来创建一个完全功能的应用程序。

jBPM 工具将这一现实考虑在内,以更新的 Eclipse 工具的形式,帮助开发者创建 jBPM 应用程序或与业务分析师团队合作完成应用程序。

导入使用基于 Web 的工具创建的项目

在本章中,我们讨论了如何使用 Eclipse 工具集创建 jBPM 项目。在这里,我们将讨论如何将过程设计器中创建的项目(可能由业务分析师角色的人创建)导入 Eclipse 并继续工作。

jBPM 中知识库的默认实现使用 Git,这是一个广泛使用的源代码管理系统。因此,当我们使用基于 Web 的工具集创建项目时,实际上是在 Git 中创建项目。Git 支持人们并行工作于一个项目,这个功能可以用于基于 Web 的工具集用户和 Eclipse 工具集用户之间的协作工作。

因此,首先,我们应该知道 jBPM 用作示例项目知识库的 Git 存储库位置:

  1. 登录基于 Web 的工具集,并转到项目创作 | 管理菜单。将显示一个存储库和项目的列表。

  2. 选择我们想要的项目,我们可以看到 Git 存储库 URL。将有两个 URL 可用:一个使用git协议,另一个用于ssh协议。本节的截图如下:使用基于 Web 的工具集创建的项目导入

  3. 使用可用的工具复制 URL。

  4. 打开 Eclipse(与 jBPM 安装一起安装)。

  5. 转到文件 | 导入 | 从 Git 导入项目,点击下一步,选择URL,然后点击下一步。我们将看到以下截图所示的窗口。

  6. 输入 ssh URL 和认证详情:使用基于 Web 的工具集创建的项目导入

  7. 按照向导操作并完成项目导入;在 Eclipse 的包资源管理器中,我们将看到导入的项目。项目结构是 Java 项目的 Maven 项目结构。探索一下,我们可以看到我们创建的数据对象、表单和业务流程:使用基于 Web 的工具集创建的项目导入

流程建模

基于 Eclipse 的工具集包含一个用于建模业务流程的 BPMN 图编辑器。该编辑器提供了与基于 Web 的过程设计器类似的功能。它由一个画布组成,我们可以在此画布上直观地展示业务流程,画布,它充当 BPMN 对象库,以及用于设置每个 BPMN 元素属性的属性编辑器。以下截图展示了 BPMN 图编辑器:

流程建模

我们不会详细讨论编辑器的功能,因为它们与基于 Web 的工具集所解释的功能类似。

数据对象建模

对于数据对象建模,jBPM 不提供任何可视建模工具,但在创建数据对象时内部生成的源代码提供给开发用户。此源代码是 Java 代码,对象以纯 Java 对象POJO)的形式表示,并使用知识 API 的注解进行装饰。

以下截图显示了在流程设计器中创建的 Order.java,用于在流程设计器中创建的 Order 数据对象:

数据对象建模

表单建模

与数据对象建模类似,开发人员工具不包括任何用于表单建模的视觉工具,但作为开发者可以修改的原始文件。

扩展名为 .form 的文件包含表单内的属性,而扩展名为 .ftl(指 FreeMarker 模板文件)的文件包含表单的布局信息。

注意

关于 FreeMarker 模板的更多详细信息,请参阅 en.wikipedia.org/wiki/FreeMarker

流程模拟

对于流程模拟没有可用的工具,开发者必须依赖单元测试用例和调试工具来分析流程的运行时特性。

注意

关于编写单元测试用例和处理实例的视图在 第二章 的 编写自动化测试用例 部分进行讨论,构建您的第一个 BPM 应用程序

保存更改到知识库

在基于 Web 的工具中,保存的工件将在知识库中反映出来。在 Eclipse 基础上的工具在此方面略有不同。保存的文件将仅反映在我们的本地文件系统中;为了与知识库(此处为 Git 仓库)同步,我们可以使用 Eclipse 工具进行提交并将更改推送到 Git 仓库。

  1. 右键单击项目(包资源管理器),然后转到 团队 | 提交。它将带您到以下截图所示的提交屏幕。

  2. 选择要移动到存储库的工件,提供提交信息,并使用 提交和推送 按钮将更改推送到知识库:保存更改到知识库

摘要

本章重点介绍了可用于流程设计的工具,并详细介绍了 Web 和基于 Eclipse 的工具中可用的各种功能,分别针对商业用户和技术人员。本章还帮助用户了解如何在流程设计中协同使用 Web 和基于 Eclipse 的工具。

现在,我们已经相当详细地讨论了流程设计,让我们在下一章中探索可用于运营管理的工具。

此外,第五章,BPMN 构造 可以被视为流程设计的扩展,其中我们将详细讨论可以包含在业务流程中的每个 BPMN 构造。

第四章:操作管理

本章将通过以下主题(重点关注 jBPM KIE 工作台和相关工具)向您展示执行 jBPM 操作所需的所有任务:

  • jBPM 环境配置:Git 和 Maven 仓库、组织单元以及通过基于角色的访问控制(RBAC)进行的基本管理和权限管理

  • 新的 jBPM 资产管理功能和模块部署

  • 流程和任务管理

  • 使用 BAM 的工作示例进行 jBPM 审计和历史日志分析

  • 使用 jBPM Executor 进行作业和命令调度

本章要求您对 Git 和 Maven 都有实际了解,它们在 KIE 工作台架构中扮演着核心角色。您将被要求使用 Git 并将工件部署到 Maven。让我们首先回顾 jBPM 6.2 开发系统的典型软件架构,目的是阐明新系统组件及其交互方式。

KIE 工作台、Git 和 Maven 的概述

首先,jBPM 6.2 架构可能看起来有点难以理解,因为已经集成了几个新组件,为开发者提供了行业标准工具,以简化源代码管理、构建/部署。jBPM 平台与 Git 和 Maven 仓库集成,以便您可以共享 Kie 业务资产并将 Kie 模块发布到远程团队。让我们看看 Git 和 Maven 如何融入 Kie 平台(如下所示图像)。

KIE 工作台、Git 和 Maven 的概述

KIE 工作台管理来自其 Kie Git 仓库(无论是全新的还是从远程仓库克隆而来)的资产。所有 Kie Git 仓库都可以在 .niogit 文件夹中找到。Kie 部署过程将模块安装到 Kie Maven 仓库中(位于 repositories/kie 文件夹)。此仓库可以通过 Git 或 SSH 协议公开访问。

使用 Git

KIE 工作台使我们能够创建一个新的空 Git 裸仓库,或者将远程 Git 仓库克隆到一个全新的 Kie 裸仓库中。然而,工作台不允许我们将资产导入到 Kie 仓库的现有分支中。

注意

“裸”仓库在 Git 中作为拥有一个中心(主要是远程)仓库的方式存在,许多人可以向其推送内容。有关裸 Git 仓库的详细信息,请参阅官方 Git 文档。

我们可以从 创作 | 管理 菜单(仓库 项)管理仓库。现在让我们让 Kie 仓库发挥作用。

克隆远程仓库

我们将克隆来自 GitHub 的 chapter 4-pizza 示例仓库。它托管了两个项目,我们将在本章后面使用这些项目来实验部署过程。要克隆远程仓库,请打开 仓库 | 克隆仓库 对话框(参见以下截图)并按以下配置参数进行设置:

  • 仓库名称chapter4-pizza

  • 组织单位:使用 jBPM 提供的默认设置或创建一个新的(在此阶段这不相关;让我们将其值设置为 demo

  • Git URLhttps://github.com/masteringjbpm6/chapter4-pizza.git克隆远程仓库

我们已经说过,克隆的和新的仓库都放在你可以在 KIE 安装文件夹中找到的 .niogit 文件夹中。此外,所有 KIE 仓库默认共享以下 URL:

git://localhost:9418/{reposname}ssh://localhost:8001/{reposname}

其中 {reposname} 是你在对话框窗口中提供的存储库名称(例如,chapter4-pizza)。

小贴士

从不直接从 KIE .niogit 文件夹克隆仓库;始终使用仓库 URL。

.niogit 文件夹还包含用于存储元数据和设置的 system.git Kie 仓库;我们将在下一节中查看它。

修改并提交

从 KIE 工作台修改项目资产意味着你的更改将被提交到 KIE Git 仓库。让我们编辑 jBPM 流程定义,看看保存资产时会发生什么。打开作者 | 项目作者菜单(项目资源管理器),将路径更改为 demo/chapter4-pizza/pizzadelivery;你应该在业务流程组下看到pizzadelivery流程(见以下截图)。

修改并提交

只需拖动并移动,例如,流程开始节点,然后选择保存菜单项。系统会提示你输入提交注释;输入 workbench editing 并按确定。现在,如果我们去检查 .niogit/chapter4-pizza.git 仓库并执行以下命令:

git log

我们可以看到以下两个提交(较旧的提交是在 GitHub 仓库初始创建时执行的,另一个是你的最后一个提交):

commit 165a0b07f1b50962696640bcb79072458f4c82d4
Author: admin <>
Date:   Sun Mar 22 11:33:16 2015 +0100
 workbench editing {/pizzadelivery/src/main/resources/pizzadelivery.bpmn2}
commit a32f9a2a9bc835e74abcb78348878d8d2fc96140
Author: admin <fiorini.s@gmail.com>
Date:   Mon Mar 23 07:48:50 2015 +0100
 pizza commit

我们可以通过指定提交 ID 来获取第一个提交文件:

git show --name-status -r a32f9a2a9bc835e74abcb78348878d8d2fc96140
Date:   Mon Mar 23 07:48:50 2015 +0100
 pizza commit
A       pizzadelivery/.classpath
A       pizzadelivery/.project
A       pizzadelivery/.settings/org.eclipse.bpmn2.modeler.core.prefs

推送到远程仓库

现在我们首先使用以下命令检查 chapter4 仓库的远程源设置:

git remote -v

以下信息将被打印:

origin  https://github.com/masteringjbpm6/chapter4-pizza.git (fetch)
origin  https://github.com/masteringjbpm6/chapter4-pizza.git (push)

为了更新远程源 GitHub 分支(master),我们执行以下推送:

git push origin master

由于仓库受保护,你将需要输入仓库用户名和密码(我们在这里使用我们的账户):

Username for 'https://github.com': masteringjbpm6
Password for 'https://masteringjbpm6@github.com': *****

为了清晰起见,已移除...

To https://github.com/masteringjbpm6/chapter4-pizza.git
79bcf3a..36b57c1  master -> master

最终更新了远程分支。

注意

对于修复和增强,我们鼓励你为我们示例 GitHub 仓库做出贡献;如果你想在本地实验示例项目,请遵循下一节。

新建仓库

通过将我们的 GitHub 仓库克隆到 KIE 仓库中,你无法(因为你不是贡献者)将更改推送到上游。除了在 GitHub 上进行分支操作外,如果你想让 KIE 集中管理你的项目,创建一个新的空 Kie 仓库是正确的选择。为此,请按照以下步骤操作:

  1. 创建一个新的 KIE Git 仓库;请记住,目前不要勾选管理仓库选项,因为这会将您的仓库变成一个 Maven 感知的仓库,使得从外部源添加项目时难以调整(我们将在资产管理示例段落中解决它)。

  2. 使用您首选的 Git 客户端从命令行克隆远程 GitHub 仓库。

  3. 将您的 Git 工作副本远程 origin 更改为新的 Kie 仓库。

  4. 提交并推送到 Kie 仓库的master分支。

备注

请参阅Git 克隆和管理仓库部分,以获取从 GitHub 创建仓库和项目设置的完整示例。

jBPM 6.2 引入了普通 Git 仓库(未管理)和一种新的“智能”仓库(管理)之间的区别,我们可以通过在新建仓库对话框窗口中设置管理仓库选项来创建这种类型的仓库。

管理资产 - 管理仓库

正如我们所见,使用 KIE 管理 Git 项目源和 Maven 可能具有挑战性:克隆、提交、设置仓库远程、推送到上游等等,我们甚至没有在我们的示例中考虑 Git 分支。jBPM 6.2 通过引入一个新功能(资产管理)简化了仓库和项目源代码的管理,该功能旨在通过一系列 jBPM 工作流程在各个阶段推动开发、构建和发布过程。简而言之,管理仓库提供项目 Maven 版本控制和 Git 分支管理。

管理工作流程

资产管理工作流程不是完全自动的;它们还需要一个管理演员(必须是kiemgmt角色;参见 KIE 工作台roles.properties文件)来完成特定任务,以便使工作流程进展(选择要发布和/或审查的资产)或向用户提供信息数据(例如错误数据)。工作流程仅在以下预定义操作发生时触发(请记住,这仅适用于管理仓库):

  • 创建或配置仓库:在创建 Git 仓库或选择仓库配置按钮后,工作流程可以自动为您添加一个dev分支和一个release分支(master分支始终是默认分支)。

  • 资产提升:当用户认为他的/她的资产准备好发布时,他/她可以通过选择提升按钮提交它们进行所谓的提升。提升需要管理用户选择(Git cherry picking)并批准提交的更改,通过将它们提升到 Git 发布分支,或者延迟过程以供稍后审查。

  • 项目构建:用户执行构建,选择特定的仓库分支。构建涉及将项目作为一个 Maven 模块编译并安装到内部 Kie 仓库中。

  • 发布:用户执行 发布。发布功能涉及在仓库级别的构建和部署过程。所有仓库项目都会构建,然后发布到 Kie 运行时(部署到运行时选项),以便可以使用业务资产。我们只能从以 release 标签开头的分支发布(例如,release-1.0.1)。

注意

请查阅 jBPM 6.2 用户指南的第九章,以获取有关资产管理工作流的更多详细信息。

Git 克隆和管理仓库

jBPM 6.2 不支持导入到管理仓库;简单的解决方案模仿了我们在(未管理的)新建仓库 段落中看到的方法,略有不同;让我们看看如何导入我们的 GitHub chapter4-managed 仓库项目:

让我们创建一个多模块仓库;每一步的设置如下:

  1. 仓库名称: ManagedVesuvio,和 组织单元: demo(目前不相关)。

  2. 多项目仓库(勾选),自动配置分支(勾选),和 项目设置(保留默认设置)。

    注意

    在步骤 2 中,指定的 Maven GAV 将是 Maven 父模块 GAV (demo:ManagedVesuvio:1.0.0-SNAPSHOT)。创建了两个额外的分支:dev-1.0.0release-1.0.0。如果我们没有选择自动分支管理,则只有默认的 master 分支可用,整个仓库无法发布(有关更多信息,请参阅 发布 部分)。

  3. 克隆 chapter4-managed 示例项目:

    git clone https://github.com/masteringjbpm6/chapter4-managed.git
    
    
  4. 添加一个名为 kievesuvio 的新远程仓库(或者如果您愿意,替换 origin):

    git remote add kievesuvio ssh://admin@localhost:8001/ManagedVesuvio
    
    
  5. 切换到 chapter4-managed 文件夹,并添加文件和提交:

    git add .
    git commit -m "first Kie managed commit"
    
    
  6. master 分支获取更新并推送到 KIE ManagedVesuvio dev-1.0.0 分支:

    git pull kievesuvio master
    git push kievesuvio master:dev-1.0.0
    
    
    1. 在此阶段,dev-1.0.0 分支已更新,但 KIE 项目 资源管理器 并未意识到仓库结构中的分支变化。这是由于仓库结构显示了 Maven 多模块配置(更多详情请见 管理仓库和 Maven 部分),并且其 pom.xml 文件已过时。我们必须手动将 napolivesuvio 项目添加到其中。
  7. 在文件资源管理器(创建 | 管理)中,点击 pom.xml 并添加以下 <modules> 元素:

    <modules>
      <module>napoli</module>
      <module>vesuvio</module>
    </modules>
    

保存文件后,KIE 应该会识别项目,并且仓库结构应该显示我们的模块。

Git 克隆和管理仓库

仓库现在已正确配置,我们准备处理资产管理功能。

资产管理示例

napolivesuvio项目各包含一个基本流程定义。napoli流程(napoli.bpmn2)包括vesuvio流程(vesuvio.bpmn2)作为一个可重用的子流程(更多关于 BPMN2 元素的内容请见第五章,BPMN 结构)。用户通过在仓库 | 仓库结构视图中选择适当的按钮(见以下截图)以及在任务 | 任务列表窗口中完成人工任务来执行资产管理任务。

资产管理示例

资产管理任务仅分配给拥有kiemgmt角色的用户;因此,请确保您使用admin用户登录到 KIE 工作台,因为这是唯一预设的拥有此角色的用户。

提升资产

我们将资产(napoli 和 vesuvio 模块)提交到发布分支进行提升:

  1. 选择项目 | 编写和仓库 | 仓库结构;选择提升按钮,并输入目标分支:release-1.0.0

  2. 任务 | 任务列表窗口中,你现在应该被分配了一个选择资产提升的任务;点击它,认领任务,提升所有资产,并完成任务,如图所示:提升资产

  3. 返回到仓库结构,通过选择release-1.0.0分支,你应该能看到两个提升的项目。

    注意

    资产现在已合并到 Git 仓库release-1.0.0分支。

发布

仓库结构中,选择release-1.0.0分支并按下发布按钮。可选地,提高模块的发布版本,切换部署到运行时(用户:admin,密码:admin,服务器 URL:default),然后继续。

注意

napolivesuvio现在已安装到 KIE Maven 仓库,你可以在编写 | 工件仓库工件中找到它们。如果你选择了部署到运行时,包含的流程定义将在流程管理 | 流程定义中可用。

构建(单个项目)

发布流程始终通过构建过程运行所有管理的仓库项目;构建/发布过程也可以通过构建菜单在单个项目上对所有类型的仓库(管理/非管理)执行:

  • 构建与安装:将工件部署到 Kie 仓库和系统 Maven 仓库(如果有)

  • 构建与部署:执行安装步骤(见前文),然后,将模块部署到 Kie 运行时:业务工件可用于运行时使用

  • 我们将在KIE 部署部分讨论更多关于部署的内容

资产版本历史

所有资产的 Git 版本信息都可在概览选项卡中找到。例如,通过点击napoli流程,我们可以看到所有的提交日志,并且可以使用选择当前按钮加载资产的先前 Git 版本(见以下截图;注释可能有所不同)。

资产版本历史

管理仓库和 Maven

管理的 Git 仓库本身存储在repositories/kie仓库中的 Maven 模块中。它们可以是单一项目或多个项目的仓库;这会影响 Maven 化 Kie 项目的配置方式。

  • 单一项目仓库包含一个 Kie Maven 项目

  • 多项目仓库包含一个 Maven 多模块项目

父模块pom.xml文件显示它是一个pom(多)模块,包含napolivesuvio模块:

<groupId>packt</groupId>
<artifactId>ManagedVesuvio</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>ManagedVesuvio</name>
<modules>
 <module>napoli</module>
 <module>vesuvio</module>
</modules>

napoli模块的pom.xml文件显示它依赖于vesuvio模块:

<parent>
 <groupId>packt</groupId>
 <artifactId>ManagedVesuvio</artifactId>
 <version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>packt</groupId>
<artifactId>napoli</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>napoli</name> 
<dependencies>
  <dependency>
 <groupId>packt</groupId>
 <artifactId>vesuvio</artifactId>
 <version>1.0.0-SNAPSHOT</version>
 </dependency>
</dependencies>

注意

请参阅maven.apache.org/guides/mini/guide-multiple-modules.html以了解 Maven 多模块管理的介绍。

管理流程定义

jBPM 资产管理流程定义及其相关业务逻辑可以在名为guvnor-asset-mgmt的 Drools 项目中找到。此模块是预部署的,并由 KIE 工作台加载;它通常列在工件仓库中,您可以在部署 | 流程部署窗口中找到其流程定义,并在流程实例窗口中找到其运行实例。

最后的注意事项

KIE 工作台项目编辑器只允许您编辑、构建和发布适当的 Kie 模块,而不是普通的 Maven 模块(这些模块缺少kmodule.xml文件)。因此,如果您只有一个 Kie 模块,将大量模块克隆到 Kie 多项目管理的仓库中并没有太多意义;请尝试用一点盐来设计您的仓库内容,以确保您的 Kie 运行时和开发环境始终保持清洁和健康。现在,我们不再使用 KIE 工作台来完成下一个示例(留给读者作为练习),而是切换到另一个工具,即 KIE CLI(代表命令行界面),并查看执行常见操作任务的不同方法。

kie-config-cli 工具概述

第二章,构建您的第一个 BPM 应用程序(见创建您的第一个 jBPM 项目部分)介绍了几个 KIE 控制台功能(仓库和组织管理、模块部署等)。KIE CLI 实用程序为管理员/用户提供在控制台/shell 级别执行与组织单元、仓库、用户权限和部署管理相关的常见任务的工具。

注意

工具项目托管在 GitHub 上:github.com/droolsjbpm/kie-wb-distributions/tree/master/kie-config-cli

您可以从 JBoss 快照 Maven 仓库下载工具发行版:

snapshots.jboss.org/maven2/org/kie/kie-config-cli.

上一节详细解释了 KIE 如何处理源项目的 Git 仓库,并预览了 KIE 系统 Git 仓库的存在;KIE CLI 工具与之接口,并在两种模式下操作:

  • 在线(默认和推荐):启动时,它通过 kie-wb 中嵌入的 Git 服务连接到系统仓库。所有更改都是本地的,并且仅在显式执行 push-changes CLI 命令时才发布到上游。exit 命令将发布所有本地更改;要在退出 CLI 时丢弃本地更改,应使用 discard 命令

  • 离线:直接在服务器上创建和操作 Kie 系统仓库(不可用 discard 选项)

系统仓库存储 KIE 工作台的私有配置/设置数据:编辑器的行为、组织组、安全等。

系统仓库位于 KIE .niogit 文件夹中(.niogit/system.git)。

注意

核心 Git 后端功能由 Red Hat Uberfire 框架提供。www.uberfireframework.org

默认情况下,KIE 工作台通过其后端服务监控 system.git 仓库的更改,并相应地更新其用户界面。在设置完下一个示例环境之后,我们将继续在下一节中描述系统仓库。我们本可以通过 KIE 工作台的功能来完成这项工作,但我们希望您亲自动手,让 KIE 控制台发挥作用,熟悉幕后发生的事情。

连接(在线模式)

启动工具(通过安装文件夹中的 kie-config-cli 脚本)后,让我们首先连接到本地主机上的 KIE 系统仓库。

小贴士

由于安全原因,应优先考虑 SSH 协议而不是 Git 协议。

************* Welcome to Kie config CLI ****************
>>Please specify location of remote Git system repository [ssh://localhost:8001/system]
ssh://localhost:8001/system
>>Please enter username:
admin
>>Please enter password:
admin

创建组织单元

KIE 工作台需要组织单元来创建仓库和用户,并实施 RBAC 规则控制,以执行某些任务。

create-org-unit
>>Organizational Unit name:packt
>>Organizational Unit owner:admin@packt.org
>>Default Group Id for this Organizational Unit:com.packt
>>Repositories (comma separated list):
Result:
Organizational Unit packt successfully created

创建仓库

我们已经看到如何从 KIE 工作台克隆 Git 仓库。现在让我们创建一个新的 Git 仓库(本地、裸、未管理;无需用户名/密码),我们可以在此存储我们的新项目。

create-repo
>>Repository alias:masteringjbpm6
>>User:
>>Password:
>>Remote origin:
Result:
Repository with alias masterjbpm6 has been successfully created

定义基于角色的访问控制规则

为了完成示例设置过程,我们在 masterjbm6 仓库中设置了一些 RBAC 规则;该组织未设置任何角色,因此为了限制对仓库的访问,我们向仓库对象添加角色:

add-role-repo
>>Repository alias:masteringjbpm6
>>Security roles (comma separated list):user, analyst
Result:
Role user added successfully to repository masterjbpm6
Role analyst added successfully to repository masterjbpm6

添加 jBPM 控制台用户

users.propertiesroles.properties 定义了可以登录 jBPM 控制台的用户(及其角色)。

让我们编辑 users.properties 并添加两个新用户:

simone=simone
arun=arun

编辑 roles.properties 并将用户与我们之前创建的角色关联起来:

simone=admin
arun=admin

将仓库添加到组织中

每个 KIE Git 仓库都必须绑定到一个组织;组织负责控制对其的访问并给予组织用户操作权限。

add-repo-org-unit
>>Organizational Unit name:packt
>>Repository alias:masteringjbpm6
Result:
Repository masteringjbpm6 was successfully added to Organizational Unit packt

推送到 system.git

push-changes 命令将更改发送到 KIE system.git 仓库:

push-changes
>>Result:
>>Pushed successfully

因此,你的 KIE 工作台会得到更新,显示你 UI 上的更新设置。

更多关于系统.git 仓库的信息

到目前为止,你应该对 system.git 仓库的作用有所了解;作为一个最后的练习,让我们克隆它并查看其内部结构:

git clone file:///$JBPM_HOME/.niogit/system.git

注意

记住,永远不要从 KIE CLI 工具外部向系统仓库推送更改;这可能会搞乱整个 KIE 工作台安装!

系统仓库包含一些实体(组织单元和仓库)以及内部配置文件;在这里,我们可以找到我们全新的组织和仓库描述文件:

  • masteringjbpm6.repository

  • packt.organizationalunit

masteringjbpm6.repository 文件内容如下(请注意 security:role 设置):

<group>
  <name>masteringjbpm6</name>
  <description></description>
  <type>REPOSITORY</type>
  <enabled>true</enabled>
  <items>
    <entry>
      <string>scheme</string>
      <item>
        <name>scheme</name>
        <value class="string">Git</value>
      </item>
    </entry>
    <entry>
      <string>security:roles</string>
      <item>
        <name>security:roles</name>
        <value class="list">
          <string>user</string>
          <string>analyst</string>
        </value>
      </item>
    </entry>
    <entry>
      <string>branch</string>
      <item>
        <name>branch</name>
        <value class="string">master</value>
      </item>
    </entry>
  </items>
</group>

现在我们已经完成了我们的新 KIE Git 仓库和环境配置,我们准备好处理新的部署功能和 Kie-Maven 集成,这是我们下一个示例的主题。

KIE 部署

jBPM 6 平台引入了一个全新的部署流程;之前利用 Guvnor 包(由Java 内容仓库JCR)和 Drools KnowledgeAgent (changeset.xml) 支持的专有机制)被广泛采用的 Apache Maven 工具所取代。这在工具配置(更倾向于约定/配置)以及支持、标准化和部署灵活性方面都大大提高了开发过程。

当你部署你的项目时,你实际上创建了一个 KIE 部署单元(KJAR);这个模块是一个 Maven 启用的项目,是一个包含所有项目业务资产(流程、工作项处理器、业务规则、表单等)以及其知识会话和运行时声明性元数据描述符(META-INF/kmodule.xml)的压缩标准 Java 归档。

注意

kmodule.xml 文件在官方 jBPM 和 Drools 文档中有详细说明。

KIE 模块的唯一 ID 是从其 Maven GAV(分组 ID,工件 ID,版本)开始构建的,并添加了知识库名称(默认知识库名称是空的;我们将在第六章,核心架构中回到这一点),例如:

groupID:artifactID:version{:kbasename}

jBPM 运行时使用 Drools KIE-CI 组件在配置的 Maven 仓库中自动搜索其他 Maven 模块(通过嵌入在 KIE JAR 中的项目 pom.xml 文件或通过 Maven settings.xml 文件)来解决 KJAR 依赖项。您还可以使用 kie.maven.settings.custom 系统属性并指向任何 Maven settings.xml 文件。

注意

请参阅第六章,核心架构,特别是关于仓库和扫描器部分,以获取有关运行时类加载和解决模块依赖项的详细信息。

默认的 KIE Maven 仓库工件(repositories/kie 文件夹)可通过以下 URL 远程访问:http://{jbpmconsole-host}:{port}/jbpm-console/maven2wb/

现在我们来总结在部署过程中执行的核心操作:

  • 将模块安装到 KIE Maven 仓库的 Maven 安装

  • 将模块部署到您的系统 Maven 仓库(使用 Maven 主目录中的 Maven settings.xml 文件,或者检查 kie.maven.settings.custom 系统属性)。

  • 被称为 DeploymentStore 的 jBPM 数据库表通过部署描述符(XML 格式)进行更新。此更改是在 jBPM 6.2 版本中引入的;在此之前,部署信息存储在 system.git 仓库内。

提示

发布/部署操作永远不会部署 Kie 模块的依赖项;为了避免运行时错误中缺少依赖项,您必须在将 Kie 模块资产投入使用之前,通过将它们安装到 Maven 仓库中提供正确的依赖项工件。

以下图显示了与 KIE 控制台一起工作时标准的 Maven 仓库配置;KIE 项目首先部署到内部 KIE Maven 仓库,然后与远程Maven 仓库同步,然后为任何应用程序提供远程公共访问(HTTP),以便解决其依赖项。

KIE 部署

工件和 KIE 模块

一个 KIE 模块可能依赖于多个额外的 Maven 工件。KIE 工作台通过选择创作 | 工件仓库菜单项为您提供对您的 KIE Maven 仓库的视图。路径列显示工件的 Maven 仓库 URL;为了澄清,让我们看一下我们从 ManagedVesuvio 仓库发布的 napoli Kie 模块:

工件名称napoli-1.0.0.jar

路径packt/napoli/1.0.0-SNAPSHOT/napoli-1.0.0.jar

此工件可在以下 Maven 工件 URL 获取:

http://{jbpmconsole-host}:{port}/jbpm-console/maven2wb/packt/napoli/1.0.0-SNAPSHOT/napoli-1.0.0.jar

请记住,虽然工件仓库是 KIE Maven 仓库内容的快照,但 KIE 部署 | 部署菜单项仅显示有效的 KIE 模块(KJARs),这些模块是从 KIE 控制台运行时加载和验证的。

提示

org.guvnor.m2repo.dir:系统属性设置 Maven 仓库文件夹的路径;默认是 ${jBPM-install-directory}/repositories/kie

部署选项

KJAR 艺术品必须始终部署到 KIE 控制台的 Maven 仓库,以便我们可以集中管理它们,并使控制台的行为保持一致。鉴于这一点,多亏了 Maven 仓库风格的引入和新一代工具(KIE 控制台和 Eclipse BPMN 工具),你可以以相当灵活的程度定制部署过程以适应你的开发环境。

这里有一些可行的部署选项:

  • 从 KIE 控制台创建一个项目(默认为 Maven 化),到一个配置好的 KIE Git 仓库;从 Eclipse 克隆并拉取,从 Eclipse 继续开发(添加业务模型等),推送到 KIE(KIE 控制台自动刷新其仓库视图);从 KIE 构建和部署。

  • 从 Eclipse 创建一个 Maven 项目(请记住添加 kmodule.xml 文件和 pom.xml 中的 jBPM 依赖项);创建所有你的业务资产,并从 Eclipse 开发一个单元测试;推送到 KIE;也许在 KIE 中做一些修复,然后构建和部署。

  • 如前所述,从 Eclipse 创建一个 Maven 项目;使用 Maven 安装(无论是从 Eclipse 还是命令行);让 KIE 控制台通过上传控制台中的新工件来宣传新的 KIE 部署模块(部署 | 部署)。

  • 从 Kie 控制台,从远程 Git 仓库创建一个仓库克隆;创建一个项目,添加资产,然后保存和提交。部署到 Maven 并将更改推送到远程 Git 仓库。

  • 多亏了其 Maven 和 Git 集成,KIE 平台可以非常灵活地适应复杂开发环境。

以示例方式部署 – 披萨项目

我们预览了使用 ManagedVesuvio 仓库发布流程安装和部署的过程,其中两个 KIE 模块(一个依赖于另一个)被发布。让我们通过一个不同的模块示例来亲自动手:一个 KIE 模块(主项目)和一个普通模块(依赖项)。以下是一些披萨示例项目:

  • Pizzadelivery:KJAR 模块(它包含流程定义)

  • Pizzamodel:一个实用项目,包含用 Java 类建模我们的业务对象(订单、披萨等)的类。

Pizzadelivery 项目依赖于 Pizzamodel 项目。让我们首先回顾一下示例流程定义(我们将在后面的段落中使用相同的流程作为我们的 BAM 示例解决方案)。

流程定义 – pizzadelivery

示例流程定义捕捉了一个典型的外卖披萨流程:

  1. 下单后,Nino 通过电话管理收到的订单(处理收到的订单任务)。

  2. Maria 从 Nino 那里获取订单详情,并将一个粘性便条交给披萨制作人员(订单分配任务)。

  3. 一个披萨制作师(马里奥或路易吉)开始准备披萨(制作披萨任务),同时,玛丽亚将订单配送分配给一个披萨男孩(分配配送任务)。交付分配任务和制作披萨任务的完成(并行任务,更多内容请参阅 第五章,BPMN 构造)意味着披萨已经准备好交付。

  4. 萨尔瓦托雷交付披萨(披萨配送任务)。流程定义 – pizzadelivery

部署披萨

首先,我们通过 Maven 使依赖项可用,然后,通过 KIE 工作台部署模块。让我们将源代码导入到我们的新 KIE 仓库(在 创建仓库 部分中设置的仓库)。

Git 和项目配置

到目前为止,你应该已经非常熟悉将源项目导入到 KIE 仓库中:

  1. 从我们的示例 GitHub 仓库克隆示例仓库:

    git clone https://github.com/masteringjbpm6/chapter4-pizza.git
    
    
  2. 将克隆的仓库项目添加到本地(未管理)的 masteringjbpm6 仓库部分:

    git remote remove origin
    git remote add origin ssh://localhost:8001/masteringjbpm6/
    
    
  3. 通过执行 git remote 命令,我们可以看到以下内容:

    $ git remote -v
    origin ssh://admin@localhost:8001/masteringjbpm6 (fetch)
    origin ssh://admin@localhost:8001/masteringjbpm6 (push)
    
    
  4. 现在我们只将样本 KIE 模块项目推送到新的远程仓库(origin):

    git add pizzadelivery
    git commit –m "pizzadelivery: first kjar"
    git push -u origin master
    
    

此处的目的是通过 Git 只发送 KIE 控制台(KIE 项目),而不是创建额外的问题来源。我们现在将通过我们的 Maven 仓库提供 KIE pizzadelivery 项目的 Maven 依赖项(pizzamodel.jar)。

部署依赖项

通过切换到 项目创作 | 项目资源管理器,我们可以找到 PizzaDelivery 项目(通过 packt/masteringjbpm6 仓库面包屑导航)。按下 打开项目编辑器 按钮,从下拉列表中选择 项目设置 | 依赖项 项,我们可以看到 pizzadelivery 模块依赖于 pizzamodel 工件(见下图),而这个工件尚未存在于 Maven 仓库中。

部署依赖项

消息选项卡报告了这个问题,相应地:

未解决的依赖项 com.packt.masterjbpm6:pizzamodel:1.0.0-SNAPSHOT

提供工件

现在发生的事情应该是清晰的:KIE 依赖项机制(我们将在 第六章中讨论的“扫描器”,核心架构)在扫描可用的 Maven 仓库时无法解决 pizzadelivery 项目的依赖项(从其 pom.xml 文件加载);为了解决这个问题,我们将通过以下两个步骤提供缺失的工件:

  1. Maven 安装:在 pizzamodel 项目根目录下执行 mvn clean install(或使用 Eclipse 运行 Maven 构建 集成功能)将构建和安装的工件放入我们的 Maven 仓库,以便在运行时解决依赖项。

  2. 艺术品仓库上传:转到 Kie 控制台 创作 | 艺术品仓库 页面,并点击 上传 按钮;从你的 Maven 仓库文件夹(com/packt/masterjbpm6)中选择 pizzamodel JAR 文件。KIE 控制台将把工件复制到其内部 Maven 仓库。提供工件

现在工件已经可用,如果你在 消息 面板的 刷新 按钮上点击,问题应该会得到解决。

部署

现在我们已经准备好部署。在 项目资源管理器 选项卡中,选择 工具 | 项目编辑器 菜单项。检查 PizzaDelivery 模块的 Maven GAV 属性是否正确,然后点击 构建 & 部署 按钮。

部署中

请注意,在部署过程中,KIE 控制台将尝试解决和验证你的所有项目依赖项,可能会访问多个远程 Maven 仓库(取决于你的配置),并花费一些时间来完成。确保你的互联网连接是活跃的,或者设置一个 Maven 代理仓库(强烈推荐)。

应用服务器控制台跟踪以下信息:

18:32:33,517 INFO  [org.drools.compiler.kie.builder.impl.KieRepositoryImpl] (default task-106) KieModule was added: MemoryKieModule[releaseId=com.packt.masterjbpm6:pizzadelivery:1.0]
18:32:34,547 INFO  [org.jbpm.console.ng.bd.backend.server.DeploymentManagerEntryPointImpl] (default task-106) Deploying unit com.packt.masterjbpm6:pizzadelivery:1.0
18:32:35,969 INFO  [org.jbpm.kie.services.impl.store.DeploymentSynchronizer] (default task-106) Deployment unit com.packt.masterjbpm6:pizzadelivery:1.0 stored successfully

小贴士

请记住,无论是否有活动(运行)的流程实例,你都不能部署具有相同 ID 的部署单元(覆盖它);需要执行卸载操作(参见下一节)。

日志确认部署已成功完成;我们可以在 部署 | 部署 选项卡中看到我们的 KJAR 模块。记住,为了部署过程成功(至少对于 6.2 版本的 jBPM 来说),你的 kmodule.xml 必须要么:

  • 声明一个空的 <kmodule> 元素

  • 声明一个具有以下属性的 <ksession> 元素:type="stateful" default="true"

部署中

为了彻底起见,检查 jBPM 数据存储中的 DEPLOYMENTSTORE 表中的部署条目;我们应该看到已经添加了一个新行。

表行有一个 DEPLOYMENTID 列:com.packt.masterjbpm6:pizzadelivery:1.0.0 和一个 DEPLOYMENTUNIT 列,其中包含实际的部署描述符:

<org.jbpm.kie.services.impl.KModuleDeploymentUnit>
  <artifactId>pizzadelivery</artifactId>
  <groupId>com.packt.masterjbpm6</groupId>
  <version>1.0.0</version>
  <strategy>SINGLETON</strategy>

</org.jbpm.kie.services.impl.KModuleDeploymentUnit>

这实际上是部署过程的第三步(参见 KIE 部署 部分)。

手动添加 KIE 模块

即使你已经在 KIE 工作台外部(仅在你的系统 Maven 仓库中,可能使用 Eclipse IDE 和 Maven 安装目标)构建和安装了你的 KIE 模块,你仍然可以在以后将其部署到 KIE。手动添加部署单元意味着你正在使这个(Maven 安装的)模块对 KIE 运行时可用。从 部署 | 部署 视图,你可以添加新的部署单元(新建部署 按钮);只需提供你想要部署的项目 Maven GAV 即可,可选地提供知识库和知识会话名称。

小贴士

此外,您可以选择符合您要求的ksession运行时策略:单例、按请求或按流程实例(有关运行时策略,请参阅第六章,核心架构)。

KIE 工作台将提供一个新的kmodule.xml文件和元信息,将您的普通 JAR 模块转换为新的 KIE 模块。如果匹配的 Maven 工件不在 KIE Maven 存储库中,则无法创建新的部署单元。

应用了流程和任务管理 - 披萨配送流程

KIE 工作台允许您管理流程实例并与流程任务交互。您可以执行以下操作:

  • 向特定流程实例或批量信号(广播到所有流程实例)发送信号。

  • 中断特定流程实例或执行批量中断 - 中断将终止实例(们)和所有挂起的任务。与流程及其任务相关的所有数据都将从 jBPM 数据库表中删除。

  • 获取流程详细信息 - 详细信息页面包括从默认 jBPM 数据库审计表中获取的审计日志(我们将在最后一节和第六章,核心架构)中讨论审计)。

  • 开始、释放和完成任务。

启动流程

打开流程管理 | 流程定义选项卡,并单击我们新部署的 PizzaDelivery 流程定义旁边的启动图标按钮;一个新的流程实例开始。

取消部署

取消部署操作仅从 jBPM 数据库表中删除部署单元的配置文件,而将 Maven 工件保留在原位。

用户管理

我们在示例设置部分介绍了 PizzaDelivery 流程;该流程需要五个不同的真人演员:

  • 订单管理:Nino

  • 订单分配:Maria

  • 披萨烹饪:马里奥或路易吉

  • 配送分配:Maria

  • 披萨配送:Salvatore

为了使用 KIE 控制台来练习我们的第一个流程实例,我们需要将这些流程参与者(演员)添加到 KIE 运行时。编辑$JBOSS_HOME\standalone\configuration文件夹中的user.propertiesroles.properties文件;这些用户将由 KIE 工作台自动添加到任务服务数据库表(ORGANIZATIONALENTITY,更多内容请参阅第六章,核心架构)。

将演员(指定认证密码)添加到user.properties文件中:

nino=nino
maria=maria
salvatore=salvatore
mario=mario
luigi=luigi

roles.properties文件中指定用户的角色(默认角色user足以执行任务):

nino=user
salvatore=user
mario=user
luigi=user
maria=user

小贴士

无需重新启动应用程序服务器即可使其获取新设置。

任务管理

要完成流程实例,请按照以下顺序执行步骤:

  • 以 Nino 身份登录启动完成****处理 incoming 订单任务

  • 以 Maria 身份登录认领完成****分配订单任务

  • 以马里奥(或路易吉)身份登录认领完成制作披萨的任务

  • 以玛丽亚身份登录认领完成分配递送的任务

  • 以萨尔瓦托雷身份登录认领完成递送披萨的任务

所有任务已完成。流程实例结束,查看流程实例详情标签页,我们可以看到实例日志跟踪(事件按逆时间顺序显示):

实例日志:

22/nov/14 23:35:53: 8 – EndNode
22/nov/14 23:29:54: 7 - deliver the pizza (HumanTaskNode)
22/nov/14 23:29:54: 6 - Join
22/nov/14 23:27:50: 6 - Join
22/nov/14 23:26:56: 4 - make the pizza (HumanTaskNode)
22/nov/14 23:26:56: 5 - assign the delivery (HumanTaskNode)
22/nov/14 23:26:56: 3 – Split
22/nov/14 22:41:56: 2 - Assign the order (HumanTaskNode)
22/nov/14 18:10:05: 1 - Handle Incoming Order (HumanTaskNode)
22/nov/14 18:10:05: 0 - Receive Pizza Order (StartNode)

Split 日志记录并行分叉网关的激活。Join 日志记录并行汇聚网关的传入连接的激活。跟踪日志是从 NODEINSTANCELOG 表中加载的。

正如你可能意识到的,从 KIE 工作台中完全测试流程定义并不容易;在演员之间切换是一个繁琐且耗时的任务……想想一个包含大量人工任务和演员或群体的复杂流程。我们将通过使用 BAM 示例和下一章中的测试自动化来了解如何克服这些问题。

管理作业和异步命令执行

从 jBPM 6 开始,平台提供了一种新的调度服务(称为 Executor),它允许你安排、执行和管理异步作业任务。Executor 可以用作通用 Java 批处理调度设施,也可以用作能够执行异步流程任务的服务(有关更多详细信息,请参阅第五章,BPMN 构造异步任务部分)。例如,资产管理功能内部安排不同类型的命令(为了获得一些想法,请打开部署|作业窗口,如图所示):CreateBranchCommandListCommitsCommandBuildProjectCommandMavenDeployProjectCommand 等。

管理作业和异步命令执行

Executor 服务执行预先配置的 Command 类;Command 是一个 Java 类,它执行一组在 jBPM 流程上下文之外运行的业务语句,并通过一系列接口(CommandContextExecutionResults)与 Executor 通信,这些接口强制执行参数传递。

Job 类持久化存储在 REQUESTINFO jBPM 数据库表中,而结果错误问题则持久化存储在 ERRORINFO 表中。

创建、安排和启动新的作业

简单明了的通用 Job 定义(没有 jBPM 上下文可用)需要你提供要安排的类的类名(请参阅以下截图)。

  1. 类型字段中输入类名com.packt.masterjbpm6.command.SimpleCommand,在名称字段中输入SimpleCommandJob类必须包含在 KIE 工作台应用程序(jbpm-console.war)的类路径中,因此可以将pizzamodel.jar文件复制到WEB-INF/lib中的 exploded WAR,或者将其复制到 jBPM 设置文件夹的dependencies文件夹中,并使用在 jBPM build.xml文件中可找到的 Ant 目标install.jBPM-console.into.jboss重新构建控制台应用程序。

  2. 设置到期时间(调度)和可选的重试次数Job类在失败后可以重新启动的次数),以及参数。

  3. 参数(上下文数据)通过CommandContext类在执行时传递给Job实例。参数必须是可序列化的。创建、调度和启动新的 Job

创建后,任务变为队列状态(如下面的截图所示),将在预定时间执行。不同的 Job 状态如下:队列完成取消错误重试运行

创建、调度和启动新的 Job

流程定义转换

转换涉及将流程定义从旧版本格式移动到新版本格式。当将较旧的流程定义升级到新的 jBPM 发布格式时,jBPM 6 为我们提供了一些(相当实验性的)选项:

  • 使用 jBPM Web 流程设计器的菜单功能从旧的专有 jBPM JPDL 3/4 导入到 BPMN2

  • 使用jBPM5migrationtoolprojectjbpmmigration-0.13.jar或更新的版本进行即席迁移(也支持 API 映射)

jBPM5migrationtoolproject项目的目标是向现有的 jBPM 用户提供一些迁移工具,以便从 jBPM5 迁移。

注意

项目主页和 Wiki 页面在此处可用:developer.jboss.org/wiki/jBPM5migrationtoolproject。该项目托管在 GitHub 上:github.com/droolsjbpm/jbpmmigration

流程定义版本控制和实例升级

根据企业业务需求和业务组织,流程可能会随着时间的推移以非常不同的速度发生变化;需要解决几个业务迁移案例:

  • 一个复杂的业务流程可能需要数月才能完成(可能由于手动任务),然而,业务人员需要尽快提供更新的流程定义,因为一些旧的遗留系统必须集成到流程中。

  • 流程定义需要修复,但该流程定义的多个实例正在运行,我们不希望终止它们并让用户从工作流开始处重新启动。

除了增加过程定义版本属性(数字)之外,这只是一个助记符,不会影响流程实例的行为,因此,为了反映版本号,命名您的流程 ID(字符串)是一个好的做法,因为引擎本身不提供任何版本跟踪机制,例如:

com.packt.masteringjbpm6.pizzadelivery_v1_0,
com.packt.masteringjbpm6.pizzadelivery_v1_1

这样,你就可以在保留审计数据并保持分离的同时,灵活地在不同版本的过程定义之间切换实例化。实际上,每个实例都与其过程定义(通过 ID)绑定,并且在实例完成之前不得覆盖。

为了支持用户在不同过程定义之间迁移流程实例,jBPM 6 引入了WorkflowProcessInstanceUpgrader类。

WorkflowProcessInstanceUpgrader.upgradeProcessInstance 方法首先将流程实例从信号中断开连接,然后事件处理管理器逐个遍历流程实例节点,尝试通过从您提供的映射数据中获取 uniqueID 来将节点映射到目标流程定义节点。

uniqueID 是由引擎通过连接节点父(容器)元素的 ID 生成的内部标识符,例如:

// create the node mapping data
Map<String, Long> mapping = new HashMap<String, Long>(); 
// top level node 1 must be mapped to a new node with id 2
mapping.put("1", 2L);
// node 3, which is inside a composite node 4, must be mapped to a new node with id 5
mapping.put("4.3", 5L); 
// upgrade old processInstance to a new process instance definition with id= com.packt.masteringjbpm6.pizzadelivery_v1_1
WorkflowProcessInstanceUpgrader.upgradeProcessInstance( ksession, processInstance.getId(),"com.packt.masteringjbpm6.pizzadelivery_v1_1", mapping);

对于复杂的过程定义,这种解决方案总体上还远未完善;建议在可能的情况下实施自己的过程迁移。

BAM

业务活动监控器(BAM)提供了构建系统外可定制的 KPI 的工具,这对于管理层在采取主动决策时非常有用。该术语由 Gartner Inc. 定义(www.gartner.com/it-glossary/bam-business-activity-monitoring),指的是企业数据的实时聚合、分析和表示(可能将其与系统利益相关者和客户相关联)。

BAM 的目标是产生(近)实时信息,关于 jBPM 业务系统的操作、流程和交易的状态和结果;这支持企业管理层采取反应性决策(决策支持系统DSS)),并帮助员工识别关键区域(可能的问题来源)。

以下是一些示例:

  • 采用 JIT 生产业务模式的企业必须持续监控其生产和采购流程,并将它们与 incoming orders 和 business providers 相关联

  • 电信公司需要概述其服务,提供操作以便能够及时了解其客户

BAM 通常需要与 BI/数据仓库工具集成;第一类工具是实时(面向数据的多源异构),而第二类是历史业务数据。随着 NoSQL 数据库引擎、大数据和基于云的平台的出现,这一趋势今天正在迅速转变,转向处理流处理(实时)以及批量处理(复杂事件处理CEP))的新一代工具。

BPM 和 BAM

BAM 的主要 jBPM 数据来源是引擎审计服务和 jBPM 数据库表。

审计数据可能对某些业务系统很重要,而对其他系统则无足轻重。审计和日志历史数据可能对您的系统/平台软件是一项繁重的任务,并且在 I/O 和/或分配的资源(磁盘空间、数据库资源等)方面可能非常昂贵。jBPM 审计日志服务数据库模式只是一个默认实现;默认审计数据的类型和数量可能不符合您的需求,您的业务应用程序可能需要捕获更精细(或只是不同)的信息级别。

jBPM 审计服务模块(jbpm-audit-6.2.0.jar)为实施者提供了两种通过收集引擎事件来生成审计数据的方法:

  • JPA:同步记录器,绑定到引擎事务,并将审计事件作为运行时引擎事务的一部分进行持久化

  • JMS:异步记录器,可以配置为将消息放置在队列中,要么与活动事务相关(仅在事务提交后),要么直接生成

注意

请参阅第六章,核心架构,以获得对 jBPM 审计和日志服务的全面解释。

仔细评估所需审计服务粒度对引擎性能的影响,并据此规划您的实施;对于生产环境,请考虑以下方面:

  • 使用与引擎数据库不同的数据库来存储您的审计数据;这有助于简化数据库管理任务(例如,没有外键问题)

  • 使用异步事件处理以获得更好的吞吐量

在审计数据真正重要的地方,一些系统通常需要保留流程历史日志相当长的时间(数年);规划一个可靠的数据库策略(备份/恢复)是必须的。以下列表为您提供了一些实用的建议:

  • 无需审计数据:关闭 JPA 审计持久化选项(配置 jBPM 的persistence.xml文件)

  • 默认审计数据:启用 JPA 审计持久化

  • 自定义/更精细的级别但无需额外处理:关闭 JPA 审计选项,并遵循第六章中的自定义审计实现说明,核心架构

  • 定制/更细粒度级别和额外处理需要(BI):关闭 JPA 审计选项,并遵循第六章中的自定义审计实现说明,核心架构;建议写入不同的数据库

请查阅第六章,核心架构,以获取实现细节和示例。

默认历史日志

引擎审计日志/历史信息存储在PROCESSINSTANCELOGNODEINSTANCELOGVARIABLEINSTANCELOG表中。

注意

请查阅 jBPM 6.1 用户指南的第八章,以获取有关 jBPM 数据库模式的详细信息。

对于我们的 BAM 目的而言,一些不太为人所知但非常实用的表是TASKEVENTAUDITTAASKIMPL表。

TASKEVENTAUDITTAASKIMPL表由org.jbpm.services.task.audit.JPATaskLifeCycleEventListener管理,这是一个在运行时创建并附加到TaskService实例的任务生命周期监听器,当时设置了 JPA EntityManager

TASKEVENT表记录任务事件转换:

STARTED, ACTIVATED, COMPLETED, STOPPED, EXITED, FAILED, ADDED, CLAIMED, SKIPPED, SUSPENDED, CREATED,FORWARDED, RELEASED, RESUMED, DELEGATED, NOMINATED

每一行都包含父任务的键(TASKID列);您可以查看放置在pizzadelivery Eclipse 项目中的示例完整表转储文件(taskevent_dump.txt)。在以下段落中,我们将介绍 BAM 仪表板,并通过使用我们的 PizzaDelivery 业务流程审计数据构建一个定制示例。

BAM 和 Dashbuilder – 比萨饼制作者的性能指标

jBPM 6 附带一个使用 Dashbuilder 构建的预配置 Web 应用程序。

小贴士

Dashbuilder 是 Red Hat 开源平台,用于构建业务仪表板和报告;在撰写本书时,最新的仪表板版本是 6.2.0;请参阅以下产品主页:

www.dashbuilder.org.

流程和任务仪表板绝不是生产就绪的 BAM 平台,但它有助于 jBPM 系统利益相关者(以及 jBPM 管理员)几乎无需成本地获得底层审计默认数据的持续预览。

小贴士

请查阅 jBPM 6.2 用户指南的第十六章,了解 jBPM 仪表板的介绍。

为了说明一个实际案例,我们现在将生成一些审计数据,并在我们的 jBPM 仪表板上添加一个新的图表(由新的数据提供者支持);该图表将显示所有流程参与者相对于分配任务的性能指标。

示例项目

该书附带几个示例 Java 项目;从本段开始,我们将使用 Maven 项目,您可以在 Eclipse 中加载和运行这些项目,而无需通过 KIE 工作台部署。示例是 jUnit 基础的测试类,每个类包含单个或多个测试方法。要解决所有必需的 jBPM 库依赖项,您可以将 org.jbpm:jbpm-test:6.2.0.Final 依赖项添加到由您的 Eclipse 项目生成的 pom.xml 文件中。

配置持久化

示例使用 Bitronix 和 H2 数据库进行持久化;数据库连接设置可以在 localJBPM.properties 文件中为所有项目进行配置。此文件位于 第五章 的 test-common 项目中,从 BPMN 构造 示例开始,以及本章的 Pizza 项目中的 examples 文件夹。主要设置如下:

persistence.datasource.user=sa
persistence.datasource.password=
persistence.datasource.url= jdbc:h2:tcp://localhost/~/jbpm-62
persistence.datasource.name=jdbc/localjbpm-ds

请注意,persistence.datasource.name 必须等于项目 persistence.xml 文件中的持久化单元 jta-data-source 元素:

<jta-data-source>jdbc/localjbpm-ds</jta-data-source

生成审计数据 – ProcessBAM 单元测试

我们利用默认的 jBPM JPA 审计监听器,通过我们的 PizzaDelivery 流程生成一些审计数据。将 Maven Pizza 项目导入 Eclipse 并运行 ProcessBAM 单元测试;此类管理五个线程,模拟五个流程角色的操作。它引入了一些任务完成的延迟,以便获得真实的审计数据(时间以毫秒表示;测试运行需要几分钟)。

测试还使路易吉在制作披萨时比马里奥略慢,因此在评估 KPI 时我们可以注意到这种差异。

salvatore is executing task 'deliver the pizza'
salvatore is waiting for 2000 before completing task 'deliver the pizza'
mario is done with work for now.
salvatore is done with work for now.
...all tasks completed.

应创建 10 个 pizzadelivery 流程,并完成所有相关任务:

  • Nino 管理的 10 个订单

  • Maria 执行的 10 个订单分配

  • Mario 或路易吉共享的 10 个披萨烹饪(随机)

  • Maria 执行的 10 个配送分配

  • Salvatore 完成的 10 个披萨配送

让我们看看从审计数据开始配置和创建仪表板图表所需的步骤。

编写业务查询

查询(H2 SQL 语法和函数)计算每个任务从 STARTED 到 COMPLETED 任务事件转换的持续时间(以毫秒为单位)。

SELECT te.id,te.type as taskevent, te.logtime as startdate, te2.type as taskevent, te2.logtime as enddate,
TIMESTAMPDIFF ('MILLISECOND',te.logtime,te2.logtime) as elapsed, te.userid, t.name as taskname
FROM TASK as  t
INNER JOIN TASKEVENT as te on te.taskid=t.id
INNER JOIN TASKEVENT te2 on te2.taskid=te.taskid and te2.logtime in

(select tetmp.logtime from taskevent as tetmp where tetmp.logtime>te.logtime and te.taskid=tetmp.taskid)

where te.userid is not null and te.userid  <>''

从此数据提供者查询开始,我们将定义两个 KPI:

  • 用户在任务上花费的总时间

  • 用户执行的任务总数

添加数据提供者

登录到 KIE 工作台(用户名:admin,密码:admin)并前往 业务仪表板(在 仪表板 菜单中):

  1. 从左侧导航菜单面板中选择 管理 | 数据提供者 链接。

  2. 创建一个新的数据提供者:名称=pizzerianapoli类型=SQL 查询,并在 查询 字段中粘贴前面的 SQL 查询;按下 尝试数据加载 按钮,应出现以下消息:

    SQL provider data load configuration is correct!
    Elapsed time to execute query: 5189 ms
    Number of entries: 50
    

条目数:50”的消息确认了查询结果是正确的:10 个进程,每个进程有 5 个任务,总共 50 个任务。点击保存以确认数据提供者设置。点击编辑数据提供者按钮将打开定义的列的数据提供者列表:

添加数据提供者

我们现在准备定义 KPI,但在那之前,我们需要在仪表板上创建一个新的空白页面,我们将在这个页面上放置图表。

创建新的仪表板页面和 KPI 面板

让我们创建一个将要托管图表的仪表板页面。

  1. 通过点击仪表板顶部工具栏中页面列表旁边的空白页面图标来创建新页面(参见以下截图):创建新的仪表板页面和 KPI 面板

  2. 设置页面设置(查看以下截图),然后点击创建新页面按钮:创建新的仪表板页面和 KPI 面板

  3. 在左侧导航菜单中点击新页面,将页面 URL 插入页面属性中,并保存更改。

  4. 返回工作区,从顶部页面下拉列表中选择新页面;然后,选择在当前页面创建新面板项;将弹出一个包含所有可用面板的列表。创建新的仪表板页面和 KPI 面板

  5. 创建面板项(位于组件列表顶部)拖放到你希望 KPI 显示的突出显示的目标页面面板上;放置的项会提示你选择数据源提供者。选择我们的pizzerianapoli数据提供者会将 KPI 配置面板带到前台。

KPI 的相关设置细节如下:

用户在任务上花费的总时间 KPI 配置:

  • 数据提供者:pizzerianapoli

  • KPI 名称:用户在任务上花费的总时间(毫秒)

  • 柱状图

  • 范围(X 轴):userid

  • 范围(Y 轴):经过时间;编辑范围(标量函数:求和)

  • 渲染器:打开 Flash

  • 图表类型:带边界的箱形图

用户执行的任务数 KPI 配置:

  • 数据提供者:pizzerianapoli

  • KPI 名称:按用户划分的任务

  • 柱状图

  • 范围(X 轴):userid

  • 范围(Y 轴):taskname;编辑范围(标量函数:计数)

  • 渲染器:NVD3创建新的仪表板页面和 KPI 面板

备注

正如我们之前强调的,审计数据的经过时间以毫秒为单位表示;这使得模拟随机延迟成为可能,并避免了测试运行时间过长。

由于延迟的随机性和“制作披萨”任务分配(马里奥和路易吉都可能成为相同任务的拥有者),你可能会有不同的审计数据;马里奥和路易吉的经过时间和总任务数在每个测试执行中都会变化。

然而,单元测试总是使路易吉比马里奥慢,因此整体 KPI 值从未改变。

在两个 KPI 中特别值得注意的是以下内容:

  • 马里奥是制作披萨最快的人

  • 玛丽亚是员工中最忙碌的(20 项任务:10 项订单分配 + 10 项配送分配);她必须肯定提高她的效率,以免拖慢两位披萨制作师(等待她的订单分配)和送披萨的男孩(等待配送订单)

摘要

在本章中,我们探讨了 jBPM 管理功能,分别讨论了项目开发和 Kie 模块部署的新 Git 和 Maven 集成。你应该对 jBPM 模块管理有更好的理解,并且应该能够利用现成的 Red Hat 仪表板工具从 jBPM 审计数据开始创建和配置 BAM 图表。下一章将深入探讨 BPMN 结构,并提供实际的过程定义示例。

第五章。BPMN 构造

为了分类 BPMN 软件工具提供的支持级别,BPMN 标准定义了“一致性类别”如下:

  • 流程建模一致性:此类包括 BPMN 核心元素、流程、协作和对话图。它定义了包含有限视觉元素(描述性)、扩展建模元素(分析性)以及建模执行流程所需元素(通用可执行)的子类。

  • 流程执行一致性:它要求软件工具支持 BPMN 的操作语义。

  • 编排建模一致性:编排建模一致性类别包括 BPMN 核心元素和协作与编排图。

注意

jBPM 支持大部分的通用可执行类别,并增加了额外的扩展。请查阅 jBPM 6.2 用户指南的第六章,核心架构,以了解该主题的见解。

jBPM 在 jBPM 5 版本中引入了 BPMN 2.0 规范的实现,包括图形符号(元素视觉表示)和 XML 序列化,简化了开发者和业务团队之间交换流程定义的任务(就基于 Eclipse 的 BPMN 编辑器和基于 Web 的流程设计器互操作性而言)。

其他 jBPM BPMN 显著特性如下:

  • 遵循 BPMN 流程执行语义(“通用可执行”子类规范)

  • BPMN DI(代表图互操作性)规范用于存储图信息

  • BPMN 输入/输出映射规范

在第一章,业务流程建模 – 连接业务与技术中,我们已经对主要的 BPMN 概念、构造和建模模式进行了概述。我们选择本章的主题,不是为了提供 BPMN 建模或参考指南,而是为了提供一个实际的、以示例驱动的解释,说明 jBPM 支持的所有 BPMN 构造,同时不隐藏其背后的技术细节。

在本章中,我们将讨论以下内容:

  • BPMN 构造背后的概念

  • 如何在业务流程中使用它(示例)

  • 使用 BPMN 构造的最佳实践

参数、变量和数据

大多数情况下,业务流程是数据驱动的流程:任务处理变量,规则处理事实;在没有处理来自外部系统、用户输入和其他来源的变量、参数、对象和状态的情况下,不会要求你绘制 BPMN 图形。大多数 jBPM 构造在无数据的情况下都是无用的。让我们澄清基本概念:

  • 参数:这些是通过 API 从用户那里传入的数据输入。用户可以在流程创建期间、在人工任务完成时或向 Web 服务调用中的服务任务传递参数。

  • 变量:变量是存在于单个流程实例作用域内的对象。变量可以直接在流程实例结构内部创建(例如,脚本活动和数据对象)或从另一个作用域中的其他变量(数据输入/输出映射)映射而来,例如,从主流程到子流程,从流程到人工任务等。

  • 全局变量:在单个 Kie 工作会话中跨不同流程实例共享的静态变量。

  • 事实:可以添加到 Kie 会话中的数据,然后更新或删除(撤回)。从技术上讲,这些信息通过名为入口点的通道插入到会话中,并根据 Drools 业务规则进行评估以激活。Drools 议程管理规则激活和触发机制。

注意

请参阅 Drools 参考文档以获取有关事实、规则、入口点、议程以及 Drools 规则引擎的一般信息的更多详细信息:docs.jboss.org/drools/release/6.2.0.Final/drools-docs/html。Drools 和 jBPM 是互补的项目,它们可以很好地集成在一起。

变量和全局变量通过在运行时提供给 jBPM 结构的上下文类型隐式引用来访问:

  • ProcessContextkcontext):这为您提供了访问变量的权限

  • KieRuntimekcontext.getKieRuntime()):这为您提供了访问全局和事实的权限

除了实现java.io.Serialization接口外,对参数、变量和全局类类型没有实现约束。实际上,记住 jBPM 使用的是标准的内存序列化机制(readObject/writeObject)。当我们启用持久化时,它还具备一个额外的自定义对象序列化机制,用于会话和流程实例的存储和检索(参见第七章中的序列化定制和扩展 jBPM)。此外,当存在用于审计和记录的持久化流程变量(VARIABLEINSTANCELOG表)时,jBPM 通过调用流程变量的toString()方法来存储这些值。

提示

jBPM 在其任何架构表中都不提供开箱即用的流程变量持久化。我们需要实现我们的专用变量序列化策略(我们将在第七章中介绍变量持久化,定制和扩展 jBPM)。

顺序流

顺序流程是流程中两个元素之间的连接器。它表示执行流程。顺序流程可以可选地定义一个条件(条件顺序流程)。引擎始终评估任务节点的输出顺序流程:如果条件评估为真,则引擎选择并遵循该顺序流程;没有定义条件的顺序流程始终由引擎跟随。一个菱形连接器(见附录 B

示例测试类:

com.packt.masterjbpm6.SequenceTest

示例流程:

sequenceflows.bpmn

描述:该测试创建了一个具有Order变量和不同成本值的流程实例。由于将jbpm.enable.multi.con系统属性设置为TRUE,该流程允许执行多个(此处有两个)从单个脚本活动发散的条件顺序流程。如果订单成本超过 10,则选择第一条顺序流程,而当订单成本≤10 时,则选择第二条顺序流程。

网关

网关是允许您在流程中创建分支的元素。从概念上讲,这些分支可以是发散或汇聚的。您可以建模不同类型的业务流程顺序流程的行为:条件分支(包含和排除)、分支、合并和连接。

让我们先回顾一下即将在下一节中讨论的关键网关概念和实际示例:

  • 分支(分裂)表示流程分为两个或更多应逻辑并行(并发)执行的路径:由于实现原因,jBPM 永远不会并发(在线程级别)执行并行流程,而是始终顺序执行,一次一步

  • 并联(或同步)指的是将两个或更多并行路径合并为一条路径

  • 分支(或决策)是控制流可以采取一个或多个替代路径的点

  • 合并指的是两个或更多替代顺序流程路径合并为单个顺序流程路径的过程点

因此,网关方向属性定义如下:

  • 未指定:可能具有多个输入和输出连接

  • 混合:多个输入和输出连接

  • 汇聚:多个输入连接和仅一个输出连接

  • 分支:只有一个入连接和多个出序列流

未指定和混合方向尚未实现

让我们看看这些 BPM 概念如何转换为 jBPM 建模元素。

并行(AND)网关

此网关允许我们进入多个执行路径或连接多个执行路径。当用于分支序列流(分支或 AND 分裂)时,所有出分支同时激活。当连接并行分支(汇聚或 AND 连接)时,它等待所有入分支完成后再移动到出序列流。当需要在任何特定顺序同时执行许多活动时,必须使用此网关。

示例测试类:

com.packt.masterjbpm6.gateway.GatewayParallelTest

示例流程:

gateway_parallel.bpmn

描述:计划路线脚本任务计算订单配送路线,而准备原料人工任务向订单物料清单中添加一些马苏里拉奶酪。关闭完成脚本任务在所有出流完成后显示结果。

并行(AND)网关

条件分支

这些网关引入了条件表达式。与每个出/入序列流链接的条件表达式在过程执行期间使用过程数据(基于数据的网关)进行评估。可选地,可以将网关的一个出路径标记为默认流(其条件被忽略):只有当其他路径流都无法选择时,才会采取此路径。默认(序列)流在以下图像中以斜杠标记进行视觉标记:

条件分支

在排他和包含网关元素中支持“默认流”属性。

Drools

我们在第一部分简要介绍了 Drools 事实和规则。基于 Drools 表达式的条件分支与事实一起工作,但不与过程变量一起工作。如果我们想在网关结构中利用 Drools 表达式功能,我们必须将过程变量作为 Drools 事实插入,例如,给定过程变量order

Order order = new Order();
order.setNote("urgent");
order.setCost(110);

从过程定义内部(通过脚本任务、退出脚本等),我们插入以下事实:

kcontext.getKnowledgeRuntime().insert(order);

或者,我们可以通过以下 API 来实现:

  ksession.insert(order);
  ksession.fireAllRules();

排他(XOR)网关

它用于在过程中建模一个决策。不能采取多条路径;路径是互斥的,因此得名。如果有多个序列流具有评估为真的条件,则选择在 XML 中定义的第一个路径以继续过程。在排他网关中,所有出序列流都应该定义条件。默认序列流是此规则的例外。

示例测试类:

com.packt.masterjbpm6.gateway.GatewayExclusiveTest

示例流程:

gateway_exclusive.bpmn

描述:对于成功的披萨配送,采取不同的路径;当其他条件不满足时,选择默认路径。

排他(XOR)网关

包含(OR)网关

一个包容网关是业务流程的分支点。与排他网关不同,包容网关可以触发多个输出流并在并行中执行它们(例如并行网关)。因此,在分支行为中,网关将始终评估所有输出序列流条件,无论它是否已经有一个满足的输出流(与排他网关不同)。在合并行为的情况下,网关将等待直到所有进入的激活序列流都到达它(合并)。当我们需要根据某些条件分叉执行并在之后重新连接它们时,我们通常可以在一对拆分/合并网关中使用此结构(参见以下示例)。

示例测试类:

com.packt.masterjbpm6.gateway.GatewayInclusiveTest

示例流程:

gateway_inclusive.bpmn

包容(OR)网关

描述:为了评估订单交付状态,采取了多条不同的路径;testIssues 测试被设置成使流程同时采取未按时交付deliveryDate > dueDate)和重试次数 > 1的路径。当其他条件不满足时,选择默认路径(参见testNoIssues测试)。

基于事件的网关

基于事件的网关与排他网关类似,但网关触发是基于事件发生而不是条件评估。当我们的流程到达基于事件的网关时,我们将必须等待直到发生某些事情。一个特定的事件,通常是收到消息,决定了将要采取的路径。基本上,决策是由另一个不可见于流程的数据的演员做出的。此网关始终是一个分支网关,并且必须至少有一个事件附加。

示例测试类:

com.packt.masterjbpm6.gateway.GatewayEventAndTaskTest

示例流程:

gateway_event_and_task.bpmn

基于事件的网关

描述:事件网关附加了一个计时器;当计时器到期时,将执行发送警报脚本,使流程终止。

实例化网关

实例化网关是一种特殊的事件网关,它一收到附加的事件就触发流程实例化。"实例化"选项(截至 jBPM 6.2,此选项仅在 jBPM Eclipse 插件中可用)将网关配置为没有输入连接的分支网关:这为您提供了通过事件实例化流程的方法,例如计时器到期或捕获信号事件(参见以下章节中的计时器和信号)。jBPM 不支持没有输入连接的纯实例化网关:您始终必须将其链接到"无"启动事件(参见以下图示)或流程编译将失败(报错"缺少输入连接")。

示例测试类:

com.packt.masterjbpm6.gateway.GatewayEventTest

示例流程:

gateway_event.bpmn

描述:根据从外部(API 调用)发送的事件,不同的路径会被采取(testCustomerPhoneCallEventtestDeliveredEvent方法);如果在 15 秒内没有捕获到事件,计时器会触发(testTimerExpired方法)。请注意,捕获事件都会将信号数据(一个随机生成的orderid字符串)传递给过程参数orderid,该参数随后会在脚本任务中打印出来。

实例化网关

复杂网关

此网关可用于建模复杂的同步行为。构造选项在设计师级别可用,但 jBPM 没有对此结构进行实现。

事件

事件是用于在过程生命周期中建模发生的事情的元素。BPMN 2.0 定义了两个主要的事件类别:捕获抛出事件。

  • 捕获:此事件表示过程执行中的暂停点:一旦流程达到捕获事件节点,它将停止在等待状态,等待特定触发器发生。

  • 抛出:此事件表示生成事件的动作。当过程执行达到事件构造时,会执行一个动作并触发一个触发器。对于此抛出事件,根据事件类型,可能存在匹配的捕获事件,也可能不存在,即发送信号(抛出)/捕获信号或发送错误(抛出)/捕获错误。另一方面,补偿抛出事件没有对应的捕获事件,而计时器事件始终是捕获事件。

事件还可以根据其他标准进行分类:

  • 事件可以出现在过程的开始(开始事件)、过程中(中间事件)或过程的结束(结束事件)。

  • 事件可以是通用的,或者是以下预定义类型之一:基于时间的、基于消息的、基于信号的、基于规则的、基于异常的等等。

  • 事件可以位于序列流内,也可以附加在活动的边界上(边界事件)。

  • 事件可以退出当前过程执行,也可以不退出。

注意

在我们开始之前的一个注意事项:

为了便于阅读,我们将通过按事件类型(开始、边界、结束)分组事件,然后展示每种事件类型(信号、消息、计时器等)支持的变体(捕获/抛出和开始/中间/边界/结束)。

对于更多信息以及完整的 jBPM 结构参考(按与您在 Eclipse BPMN 建模工具调色板和 KIE 控制台调色板中找到的相同方式排序),请参阅附录 B,jBPM BPMN 结构参考

开始事件

开始事件定义了过程开始的位置(以及方式);开始事件是仅捕获事件。当特定的开始事件触发器被触发(计时器、消息、信号等)时,过程开始。我们现在将看到无开始事件;其他开始事件类型将在各自的章节中讨论。

支持的开始事件有:无、消息、定时器、升级、条件、错误、补偿、信号

无开始事件

最简单的开始事件形式是无开始事件。从技术上讲,这意味着启动流程实例的触发器未指定;换句话说,引擎不知道何时启动流程实例。启动流程的唯一方法是通过对 Kie 会话引用调用startProcess方法。

ProcessInstancestartProcess(String processId, Map<String, Object> parameters);

结束事件

结束事件旨在表达流程或子流程的结束,并且它们总是抛出事件。当流程执行到达结束事件节点时,将抛出相关的事件类型。一个流程定义可以定义一个或多个结束事件。在本节中,我们将看到“无”和“终止”结束事件;其他结束事件类型将在各自的章节中讨论。

支持的结束事件有:无、消息、升级、错误、取消、补偿、信号、终止

(无)结束事件

无结束事件不会抛出任何事件,引擎只是结束当前流程实例的序列流执行。如果没有更多的活动序列流或没有其他要执行的操作(活动),则流程实例完成。

终止结束事件

终止结束事件将流程实例带到完成状态;所有挂起的任务、活动序列流和子流程都将被中止。

边界事件

边界事件是事件(总是捕获),它们以图形方式附加到活动(包括子流程)的边界(参见以下图)。事件注册为某种类型的触发器(参见以下支持的边界事件),并且仅在附加活动的执行范围内做出反应,根据事件类型略有不同。如果事件被触发,它可以选择取消附加到其上的活动(通过其cancelActivity属性),并执行事件的输出序列流。边界事件在附加活动启动时被激活;换句话说,它们绑定到活动实例的生命周期。当引擎流程执行路径离开活动时,所有附加的边界事件都将被停用,并且它们的触发将被取消。

支持的边界事件有:条件、错误、升级、消息、信号、定时器

请参阅边界消息事件部分以获取一个工作示例。

边界事件

信号事件

信号是一种通用、简单的通信形式,例如消息(见下文)。我们可以使用信号来同步和交换信息。捕获信号可能没有对应的抛出信号构造。它也可以从外部源(API)以编程方式发送。与其它事件(错误事件)不同,如果捕获到信号,它不会被消耗。如果有两个活动中间捕获事件在同一信号事件名称上触发,这两个事件都会被触发,即使它们属于不同的流程实例和定义。如果发送了信号,但没有为该事件注册捕获信号,则该事件会丢失。

范围

信号可以在同一流程的不同部分或广播流程(跨所有流程实例的范围)之间具有可见性,或者针对特定的流程实例。你可以在流程实例中抛出一个信号事件,其他具有不同流程定义的流程实例可以对此事件做出反应。请记住,这种行为(更广泛的或更窄的信号范围)可能会受到创建你的 Kie 会话时选择的 运行时策略 的影响(该主题在 第六章,核心架构 中讨论)。

信号 ID 和信号名称提示

你可能会在创建/修改 BPMN 流程中的流程信号时注意到与信号相关的一些问题,这些流程是在 KIE jBPM 控制台编辑器和 Eclipse BPMN 模型器之间共享的。生成的 BPMN 有所不同,这可能会导致错误和意外的行为。

当从 Eclipse BPMN 编辑器创建流程定义时,信号被分配一个内部 ID,其形式为:Signal_{number}。因此,实际使用的信号 ID 与你在 Signal 属性编辑器中看到的相同信号 ID,而不是流程定义面板(信号列表表)中用户指定的信号名称。在针对 org.kie.api.runtime.KieSession.sendSignal 方法编码时,请记住这个额外的信号名称引用。

<bpmn2:signal id="Signal_1" name="customerPhoneCall"/>
<bpmn2:signalEventDefinition id="SignalEventDefinition_1" signalRef="Signal_1"/>

因此,使用 Eclipse 生成的流程,必须使用 Signal_1 ID 与 API 一起使用。

<bpmn2:signal id="customerPhoneCall" name="customerPhoneCall"/>
<bpmn2:signalEventDefinition id="_05nSUW_YEeSWR_CUOywjGQ" signalRef="customerPhoneCall"/>

使用从 jBPM Web 控制台编辑器生成的流程,信号 ID 等于名称属性;使用 API 时必须使用 customerPhoneCall

信号数据映射

信号可以携带可选的对象数据;对于每个触发的捕获信号,你可以获取这个信号数据并将其映射到流程变量。当使用 jBPM Web 设计师操作时,为了成功将信号数据映射到流程变量,你必须配置 DataOutput 信号并将其命名为 event,正如你在以下屏幕截图中所见。图片显示了 start_signal.bpmn 流程信号事件的 数据映射(见 Start Signal event 部分的详细事件数据映射示例)。

Signal data mapping

这是一个非常灵活的机制。通过在信号中传递数据,您可以轻松地更新过程变量、传递额外信息或更改过程流程。

开始信号事件

使用命名的开始信号,我们可以通过编程方式启动流程实例。该信号可以通过使用中间信号抛出事件或通过 API(sendSignal方法)在现有流程实例内部触发。在这两种情况下,所有具有相同名称的信号开始事件的流程定义都将启动。您可以在单个流程定义中拥有多个开始信号事件。

示例测试类:

com.packt.masterjbpm6.event.StartTest (method testSignalStart)

示例流程:

start_signal.bpmn

开始信号事件

描述:发送不同的开始信号事件以创建不同的流程实例。信号数据映射到流程变量(请参见前一小节中对事件数据映射的解释)。

中间信号事件

中间捕获信号事件捕获从抛出中间信号或通过 API 调用(KieSessionProcessInstance.sendSignal)发送的信号,并继续流程实例流程。捕获信号没有传入连接。

边界信号事件

请参阅边界事件部分。

结束信号事件

这种信号事件在流程完成时发送。它可以是一种方便的方式来跟踪整个系统中的流程实例完成情况。

消息事件

消息事件引用一个名称,并且可以选择性地包含有效负载。与信号不同,消息事件始终针对单个捕获消息。为了使消息流正常工作,捕获和抛出消息的名称必须完全相同。让我们指出消息和信号之间的一些区别:

  • 在 BPMN 图中,消息流程通过连接发送者和接收者来绘制,而信号永远不会在图中直接连接。抛出和捕获信号仅通过它们的名称隐式连接。

  • 消息应在同一流程实例中抛出/捕获;信号没有这种限制。消息仅在流程实例范围内工作,是点对点链接。一个信号可以从一个流程实例传播到多个流程实例(广播范围)。

消息数据映射

请参阅信号数据映射部分。

开始消息事件

开始消息事件用于在捕获消息的直接后果中启动流程实例;一个流程可以拥有多个消息开始事件。这允许我们通过简单地更改消息事件名称来选择流程创建方法(请参见以下图像)。确保消息事件名称在所有加载的流程定义中是唯一的,以避免不希望的过程创建。

小贴士

当从 API(sendSignal)发送消息时,我们必须在消息名称前加上Message-字符串前缀。

消息开始事件仅支持顶层流程,而不支持嵌入式子流程。

开始消息事件

中间消息事件

如果流程正在等待消息,它将暂停直到消息到达,或者改变异常处理的流程。要使用抛出消息,必须有一个捕获消息事件来捕获消息。它可以是消息中间事件或消息开始事件。

边界消息事件

以下示例展示了通过将两个边界事件(计时器和消息)附加到人工任务来执行任务取消和消息数据传递。计时器将cancel activity属性设置为FALSE,而消息将其设置为TRUE。边界消息事件将事件数据映射到流程变量,以便记录由测试类发送的抛出(外部)消息传递的取消原因。

示例测试类:

com.packt.masterjbpm6.event.BoundaryTest (method testBoundaryWithCancel)

示例流程:

boundary.bpmn

描述:创建了一个包含人工任务的流程。计时器事件的职责是每 15 秒循环并过期,调用脚本任务“超时警告”(其计时器表达式为15s###15s,且未标记为“取消活动”;因此,当计时器触发时,任务不会被取消)。当用户继续进行测试(测试类要求用户按下一个键以继续)时,会发送一个消息(sendSignal),触发流程消息边界事件,并取消活动(因为边界消息事件启用了“取消活动”标志)。请注意,消息是由我们的测试类发送的,其中包含一些数据,作为任务取消的原因("cancelled by ADMIN"):

sendSignal("Message-messageCancelService", "cancelled by ADMIN");

边界消息(id=messageCancelService)捕获发送的消息,并将绑定到流程变量reason的消息事件数据通过取消日志脚本任务打印到标准输出。

边界消息事件

结束消息事件

在流程结束时向特定流程发送消息。

jBPM 抛出消息实现

jBPM 抛出消息的默认实现只是一个占位符。您必须提供自己的 WorkItemHandler 定义,并将其以Send Task的名称注册到 jBPM 运行时,提供一个到工作 Kie 会话的钩子(以下代码片段中通过ksession标识):

SendMessageTaskHandler messagehandler = new SendMessageTaskHandler();
messagehandler.setKnowledgeRuntime(ksession);
ksession.getWorkItemManager().registerWorkItemHandler("Send Task",messagehandler);

备注

在本章中,您将找到对“workItem”和“workItem handler and manager”的多次引用。这些都是 jBPM 组件的一部分,允许您定义一个自定义 Java 类并将其与引擎运行时中特定的流程活动类型绑定。每次引擎激活此活动类型时,您的处理程序将被调用并传递控制权。请参阅第七章,自定义和扩展 jBPM以获取详细说明和示例。

从自定义 workItemHandler,您然后可以发送信号:

public void executeWorkItem(WorkItemworkItem, WorkItemManager manager) {
ksession.signalEvent("Message-startmessage", "processdata");

示例测试类:

com.packt.masterjbpm6.event.StartTest (method testMessageStartFromMessageThrow)

示例流程:

start_message_catch.bpmn, start_message_throw.bpmn

描述:创建的流程通过自定义 WorkItemHandler 发送消息,启动start_message_catch流程的新实例(通过开始消息事件)。

计时器事件

计时器事件是在满足计时器构造表达式时触发的事件;计时器属性如下:

  • 时间持续时间:单个触发延迟值(例如:10 m,25 s)。

  • 计时器周期:要评估的时间表达式。它可以是字符串(基于间隔的 20 s 或 5 m###35 s,其中第一个值是初始延迟,第二个值是重复触发的延迟),字符串cron表达式,或流程变量。在 JBPM 6.x 的情况下,它还可以是 ISO-8601 格式的日期。

  • 计时器周期语言:可以是默认间隔(空值和时间持续时间设置)或cron

开始计时器事件

开始计时器事件用于在给定时间创建流程实例。它可以用于应该只启动一次的流程和应该按特定时间间隔启动的流程。请注意以下要点:

  • 子流程不能有开始计时器事件。

  • 一旦流程部署,就会注册一个开始计时器事件。无需调用startProcessInstance API。

  • 当部署带有开始计时器事件的流程的新版本时,对应旧计时器的作业将被移除。

中间计时器事件

此事件仅是一个捕获事件。计时器值触发输出序列流的执行。您可以使用计时器来插入通用延迟或超时序列流执行;例如,您可以为管理人工任务完成截止日期添加计时器(请参阅基于事件的网关部分示例,其中计时器以这种方式操作)。

边界计时器事件

请参阅边界消息事件部分以获取示例。

错误事件

错误事件用于模拟业务异常。它们由在活动执行过程中可能生成的异常触发。中间抛出/捕获错误事件不适用。

边界错误事件

此边界错误事件必须附加到活动上。当错误事件触发时,活动总是被取消,并采取错误事件的输出序列流。

示例测试类:

com.packt.masterjbpm6.event.ErrorTest (method testBoundaryErrors)

示例流程:

errorboundary.bpmn

描述:两个不同的边界错误事件附加到不同errorCode属性(FileNotFoundExceptionRuntimeException)上注册的同用户任务;错误处理器记录异常消息。根据传递给过程参数(triggerexceptionflag)的值,用户任务在完成时抛出不同的异常(onExit脚本),这触发了适当的边界错误事件。

边界错误事件

流程以一个变量启动,其值影响要抛出的异常类型:

Map<String, Object> params = new HashMap<String, Object>();
// "1" for a runtime exception; "2" for a FileNotFoundException
String trigger = "1";
params.put("triggerexceptionflag", trigger);
ProcessInstance processInstance = ksession.startProcess("errorboundary", params);

用户任务的onExit脚本评估流程变量并相应地抛出异常:

String trigger=(String)context.getVariable ("triggerexceptionflag");
if (trigger.equals ("1"))
{
throw new RuntimeException("a runtime exception");
}
else
{
throw new FileNotFoundException("a filenotfoundexception exception");
}

引擎根据抛出的异常触发适当的边界错误事件;实际上,事件必须配置为将errorCode属性设置为异常类名:java.lang.RuntimeException(参见以下截图)。

边界错误事件

注意,边界错误可以将异常绑定到流程变量。在示例中,该变量(exceptionvar)通过脚本任务记录到控制台:

Throwable exc=(Throwable )context.getVariable ("exceptionvar");
System.out.println("log error message:"+exc.getMessage());

错误开始事件

错误开始事件只能用于触发事件子流程,不能用于启动流程实例。这是您可以考虑在错误异常激活替代子流程时使用的一个功能。

错误结束事件

当流程执行达到错误结束事件时,当前执行路径结束并抛出一个错误事件。此错误由匹配的中间边界错误事件或子流程开始错误事件捕获。如果没有找到错误事件,则抛出异常。

以下示例使用错误结束事件通过其错误开始事件触发一个子流程。

示例测试类:

com.packt.masterjbpm6.event.ErrorEndTest (method testSubprocessStartError)

示例流程:

errorsubprocess.bpmn

描述:主要流程特征包括一个人类任务和一个错误结束事件,该事件通过错误开始事件触发一个嵌入的子流程脚本任务。

错误结束事件

补偿

复杂的业务流程可能涉及多个异构的参与者和系统,例如现代事务系统、遗留系统(非事务性)和 Web 服务。为了保持业务一致性,当出现故障且没有事务性协议可用时,这些系统可能需要您通过调用某些专用 API 或其他方式执行程序性纠正操作。补偿是后处理操作,试图补救(而不是正确撤销或回滚)由操作产生的效果。

我们想强调的是,jBPM 的补偿不是事务性功能或try/catch错误机制。补偿是 BPM 业务功能,它将活动建模为已完成的活动的补偿对应物。

这里是常见的步骤,这些步骤发生在补偿事件期间(参见以下流程示例图以获取序列的视觉参考)。

  • 一个活动(A1),其边界连接到补偿事件(E1)已完成

  • 在流程中某处抛出一个补偿事件(E2)

  • 补偿事件(E1)捕获 E2

  • jBPM 激活与 E1 相连的补偿处理程序(A2)。

引擎不知道补偿活动将做什么,因为补偿业务逻辑的定义取决于开发者。

中间补偿事件

抛出补偿事件(E2)和边界补偿事件(E1)通过相同的事件名称隐式连接(我们已经在信号和消息中看到过这一点)。我们之前对边界事件的解释仍然适用:当补偿事件(E2)被触发时,边界补偿事件(E1)会通过调用标记有典型补偿FastBackward样式的关联补偿活动(A2)来做出反应。

边界补偿事件

补偿边界事件(E1)必须仅通过直接关联线引用一个补偿处理程序(A2)。补偿边界事件仅在活动(A1)完成时被激活(与默认边界事件行为不同,事件激活取决于活动启动状态)。补偿捕获事件(E1)在父流程实例完成或补偿事件本身被触发后将被移除。如果补偿边界事件附加到多实例子流程,将为每个实例创建一个补偿事件监听器。jBPM 似乎不支持此最后功能。

补偿活动

此活动(也称为补偿处理程序)直接连接到触发边界补偿事件,并且不能有出序列流。

示例测试类:

com.packt.masterjbpm6.event.CompensationTest (method testCompensationEvent)

示例流程:

compensateorder.bpmn

描述:我们使用这个示例过程来解释典型的补偿“工作流程”,因此你应该已经熟悉它。让我们再补充一点,当人工任务(H1)完成并且cancelOrder变量评估为“y”时,会抛出补偿事件。这激活了排他网关序列流,从而触发事件(E2)。这激活了边界补偿事件(E1),进而调用取消订单脚本任务(A2)。取消订单任务充当“补偿”活动。

补偿活动

使用信号触发补偿

jBPM 通过使用信号(通用(隐式)和特定补偿处理)提供额外的触发流程实例内补偿的方法。隐式补偿触发流程实例的所有补偿处理程序:

ksession.signalEvent("Compensation",
  CompensationScope.IMPLICIT_COMPENSATION_PREFIX
    + "compensateorder", pi.getId());

你必须使用补偿信号类型,并将信号数据传递为一个字符串,该字符串是通过连接CompensationScope类常量和流程定义 ID 生成的:

"implicit:compensateorder"

特定补偿在流程实例内触发特定的补偿处理程序。你必须传递附加到边界补偿事件的活动节点 ID,以及流程实例 ID:

ksession.signalEvent("Compensation", "_2", pi.getId());

我们的示例流程脚本任务 XML 元素如下:

<bpmn2:scriptTask id="_2" name="prepare order" scriptFormat="http://www.java.com/java">

小贴士

在流程定义级别不需要定义新的信号事件。

对于工作示例,请参考以下内容:

示例测试类:

com.packt.masterjbpm6.event.CompensationTest (methods testGlobalCompensationWithSignal and testSpecificCompensationWithSignal respectively).

结束补偿事件

结束补偿事件的工作方式与中间事件相同(请参阅示例流程图)。抛出补偿结束事件(E1),并触发补偿处理程序(A1)。此类事件在需要执行流程结束时的维护或补救业务逻辑时很有用,但仅当您的有界活动(S1)处于完成状态时。请注意,实际上,正如我们之前强调的,补偿处理程序仅在子进程(S1)已经处于完成状态时启动。

示例测试类:

com.packt.masterjbpm6.event.CompensationTest (method testSubprocessCompensationEndEvent)

示例流程:

compensateendsubprocess.bpmn

描述:该流程有一个带有附加边界补偿事件(E2)的子进程S1)。子进程触发抛出补偿结束事件(E1)。补偿边界捕获事件(E2)调用补偿处理程序(A1),该处理程序将流程变量回滚到初始值。

结束补偿事件

多实例补偿

附加到多实例子进程的补偿捕获事件尚未实现。有关多实例活动的详细信息,请参阅子进程部分。

升级

升级,根据机构、组织或公司的普遍政策,指的是工作人员与其职责之间现有的关系。升级事件的产生表明存在需要将业务流程流转转向不同用户组的条件。例如,如果收到超过一定价格阈值的订单,则必须由高级别用户(例如,经理)执行审批任务;否则,也可以由职员用户审批。

在 jBPM 6.2.0 的情况下,升级事件似乎部分实现,目前尚不清楚在此阶段支持 BPMN 规范的哪一部分。您可以通过截止日期和通知(请参阅用户任务部分)部分克服升级事件的缺乏。

条件事件

条件事件是 jBPM 的功能扩展。它们由对用户提供的 Drools 规则和事实属性的评估触发。支持条件开始和边界事件。

示例测试类:

com.packt.masterjbpm6.event.ConditionalTest (method testSubprocessStartError)

示例流程:

conditional.bpmn

描述:当事实订单笔记属性匹配“紧急”时,主过程开始;如果订单成本 > 100,则取消以下脚本任务ordercost

条件事件

活动

活动是在业务过程中执行的工作单元;它可以是最小单元或非最小单元(复合活动、调用活动或子进程)。活动可以是任务、调用活动或子进程类型。

任务

任务是可以包含在流程中的最小原子活动单元。通常,任务的执行者可以是使用基于 UI 的应用程序、参与的外部服务或一组通用业务语句的最终用户(称为人类)。任务有其局部作用域,可以从其容器接受输入参数并返回输出参数。

用户任务

用户任务用于模拟需要由人类行动者完成的工作。当流程执行到达用户任务节点时,将在为该任务定义的行动者(或组)的工作列表中创建一个新的任务实例(行动者和组属性)。人类任务可以根据对任务本身采取的操作和定义的人类角色过渡到几个不同的状态,并涉及人类利益相关者。

人类角色

人类角色定义了个人或一组行动者可以对任务执行的操作。让我们回顾为人类任务活动定义的角色:

  • 任务发起者:创建任务实例的人。根据任务是如何创建的,任务发起者可能没有定义。

  • 实际所有者:拥有并执行任务的人。任务始终有一个实际所有者。

  • 潜在所有者:被分配任务以便他们可以声称并完成的人。潜在所有者可以通过声称任务而成为实际所有者。

  • 排除所有者:行动者可能无法过渡为实际或潜在所有者,他们可能无法保留或开始任务。

  • 业务管理员:由于他们是每个任务的潜在所有者,业务管理员可以执行与任务用户相同的操作。jBPM 提供了一个默认的业务管理员用户(管理员)和组(管理员)。

状态转换

任务保持在已创建状态,直到它被激活。当任务只有一个潜在所有者时,它将过渡到已保留状态(分配给单个实际行动者);否则,它将过渡到就绪状态;此状态表示任务可以被其潜在所有者之一所声称。一旦被声称,任务将过渡到已保留状态,将潜在所有者提升为实际所有者行动者。此时,该行动者可以开始处于就绪已保留状态的任何任务,并使其过渡到进行中状态。进行中状态表示任务正在被处理。如果行动者完成了工作,任务将过渡到完成状态。如果工作的完成出现错误(异常),任务将被置于失败状态。或者,用户可以释放任务,将其返回到就绪状态。不允许从完成状态和失败状态进行转换。

注意

关于任务状态转换的详细信息,请参阅 Oasis 在docs.oasis-open.org发布的 Web 服务 – 人类任务(WS-HumanTask)规范。

状态转换

状态转换

截止日期和升级

jBPM 中任务截止日期的概念与任务开始-完成时间间隔持续时间相关联;截止日期与任务升级相关联:任务升级可能存在于任务重分配或任务通知操作中。任务截止日期是在任务到期日期上计算的:当任务开始时重置,当任务在允许的时间边界内完成时到期。截止日期在DEADLINE表中物理存储,而通知存储在NOTIFICATION表集中。

注意

重分配和通知属性编辑器仅在 KIE Web 流程编辑器中可用。

任务重分配

任务重分配是 jBPM 机制,允许您通过设置特定规则来更改任务所有权,这些规则基于任务状态转换和截止时间表达式,例如:“如果 Luigi(一个命名的任务 actor)在 60 秒内没有开始任务,则将任务实例重新分配给 Mario。”指定的用户被新用户替换为任务的可能所有者。结果的重分配规则语法如下:

[users:mario|groups:]@[60s]@not-started

您可以在单个任务实例上定义多个重分配规则。

任务事件类型条件可以是not-startednot-completed

BPMN XML 任务参数是NotStartedReassignNotCompletedReassign。重分配信息由引擎持久化到REASSIGNMENTREASSIGNMENT_POTENTIALOWNERS表中。

示例测试类:

com.packt.masterjbpm6.activity.TaskTest (testReassign method)

示例流程:

reassign.bpmn

描述:主流程启动,任务分配给 Luigi。重分配规则指出“如果 Luigi(命名任务 actor)在 60 秒内没有开始他的任务,则任务应分配给 Mario。”

通知

通知是在任务截止日期到期时提醒某人(actor,group)的操作。默认的 jBPM 通知是基于电子邮件的,默认电子邮件配置是从userinfo.propertiesemail.properties文件中读取的。userinfo.properties文件以下列形式列出用户/组信息:

entityId=email:locale:displayname:[member,member]

例如,对于一个类型为 actor 的实体,我们有:

nino=nino@domain.com:en-UK:nino

成员数据是可选的,用于列出属于一个组织实体的组成员。

注意

请参阅官方 jBPM 6.2 文档以获取配置细节。

BPMN XML 任务参数是NotStartedNotifyNotCompletedNotify

以下是一个NotStartedNotify参数值的示例:

from:mario|tousers:simo|togroups:|replyTo:|subject:warning|body:the task has not been started in 10s !@10s@not-started

委派

委托是设置任务潜在所有者的过程。实际所有者、潜在所有者或业务管理员可以将任务委派给另一个用户,将该用户添加到潜在所有者列表中(如果尚未添加)并使其成为任务所有者。任务可以在处于活跃状态(就绪、保留或进行中)时被委派,并过渡到保留状态,其skippable属性可以标记为true(目标演员/所有者可以跳过任务)。委派后,任务的状态和参数将不会改变。

转发

任务转发是潜在所有者对一个活跃任务执行的过程,他将自己替换在潜在所有者列表中,将任务传递给另一个人。潜在所有者只能在就绪状态下转发任务。如果任务处于保留或进行中状态,任务将再次过渡到就绪状态。

暂停/恢复

任务可以在其任何活跃状态(就绪、保留或进行中)中被暂停,过渡到暂停状态。暂停状态有子状态来指示任务的原始状态。当恢复时,任务将返回到暂停前的原始状态。

跳过

在处理人类任务或业务管理员的工作中,利益相关者可能决定一个任务不再需要,因此跳过此任务。这使得任务过渡到废弃状态。只有当在任务配置期间指定了此功能时,才能跳过任务(skippable属性)。

对于委托、转发和跳过以及暂停/恢复的示例,请查看测试类:

com.packt.masterjbpm6.task.TaskTest (methods testDelegateReadyStateAndSkip, testForwardAndSkip, testSuspendAndResume)

示例流程:

delegate_forward.bpmn

描述:主流程启动,并将一个人类任务保留给 Luigi。测试方法检查任务委托、转发和暂停/恢复。

发布

一个任务可以被当前所有者发布为一个人类任务,使其可供其他潜在所有者使用。从具有实际所有者(保留或进行中)的活跃状态,任务可以被释放并过渡到就绪状态。与任务关联的任务数据保持不变。

如果一个任务当前处于进行中状态,实际所有者可以停止它,将其过渡到保留状态。与任务关联的业务数据以及其实际所有者保持不变。

脚本任务

脚本任务是一个自动活动。当流程执行到达脚本任务时,将执行相应的脚本。可以通过执行上下文(kcontext变量)访问的所有流程变量都可以在脚本中引用。它具有以下属性:

  • 它由业务流程引擎执行

  • 脚本定义在引擎支持的语言中(Java 或 MVEL)

  • 脚本任务执行总是立即的

  • 脚本任务在脚本执行后过渡到完成状态

注意

对于完整的 MVEL 参考,请访问mvel.codehaus.org/

示例测试类:

com.packt.masterjbpm6.activity.ScriptTaskTest

示例流程:

script.bpmn

描述:流程脚本活动更新流程变量order的描述属性:

Order order=(Order)kcontext.getVariable ("order");
order.setNote ("order modified");

服务任务

服务任务指示服务提供者要自动执行的工作。通常,所有需要在引擎外部执行的工作都应该设计为服务任务。jBPM 支持两种类型的服务任务实现:普通 Java 类和 Web 服务。服务任务由名为服务任务的 WorkItemHandler 实现支持。

参数如下:

  • 接口: Java 类名称或 WSDL WebService 服务接口

  • 操作: Java 方法名称或 WSDL WebService 操作

  • 参数: 调用方法名称

  • ParameterType: 调用方法的参数类型(仅支持 1 个参数)

  • 模式(仅限 WS): SYNC(默认),ASYNCONEWAY

对于 Java 类型的服务任务,jBPM 使用 Java 反射通过使用接口参数加载 Java 类类型,实例化它,并使用参数提供的值调用指定的方法(通过操作ParameterType搜索)。仅支持具有单个参数的方法签名,并且调用方法的结果映射到活动结果输出参数。

模式参数仅适用于 Web 服务,并描述了请求必须执行的方式:

  • 同步(SYNC): 发送请求并在继续之前等待响应

  • 异步(ASYNC): 发送请求并使用回调获取响应

  • 单向(Oneway): 发送请求而不阻塞(忽略响应)

Web 服务运行时利用 Apache CXF 框架的“动态客户端”功能在运行时生成 Java 类。

注意

请访问cxf.apache.org/docs/dynamic-clients.html获取官方参考文档。

服务任务对于快速原型设计非常有用,但当涉及到复杂的外部服务集成时,它在满足常见开发需求方面存在不足:多个参数传递、额外的 Web 服务配置等。

以下示例演示了如何通过添加自定义工作项处理器来覆盖标准的 jBPM 服务任务组件。请注意,然而,由于任务接口是在 jBPM 工作项处理器的配置文件中定义的,因此无法从流程设计器中更改自定义服务任务处理器的输入/输出参数。

注意

工作项处理器在第七章自定义和扩展 jBPM中进行了详细解释。

示例测试类:

com.packt.masterjbpm6.test.ServiceTaskTest

示例流程:

servicetask.bpmn

描述:第一个测试(testJavaServiceTask)使用标准 Java 服务任务(接口:ServiceJavaTask,操作:processOrder,参数:order,参数类型:Order)启动过程。服务任务更改订单的 note 字段并将其返回到主过程,其脚本活动将更改跟踪到控制台。第二个测试(testJavaCustomServiceTask)具有自定义服务任务处理程序(PacktServiceTaskHandler),它覆盖默认处理程序并处理订单参数,使用特定值设置其note属性。

规则任务

(业务)规则任务使我们能够执行规则并从嵌入的规则引擎(Drools)获取输出。请记住,可以通过使用全局变量或 Drools 会话事实来共享过程变量与规则任务。

示例类:

com.packt.masterjbpm6.task.RuleTaskTest

示例知识工件:

rule.bpmn, rule.drl

描述:主过程启动,当订单成本大于 100 时触发规则任务,结果将订单的note属性更改为URGENT。查看rule.drl文件:

global StringBuffer newnote;
global com.packt.masterjbpm6.pizza.model.Order orderglobal;

rule "checkorder" ruleflow-group "masterRuleGroup"
    when
        $o: com.packt.masterjbpm6.pizza.model.Order (cost>100)
    then
    {
      System.out.println ("checkorder triggered");
      String desc="big order ! (cost="+$o.getCost()+")";
      orderglobal.setNote("URGENT");
      newnote.append (desc);
    }
End

order变量(cost > 100)插入到知识会话中,以激活当Order (cost > 100)触发时的规则;请参阅RuleTaskTest.testRule()方法:

ksession.insert(order);

当使用共享的orderglobal变量获取结果时:

ksession.setGlobal("orderglobal", order);

发送/接收任务

发送/接收任务是一般用途的消息任务,因为它们不提供默认实现。它们作为工作项处理,实现者必须通过WorkItemHandler接口提供工作实现,并将其注册到 jBPM 的WorkItemManager

接收任务的workItem名称必须是接收任务接收任务通过messageRef属性引用消息 ID;处理程序通过MessageId参数接收消息 ID 值。

发送任务的workItem名称必须是发送任务发送任务通过messageRef属性引用消息 ID;为了额外的参考,请检查中间消息事件。

发送/接收任务

示例类

com.packt.masterjbpm6.task.TaskTest (method testSendReceive)

示例过程工件:

send_receive.bpmn

描述:子过程发送任务将数据传递给父过程的接收任务。测试注册了两个自定义工作项处理程序,发送任务和接收任务通过全局过程变量共享消息。

手动任务

手动任务定义了一个需要在引擎外部执行的任务。它用于模拟由利益相关者执行且不与系统交互的工作;引擎对任务一无所知,也不需要知道。没有用户界面或系统可用于手动任务完成。对于引擎来说,手动任务被管理为一个透传活动。它从过程执行到达的那一刻开始继续。

临时(自定义或无)任务

自定义任务是一个空的、通用的、非专业的工作单元。实施者被要求为任务提供 WorkItemHandler 实现,并将其注册到 WorkItemManager

Void registerWorkItemHandler(String workItemName, WorkItemHandler handler);

注意

请参阅第七章,自定义和扩展 jBPM,以获取关于 WorkItemHandler 架构的详细章节。

处理程序已注册给所有具有给定工作项名称的工作项,并且每当流程激活具有该名称的节点时都会被调用。此外,工作项名称必须与任务元素的taskname属性匹配。WorkItemHandler 负责完成或终止任务实例。

请参阅条件事件部分以获取一个工作示例。

异步任务

现在,我们将更详细地研究自定义任务的一些特殊用法。在第四章,作业管理中,我们介绍了新的 jBPM 执行器服务和 KIE 控制台的作业调度功能。自定义任务可以方便地配置,以指导执行器通过在后台安排执行作业以异步方式调用面向服务的组件。负责作业提交的 jBPM 处理程序是org.jbpm.executor.impl.wih.AsyncWorkItemHandler(更多内容请参阅第七章,自定义和扩展 jBPM)。

提示

jBPM 流程设计器允许你在工作项处理节点上切换wait-for-completion标志。此标志并不反映处理调用同步/异步的本质。但它确实告诉引擎通过事件监听器评估处理结果,并使用任务输出映射将它们映射回流程上下文变量。如果该标志设置为 false,则自定义任务结果将被忽略。

我们可以通过以下方式配置一个异步任务:

  • 指定async作为任务taskName属性

  • 添加一个名为CommandClass的数据输入参数,并将完全限定的 Java 类名分配给调度

  • (可选)添加一个名为Retries的数据输入参数,它告诉执行器执行应该重试多少次(默认=3)

注意

第四章,管理作业和异步命令执行详细说明了如何编写Command类。

我们讨论的示例将我们的AsyncTaskCommand设置为CommandClass,启动执行器服务,并注册 AyncWorkItemHandler。

示例类:

com.packt.masterjbpm6.task.AsyncTaskTest

示例流程工件:

asynctaskprocess.bpmn

调用活动任务

调用活动任务是一种通用方法,通过指定它们的 ID(bpmn2:callActivitycalledElement属性)或NamecalledElementByName)来重用现有的、外部定义的业务结构(流程)。执行被调用元素的同步/异步(waitForCompletion=true)或独立(independent=true)。只有当waitForCompletiontrue时,才能将independent设置为false

所有这些属性都很容易设置,就像往常一样,通过 jBPM Eclipse 插件或 KIE 流程编辑器;为了参考目的,我们从流程定义中提取了callActivity构造的相关 XML:

<bpmn2:callActivity drools:waitForCompletion="true" drools:independent="true" name="CallActivity" calledElement="callactivitySubprocess">

下图显示了左侧的主流程和从调用活动节点“缩放”出来的callactivitySub1流程:

调用活动任务

被调用构造支持,就像其他活动节点(任务)一样,从调用进程到被调用进程的数据输入和输出映射,正如我们将在以下示例中看到的那样。

示例类:

com.packt.masterjbpm6.task.CallactivityTaskTest (testIndependentSubprocess method)

示例流程工件:

callactivity.bpmn (parent process), callactivitySub1.bpmn (subprocess called by the callActivity construct)

描述:启动主流程并执行callActivity;主流程将流程顺序变量传递给callActivitycallActivity子流程修改顺序变量并将其返回给调用流程定义。

作为旁注,如果我们检查PROCESSINSTANCELOG表,我们可以看到两个流程实例(主流程和被调用流程)的记录;它们的父子关系通过PARENTPROCESSINSTACEID列保存;它显示callactivitySubprocesscallactivityprocess的子流程。这是当callActivity具有independent=truewaitforcompletion=true属性时的输出。

调用活动任务

让我们看看另一个示例,看看独立属性是如何影响被调用子进程的。

示例类:

com.packt.masterjbpm6.task.CallactivityTaskTest (method testAbortProcess)

示例流程工件:

callactivityabort.bpmn (parent process), callactivitysubprocessabort.bpmn (subprocess called by the call activity construct)

描述:启动callactivityabort流程,并执行callActivityindependent=false)。被callActivity引用的子进程(callactivitysubprocessabort)包含一个人类任务,因此它停止以进行用户交互。这给我们提供了在父流程实例上发出(参见测试类代码)abortProcessInstance的时间。将independent标志设置为FALSE强制callActivity(即等待的子进程)在主流程实例的上下文中终止;当标志设置为TRUE时,callActivity不受影响(参见上一个示例)。

这是当终止具有independent=false属性的父流程实例时的输出。注意,两个流程实例的状态都是3(已终止)。

调用活动任务

子进程

如其名所示,子过程是包含在其他流程中的流程。它可以包含活动、事件、网关等,形成一个框装的流程,它是封装流程的一部分。子过程可以完全定义在父流程内(嵌入的子过程)或可以通过 ID 或名称属性通过调用活动元素链接。您可以通过调用活动在不同多个流程定义之间链接子过程,重用常见的流程元素组(活动、网关等)。嵌入的子过程结构可以具有多实例功能(参见多实例部分)。然而,使用子过程确实会施加以下约束:

  • 顺序流不能跨越子过程边界

  • 流程变量必须映射为输入和/或输出

在设计器级别,子过程可以被展开或折叠,以便隐藏或显示其细节。

临时子过程

当多个任务可以按任意顺序选择和执行(因为未指定或未知),并且它们之间没有执行依赖关系时,通常使用临时子过程。任务可能有未知的依赖关系,最常见的是因为它们是动态的,并且由人类用户根据具体情况管理。即使某些任务根本未执行,子过程也可以完成。临时子过程表示为带有波浪线(˜)标记的子过程。

临时子过程

jBPM 的临时子过程实现似乎相当不完整。在退出子过程实例时似乎存在一些问题。用户可以通过使用signal方法并引用活动名称来启动临时子过程活动:

ksession.signalEvent("report1", null, processInstance.getId());

由于它们的性质,临时子过程难以设计,在现实的结构化业务流程中作用不大;然而,在这里,我们为您提供了一个您可以调整和实验的示例:

示例类:

com.packt.masterjbpm6.task.AdHocSubprocessTest

示例流程工件:

adhocsubprocess.bpmn

描述:该临时子过程有 2 个脚本活动和 1 个人工任务。脚本任务被信号触发,人工任务被完成。

多个实例

此结构可用于创建可重用子过程定义的多个实例以及嵌入的子过程。传递输入参数集合作为实例化循环。jBPM 将为集合中的每个元素创建一个循环流程的实例。以下图显示了包含嵌入的多实例子过程(Log pizzas,平行符号表示它是一个多实例过程)和子过程属性。循环输入是流程变量list,循环实例参数(集合项)是item类型的Pizzaitem变量在实例化子过程的范围内可见。

多个实例

示例类:

com.packt.masterjbpm6.task.MultiInstanceTest

示例流程工件:

multiinstance.bpmn

描述:该流程通过传递一个披萨变量list创建:

List<Pizza> myList = new ArrayList<Pizza>();
myList.add(new Pizza(PizzaType.getType(Types.MARGHERITA), "margherita"));
myList.add(new Pizza(PizzaType.getType(Types.NAPOLI), "assorreta!"));
params.put("list", myList);
ProcessInstance processInstance = ksession.startProcess("multiinstance", params);

随后,创建了两个子进程实例,每个实例都传递了循环的item变量(一个Pizza实例)。子进程脚本活动仅打印披萨描述,然后子进程退出。

System.out.println("pizza desc " + item.getDesc());

车道

车道是一个用于在流程定义中分组活动的分区框状元素。车道可以用来直观地指出不同的分组任务分配。例如,你可以将车道想象为一个公司部门(IT、商业管理等等)中所有员工都有(或多或少)相同的职责。jBPM 会尝试将同一车道内的所有任务分配给同一用户(为用户保留任务)。例如,如果一个车道上有几个任务,那么完成第一个任务的用户将被分配到车道上的其他任务。通常,将相同的组 ID 分配给同一车道上的所有任务是很方便的。

示例类:

com.packt.masterjbpm6.task.LaneTest

示例流程工件:

lane.bpmn

描述:task1task2(在车道)活动分配给pizzerianapoli组,而Mario's Task分配给演员 Mario。taskNotInLane也分配给pizzerianapoli,但它不在车道上。

车道

在流程启动后,演员 Luigi(属于pizzerianapoli组;参见LaneUserCallBack类)在列表上有 2 个任务(task1taskNotInLane)。在他完成 task1 后,他自动获得 task2 活动(状态=已保留),而taskNotInLane的状态保持不变(就绪)。

车道

数据对象

数据对象是 BPMN 结构,表示活动需要或产生数据的方式。数据对象可以与一个或多个活动直接关联,为该活动提供输入或目标输出。

数据对象

示例类:

com.packt.masterjbpm6.task.DataObjectTest

示例流程工件:

data-object.bpmn

描述:task1 和 task2 活动共享相同的数据对象(披萨类类型);第一个任务生成披萨,然后作为第二个任务的输入。

摘要

在本章中,我们检查了 jBPM BPMN 结构,提供了动手操作的示例、技巧,并在可能的情况下,提供了一些关于 jBPM 内部机制的细节。本章的目的不是成为 BPMN 教程或 BPMN 最佳实践建模指南,我们建议选择更合适的书籍和大量的实际操作。在下一章中,我们将通过几个实际示例介绍 jBPM 子系统 API:新的 Kie API、运行时引擎、人类任务服务和持久化引擎。

第六章。核心架构

第一章,业务流程建模 – 连接业务与技术,为你提供了对新的 KIE 平台和 jBPM 工具栈组件的概述。这一章将向你展示 jBPM 是如何构建的,以及它的组件和子系统是什么,它将通过 jBPM 的源代码,用示例说明如何利用其模块化系统提供的灵活性。

本章我们将涵盖的主题列表如下:

  • 核心 API

  • 运行时引擎

  • 人类任务服务

  • 持久性和事务

  • 历史日志

KIE API

新的 KIE API 产生于提供新的公共软件服务层和为经过良好评估的 Red Hat 项目(主要是 Drools 和 jBPM)提供集成 IDE(工作台)的需求。通过 KIE API,添加了几个功能,以简化这些平台与多个环境的集成:JMS、Rest、SOAP、CDI、Spring、Seam、OSGi 和纯 Java 应用程序。

droolsjbpm-integration 额外项目(托管在 github.com/droolsjbpm/droolsjbpm-integration)提供了各种环境和技术的集成包。

我们在第四章中预览了一些新的 KIE 概念,运营管理(基于 Maven 的部署、KieModule、kmodule.xml 文件和 KieScanner),因此你应该准备好深入探讨这个主题。你还将找到,作为我们示例的配套支持,特定 KIE 组件关系的类图,这应该有助于你更清晰地了解 KIE 内部组织结构。

注意

www.kiegroup.org 是所有 KIE 技术的门户。

KieServices

KieServices 是一个线程安全的单例,充当服务工厂。它提供了对主要 KIE 组件和服务的较高层次访问。

可以通过以下方式通过其工厂获取 KieServices 引用:

KieServices ks = KieServices.Factory.get();

主要的 KIE API 服务如下:

  • KieContainer:这本质上是对 KieModule 和它定义的 KieBase(s) 的包装;它可以编译和验证 KieBase 并生成新的 KieSession(s)。

  • KieRepository:这是一个管理 KieModules 的仓库,抽象出模块源;它可以是安装在 Maven 仓库中的模块,也可以是用户通过编程创建并添加的模块。

  • KieScanner:这是一个 Maven 仓库工件扫描器。

  • KieBuilder:这是一个从其源文件集开始编译和构建 KieModule 的辅助工具。

  • KieResources:这是一个从各种 I/O 源(类路径、URL 等)创建专用资源的工厂。

  • KieLoggers:这是一个用于会话的日志配置器。

  • KieStoreServices:这是一个存储服务,用于管理 jBPM 和 Drools 运行时状态的可持久性。

  • KieMarshallers:Marshalling 提供了一个可定制的序列化架构、实用类和策略。我们将在 第七章 定制和扩展 jBPM 中描述 jBPM 灵活的 marshalling 架构。

让我们先讨论与 jBPM 运行时配置和设置相关的 KIE API 组件,因为所有您的知识工件在运行时都代表了业务流程执行引擎的构建基础。

注意

接下来的部分不属于 KieServices 这一部分的逻辑分组,因为它们都是 KIE 一等公民(类)并且可以在不考虑 KieServices 工厂类的情况下使用和创建。您可以在 jbpm-misc 项目中找到本章的完整源代码示例。

KieContainer – KIE 模块和 KieBase(s)

KieContainer 已经专门设计来处理 KIE 模块并解决其依赖关系(其他 KIE 模块或 Maven 化的 JAR),即使是通过远程 Maven 仓库。与较老的 jBPM 版本相比,这在知识模块共享和管理能力方面是一个巨大的改进。虽然 KIE 模块是一个收集一组业务工件和静态资源的集合,但组织 KieBase 和 KieSession 定义以及为用户提供获取它们的新现成引用的工具的责任在于 KieContainer。

注意

您可以在 KieContainerTest 类中找到示例。

KieContainer 可以使用 Java 类路径或用户提供的 ClassLoader 来检测、加载和包装现有的 KieModule

KieContainer kContainer = ks.getKieClasspathContainer();

getKieClasspathContainer() 函数返回包装了当前类路径中找到的 KieBaseKieContainer(通过解析可用的 kmodule.xml 文件创建)。

在 第四章 运营管理 中,我们讨论了新的 Maven 仓库集成功能。KieContainer 能够从 Maven 仓库加载 KieModule,给定其 Maven GroupId-ArtifactId-VersionGAV);如下使用 ReleaseId 类:

// create the Maven GAV wrapper
ReleaseId releaseId = ks.newReleaseId("com.packt.masterjbpm6", "pizzadelivery", "1.0-SNAPSHOT");
// then create the container to load the existing module
KieContainer kieContainer = ks.newKieContainer(releaseId);

通过将 KIE 客户端仓库服务投入使用,该容器还能够根据其 Maven GAV 动态更新其定义,从不同的 KIE 模块开始。因此,所有现有的 KieBase 资产和 KieSession 定义都将逐步更新(并且缓存的类定义将被较新的定义替换)。

ReleaseId newReleaseId = ks.newReleaseId("com.packt.masterjbpm6", "pizzadelivery", "1.1-SNAPSHOT"); 
// update the container with the KIE module identified by its GAV
Results result = kieContainer.updateToVersion (newReleaseId);
if (result.hasMessages (Level.ERROR))
{
List<Message> errors= result.getMessages(Level.ERROR);

KieBase(知识库)是 KieModule 的构建块。KieBase 类作为 KieModule 知识定义的存储库,并作为 KIE 会话的字典。它包含 Drools 规则、流程、模型等。默认情况下,这些工件在 KIE 项目的 resources 根目录中搜索,但您可以设置 packages 属性以在不同的文件夹中搜索,例如(kmodule.xml 的摘录):

<kbase name="kbase" packages="com.packt.masterjbpm6.event">

这将加载来自resources/com/packt/masterjbpm6/event项目文件夹的工件。

你的 KieModule 必须始终至少有一个命名的 KieBase(即其name属性必须设置);或者,如果你决定使用默认的(即不知道其名称而创建的)KieBase,则在你的kmodule.xml中完全省略<kbase>元素定义,或者使kmodule.xml为空。

小贴士

没有底层 KieBase 的 KieSession 没有太多意义。在用户没有指定的情况下,KIE 运行时为你提供了一个默认的 KieBase。这个默认的 KieBase 是具有packages="*"属性的 KieBase,这意味着它定义了包含在所有模块包中的所有资产。

KieBase 由 KieContainer 创建,支持继承(包含)和多重定义:

  • 包含:属于“包含的 KieBase”的所有知识工件都被添加到“包含的 KieBase”中;例如,所有kbaseold资源都被添加到kbasenew KieBase 中:

    <kbase name="kbase" includes="kbaseold"
    packages="com.packt.masterjbpm6.process">
        <ksession name="ksession" />
    </kbase>
    

包含的 KieBase 必须已经可用(定义它的 KieModule 必须已部署)或本地定义(相同的kmodule.xml文件)。在第四章中,运营管理(ManagedVesuvio 存储库示例)中,Napoli KieModule 的 kbase 包含 Vesuvio 的 kbase,以便重用其外部过程定义作为子过程;让我们通过查看它们的 Kie 模块定义来澄清。

Napoli kbase定义如下(仅相关部分):

<kbase name="kbase-napoli" default="true" packages="*" includes="kbase-vesuvio">

Vesuvio kbase定义如下(仅相关部分):

<kbase name="kbase-vesuvio" default="false" packages="*">

注意,为了使 KIE 拾取主 kbase(kbase-napoli),我们将kbase-vesuvio kbase 的default属性设置为false

  • 多重定义:可以在单个 KieModule 内部定义多个 KieBase(和 KieSession):

    <kbase name="kbase" includes="kbaseold"
    packages="com.packt.masterjbpm6.process">
        <ksession name="ksession" />
    </kbase>
    <kbase name="kbaseold" packages="com.packt.masterjbpm6.event">
        <ksession name="ksession2" />
    </kbase>
    

一旦定义了 KieBase,就可以创建一个有状态的 KieSession(默认的或命名的,通过传递其name属性)。

KieSession kieSession=kieContainer.newKieSession ("ksession");

每个 KieSession 都与一个单独的 KieBase 配对:KieContainer 实际上将会话创建委托给其 KieBase。

注意

KieBase 和 KieSession 支持许多声明性配置设置,你可以将其添加到你的kmodule.xml文件中;请参阅 jBPM 6.2 参考文档。

以下类图显示了在处理容器时必须处理的主体类(会话将在下一节中讨论)。

KieContainer – KIE 模块和 KieBase(s)

KIE 构建器 API

很可能你已经使用过KnowledgeBuilderFactoryKnowledgeBuilder来设置KnowledgeBase:KnowledgeBuilder 解析知识源文件(处理bpmn文件、Drools .drl规则等),并将它们转换为 KnowledgeBase 可以使用的知识包。资源通过类型(ResourceType枚举)进行标识和添加。KnowledgeBase 已弃用,但KieBase实际上是由KnowledgeBaseImpl实现的。

KIE API 提供了专门用于管理 KieModule 的工具:文件创建和资源组装、模块依赖管理、构建和部署到 Maven 仓库。以下类图显示了主要构建相关类(仅作快速参考)。

KIE 构建器 API

注意

您可以在 KieBuilderTestKieResourceTest 类中找到示例。

KieResources

资源是一个知识元素(流程、规则等)或资源的合同表示,或者可以间接用于加载 Kie 模块的资源(例如:kmodule.xml 的路径)。

KieResources 工厂简化了以 org.kie.api.io.Resource 形式处理对象的任务;例如:

Resource res=ks.getResources().newFileSystemResource (new File("/jbpm-constructs/target/classes/"));

此资源表示包含 KIE 模块的路径。

KieModule

虽然 KieContainer 代表一个抽象,但 KieModule 的本质是基于业务资产文件(资源):KieModule 是定义一组 KieBase 类所需的所有资源的容器。

小贴士

模块项目的源结构必须符合 Maven 项目的标准布局(如 src/main/resources)。

  • pom.xml 定义 KieModule Maven GAV

  • kmodule.xml 声明 KieBase、KieSession 及其属性

  • 知识工件

KieModule 通过 pom.xml 文件跟踪来自其他 Kie 模块和其他普通 JAR 存档的模块依赖项。

KieBuilder

KieBuilder 允许您通过添加资源和管理配置文件(通过一系列模型类,即元模型)来构建 KieModule,这些模型类代表关键的 KieModule 组件:

  • KieModuleModel: 一个 KieModule 抽象

  • KieBaseModel: 一个 KieBase 抽象

  • KieSessionModel: 一个 KieSession 抽象

基于内存的文件系统类(KieFileSystem)帮助您创建/编写 KIE 模块文件(pom.xmlkmodule.xml)。以下类图显示了 KieBuilder 和相关类(详情见后)。

KieBuilder

让我们通过一个实际例子来看 KIE 元模型创建和使用,以便从头开始在一个 Maven 仓库中设置和安装一个 KIE 模块(包括依赖项)。

您可以在 KieBuilderTest 类(testBuilderWithModels)中找到完整的示例。

KieServices ks = KieServices.Factory.get();
// create the KIE module model
KieModuleModel kmodule = ks.newKieModuleModel();
// create the KieBase model
KieBaseModel kieBaseModel = kmodule.newKieBaseModel("KBase");
// create the KieSession model
KieSessionModel ksession1 = kieBaseModel.newKieSessionModel("KSession").setDefault(true);
KieFileSystem kfs = ks.newKieFileSystem();
ReleaseId rid = ks.newReleaseId("com.packt.masterjbpm6", "pizzaDeliveryNew ", "1.0");
// generate pom.xml file
kfs.generateAndWritePomXML(rid);
// and write the <kmodule> xml file
kfs.writeKModuleXML(kmodule.toXML());

当您的文件集准备就绪时,将 KieFileSystem(内容)传递给构建器:

KieBuilder kieBuilder = ks.newKieBuilder(kfs);

// add dependencies (here, we put jar Files as Resources but you
// can use one or more KieModule too)
Resource dependencyRes = ks.getResources().newFileSystemResource(new File("c:/temp/pizzadelivery-1.0.jar "));
kieBuilder.setDependencies(dependencyRes);

我们现在可以执行“构建”。构建会编译所有模块知识包和 Java 类,验证配置文件(pom.xml 和 kmodule.xml),最后将模块安装到本地 KIE 仓库中:

kieBuilder.buildAll();
if (kieBuilder.getResults().hasMessages(Level.ERROR)) {

}

以编程方式创建 KieModule 意味着你必须以文件方式创建对象,例如:

  String myprocess= "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n <definitions id=\"Definition\"\n" +

kfs.write("src/main/resources/process.bpmn", myprocess);

运行时将根据您的 KieModule 文件系统结构创建文件。

仓库和扫描器

如我们之前所指出的,Maven 仓库是新 Kie 架构的一个重要组成部分:仓库服务允许您通过内部 KIE 仓库管理模块安装和依赖解析:

KieServices ks = KieServices.Factory.get();
KieRepository kr = ks.getRepository();

要将模块添加到 KIE 仓库存储,你必须提供 kmodule.xml 文件的路径或构建的 KIE 模块 JAR 文件的路径:

Resource kieresource= ks.getResources().newFileSystemResource(new File("c:/Users/simo/git/masterjbpm6/pizzadelivery/target/classes/"));
// or 
ks.getResources().newFileSystemResource(new File("c:/Users/simo/git/masterjbpm6/pizzadelivery/target/pizzadelivery-1.0.jar"));
// add to the KIE repo
KieModule kModule = kr.addKieModule(kieresource);
// load and use the module
KieContainer kContainer = ks.newKieContainer(kproj.getReleaseId());

addKieModule 方法接受可选的模块资源依赖项(再次,以 kmodule.xml 路径或 JAR 存档的路径的形式)。

从仓库加载一个模块就像以下这样简单:

ReleaseId releaseId = ks.newReleaseId("com.packt.masterjbpm6", "pizzadelivery", "1.0-SNAPSHOT");
KieModule kModule=kr.getKieModule(releaseId);

仓库服务封装了内部 Maven KIE 仓库服务以及我们接下来将要看到的 KieScanner 服务。

KieScanner

KieScanner 是一个用于 Maven 仓库(本地和远程)的监控器,用于自动检测给定的 KieModule 是否有更新的发布版本:在这种情况下,如果找到模块的新部署的工件,扫描器会更新 KieContainer 以反映更改的定义(KieBase、KieSession 等)。KIE 模块 KieBase(s) 会被重新构建,并且所有从 KieContainer 创建的新 KieSessions 都将使用更新的 KIE 模块定义。

扫描器可以执行阻塞式扫描更新(scanNow() 方法),在(最终)更新过程完成后返回,或者执行后台扫描过程(start(long pollingInterval)stop() 方法)。

KieServices ks = KieServices.Factory.get();
ReleaseId releaseId = ks.newReleaseId("com.packt.masterjbpm6", "pizzadelivery", "1.0-SNAPSHOT");
KieContainer kieContainer = ks.newKieContainer(releaseId);
// bind the scanner to the container
KieScanner scanner = ks.newKieScanner(kieContainer);
// synchronous scanner
scanner.scanNow();

小贴士

扫描器仅在配对的 KieContainer 的模块 Maven 版本(其 GAV 中的 V)不是固定版本时才工作。只有具有 SNAPSHOT、LATEST 或 RELEASE 限定符的版本,或范围版本的模块才会被处理。有关更多信息,请参阅 Maven 版本参考。

扫描操作执行以下操作:

  • 构建新的 Kie 模块,寻找错误。

  • 更新旧的模块依赖项。

  • 更新旧的模块资产,编译它们,并重新构建模块知识库。

  • 如果没有检测到构建错误,则将更新的模块添加到 KIE 模块仓库。

如果知识库正在使用新的或更新的类,则将其完全重新创建;否则,其资源将增量更新。过时的知识库和会话(其定义已被删除)将从 Kie 容器中删除。关于扫描器的一般建议是极其谨慎,并针对每个案例进行评估。

示例 KieScannerTest 类为你提供了两个测试方法:

  • testScannerUpdateNewSession:通过创建一个新会话并验证更新过程变量返回的值与原始定义不同,来验证扫描过程后 Kie 模块流程定义是否得到更新。

  • testScannerSameSessionAfterUpdate:验证扫描后,现有的会话是否继续使用其旧流程定义,而新的 KIE 会话则获取更新的流程定义。

扫描仪在之前的 jBPM 知识库更新机制(KnowledgeAgent)上是一个很好的改进,因为它与 Maven 紧密集成,并为实现者提供了以资产为导向的编程风格来处理 KIE 模块、Kie 项目源和资产。这一重大改进使 jBPM 更适合典型的敏捷、精益开发环境。只需想想当你与持续集成CI)、部署和自动化测试工具集成时的可能性。你可能有一个扫描器进程,它会检查你的夜间构建 Maven 仓库主干,使用你资产的最新开发版本更新你的 KIE 模块,并触发你的自动化测试套件。

注意

KieScanner API 实现和实用类属于kie-ci项目(github.com/droolsjbpm/drools/tree/master/kie-ci)。

扫描仪 API 还提供了一个 Maven 辅助类,该类管理工件查找和部署到系统 Maven 仓库:

MavenRepository repo = MavenRepository.getMavenRepository();
List<DependencyDescriptor> dependencies = repo.getArtifactDependecies("com.packt.masterjbpm6:pizzadelivery:1.0");
Artifact module = repo.resolveArtifact(ks.newReleaseId(
  "com.packt.masterjbpm6", "pizzadelivery", "1.0"));

KieScannerTest jUnit 测试类测试扫描器和构建 API。它为pizzadelivery KieModule 创建并部署了一个新版本(buildModuleForScannerUpdate方法),然后启动扫描器更新过程(testScannerUpdate()方法)。

KieLoggers

KieLoggers 工厂允许你创建审计日志记录器,它产生在特定 KIE 会话执行期间发生的所有事件的日志跟踪。以下类型的日志记录器可用:

  • 基于文件的日志记录器:以默认.log扩展名的文件日志记录器;以 XML 序列化格式跟踪事件:

    KieRuntimeLogger logger = loggers.newFileLogger(ksession, "c:/temp/kielogger");
    

    查看KieLoggersTest.testLoggers方法以获取完整示例。

  • 基于控制台的日志记录器:将日志跟踪到标准输出:

    KieRuntimeLogger logger = loggers.newConsoleLogger(ksession);
    

    在运行testRuleWithConsoleLogger方法时,你可以看到 Drools 事实的插入和 Drool 规则的触发:

    13:31:03.459 [main] INFO  o.d.c.a.WorkingMemoryConsoleLogger - OBJECT ASSERTED value:com.packt.masterjbpm6.pizza.model.Order@12e13d86 factId: 1
    
    13:31:03.708 [main] INFO  o.d.c.a.WorkingMemoryConsoleLogger - BEFORE RULEFLOW GROUP ACTIVATED group:masterRuleGroup[size=1]
    13:31:03.724 [main] INFO  o.d.c.a.WorkingMemoryConsoleLogger - BEFORE ACTIVATION FIRED rule:checkorder activationId:checkorder [1] declarations: $o=com.packt.masterjbpm6.pizza.model.Order@12e13d86 ruleflow-group: masterRuleGroup
    
  • 线程日志记录器:与基于文件的日志记录器相同,但以异步方式执行对文件的写入;它具有设置写入(刷新)间隔周期的选项,例如:

    // update the log file every 5 seconds
    KieRuntimeLogger logger = loggers.newThreadedFileLogger(ksession, "c:/temp/kie_threaded", 5000);
    

    查看testRuleWithThreadedLogger示例以获取完整示例。

日志类扩展WorkingMemoryLogger,该类实现了所有可用的事件监听器接口:ProcessAgendaRule和(KIE)知识库。由于会生成多个事件,你可以使用以下方法控制事件过滤:addFilterremoveFilter以及传递一个实现ILogEventFilter的类。我们可以在kmodule.xml文件中直接声明和配置 KieSession 日志记录器,例如:

<ksession

<fileLogger id="filelogger" file="mysession.log" threaded="true" interval="10" />
<consoleLogger id="consolelog" />
</ksession>

以下类图显示了日志记录器和事件监听器接口:

KieLoggers

KieStoreServices

org.kie.api.persistence.jpa.KieStoreServices 是一个定义 KIE 会话持久化服务合约的接口。它的默认实现是 org.drools.persistence.jpa.KnowledgeStoreServiceImpl 类。让我们看看如何使用 KieStoreServices 通过会话 ID 恢复 KIE 会话。以下示例是 StorageTest.testRuleWithStorageServer 方法的摘录。它展示了如何安全地从持久化存储中加载您的 Kie 会话并一致地执行业务流程。

KieServices ks = KieServices.Factory.get();
KieStoreServices storeservice = ks.getStoreServices();
KieContainer kContainer = ks.getKieClasspathContainer();
KieBase kbase = kContainer.getKieBase("kbase");
// initialize the Session Environment with EMF and the TM
Environment env = EnvironmentFactory.newEnvironment();
    env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, super.getEmf());

// current Bitronix transaction manager
  env.set(EnvironmentName.TRANSACTION_MANAGER,
    TransactionManagerServices.getTransactionManager());
// create the session
  ksession = storeservice.newKieSession(kbase, null, env);
// perform the Rule task test
  testRule();
  long id = ksession.getIdentifier();
// dispose the session
  ksession.dispose();
// reload the session given its ID
  KieSession loadedsession = storeservice.loadKieSession(id, kbase, null,env);
// check it is the same session
  assertEquals(id, loadedsession.getIdentifier());
// rerun the test on the loaded session
ksession = loadedsession;
  testRule();

运行时管理器服务和引擎

RuntimeManager 的引入是为了简化 KieBase (KnowledgeBase) 和 KieSession (KnowledgeSession) 的配置。其主要职责是根据预定义的策略管理和创建 RuntimeEngine 实例(见 运行时策略 部分)。

org.kie.api.runtime.manager.RuntimeEngine 是进入引擎服务的入口点;其主要目的是为用户提供预配置和即用型引擎组件:

  • KieSession

  • TaskService

  • AuditService

org.kie.api.runtime.manager.RuntimeManager 解除了用户从典型样板代码开发中的负担;它为流程设置执行环境(将 KieSession 和任务服务包装到 RuntimeEngine 中)并管理以下 Drools 服务:

  • Scheduler service:调度服务管理基于计时器的作业执行(我们已在 第四章,运营管理 和 第五章,BPMN 构造 中使用 Async taskBPMN 构造 中见过)

  • TimerService:为会话实现计时器服务

运行时管理器在会话上注册以下项目:

  • 流程工作项处理器(也包括默认的 human task 工作项处理器,它负责管理任务节点)

  • 全局变量

  • 事件监听器

运行时策略

RuntimeManager 实现了一个运行时策略,允许您选择如何管理您的 KieSession 生命周期;让我们看看可用的策略:

  • Singleton(默认 jBPM 策略):运行时管理仅一个共享的 RuntimeEngine 实例(只有一个 Kie 会话是活动状态并共享)

  • PerProcessInstance:管理器为每个流程实例使用一个专用的 Kie 会话;Kie 会话生命周期跨越流程实例的持续时间

  • PerRequest:调用 getRuntimeEngine() 方法返回一个新的 RuntimeEngine 实例(每次都创建一个新的 Kie 会话和任务服务)

RuntimeManager 必须从 RuntimeManagerFactory 创建,根据选择的运行时策略(newSingletonRuntimeManager()newPerRequestRuntimeManager()newPerProcessInstanceRuntimeManager())调用其专用工厂方法,并传递 org.kie.api.runtime.manager.RuntimeEnvironment 的实例。

选择正确的策略

您应主要根据您的业务和系统规范选择正确的运行时策略。需求可能限制您保持隔离的 jBPM 会话工作内存(例如,每个流程实例一个会话);换句话说,每个会话拥有自己的规则、事实和对象。这在需要低资源竞争和高吞吐量的高度并发系统中是可能的。

相反,单例策略管理一个线程安全的会话(具有同步访问)。这在高度并发的环境(如 Web)中可能导致性能问题,但也会允许所有 jBPM 共享功能(所有流程之间共享事实和全局变量,整个工作内存中的信号作用域等)。这些都是对问题的一般见解,您应根据系统和功能需求评估自己的策略的优缺点。

RuntimeEnvironment 类

此类封装了RuntimeManager所需的环境配置;我们通过使用RuntimeEnvironmentBuilder辅助类来实例化它:

// preconfigured environment with disabled persistence
RuntimeEnvironmentBuilder builder=RuntimeEnvironmentBuilder.Factory.get()
      .newDefaultInMemoryBuilder();
// with enabled persistence: emf is your EntityManagerFactory
RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder().entityMan agerFactory(emf).persistence(true);

RuntimeEnvironmentBuilderFactory有多个辅助方法,旨在创建预配置的专用RuntimeEnvironmentBuilder,使其能够持久化,基于类路径 KIE 容器(kmodule.xml),基于 KIE 模块(JAR 文件)等。由于构建器封装了所有配置细节,它公开了执行以下操作的方法:

  • 添加资产(BPMN 流程、Drools 规则等)

  • 设置自定义UsergroupCallback

  • 设置知识库(如果您的RuntimeEnvironmentBuilder不是 KIE 模块类路径构建器)

  • 设置实体管理器工厂以启用 JPA 持久性

    RuntimeManagerFactory managerFactory = RuntimeManagerFactory.Factory.get();
    
    // pass the RuntimeEnvironment we get from the EnvironmentBuilder
    RuntimeManager rtManager=managerFactory.newSingletonRuntimeManager (builder.get());
    

运行时管理器通过唯一标识符识别。运行时不会接受创建具有与另一个活动RuntimeManager相同 id 的RuntimeManager。以下图表显示了在运行时初始化期间发生的交互,以及涉及主要 KIE 组件的交互:

RuntimeEnvironment 类

运行时上下文

RuntimeManager可以处理上下文信息以查找特定的RuntimeEngine实现,具体取决于所选策略;上下文作为org.kie.api.runtime.manager.Context通用接口实现的传递:

  • EmptyContext:与 Singleton 或 PerRequest RuntimeManager 一起使用的上下文;不使用特定信息

  • CorrelationKeyContext:与 PerProcessInstance RuntimeManager 一起使用,通过使用流程实例关联键来查找RuntimeEngine

  • ProcessInstanceIdContext:与 PerProcessInstance RuntimeManager 一起使用,通过使用流程实例 ID 来查找RuntimeEngine(以及 Kie 会话)

    RuntimeEngine engine = rtManager.getRuntimeEngine(EmptyContext.get());
    // we can now get the initialized services
    KieSession ksession = engine.getKieSession();
    TaskService taskService = engine.getTaskService();
    

为了避免资源泄漏,强烈建议在工作会话结束时销毁RuntimeManager,例如:

rtManager.close();

KieSession

KieSession 是一个有状态的会话,它在与引擎的多个交互过程中维护其会话状态。这是与引擎交互的最佳方式。会话是从 KieContainer、KieBase 或配置的运行时引擎创建的,它始终委托给 KieBase,但提供了选择会话运行时策略的灵活性。

根据持久性的启用情况,会话按以下方式创建:

  • 内存会话: 与会话和引擎状态相关的所有数据都保存在内存中,并在引擎重启时丢失

  • JTA 会话: 通过 JPA EntityManager 和 JTA 事务管理器持久化会话

要创建一个新的(有状态的)KieSession,我们配置环境并使用 JPAKnowledgeService

Environment env = KnowledgeBaseFactory.newEnvironment();
EntityManagerFactory emf= Persistence.createEntityManagerFactory( "com.packt.masterjbpm6.persistenceunit" ));
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY,emf);
env.set( EnvironmentName.TRANSACTION_MANAGER,
bitronix.tm.TransactionManagerServices.getTransactionManager());

StatefulKnowledgeSession ksession = JPAKnowledgeService.newKieSession( kbase, null, env );

示例使用 Bitronix 事务管理器BTM)(更多关于此内容在 持久性和事务 部分)。

返回的 StatefulKnowledgeSessionCommandBaseStatefulKnowledgeSession 类型,它使用 SingleSessionCommandService 类型的命令服务装饰会话实现(StatefulKnowldgeSessionImpl)(参见以下类图)。

SingleSessionCommandService 通过 TransactionInterceptor 类透明地管理通过 JPA SessionInfo 实体类的有状态会话的持久性。

无状态 KieSession

无状态 KIE 会话是有状态会话的包装器,运行时在单个命令执行期间创建和销毁它,以便它不维护会话状态并且不能持久化。

全局变量

KieSession 管理全局变量;全局变量是用于将信息传递到引擎的对象,可以在流程或规则中使用。全局变量在所有流程和规则实例之间共享。让我们看看 KieSession 方法可以如何处理它们:

  • getGlobals(): 返回内部的全局变量解析器

  • getGlobal (String): 根据其标识符返回全局对象

  • setGlobal(String, Object): 设置全局对象并为其分配一个标识符

以下类图显示了会话类的详细信息:

全局变量

命令执行器接口

所有 KIE 会话(无状态和有状态)都实现了 CommandExecutor 接口,这是一个服务,允许您执行单个命令或命令批处理。让我们看看其中的一些:

  • 进程/任务: CompleteWorkItemCommand, AbortWorkItemCommand, StartProcessCommand 等等

  • Drools 规则: FireAllRulesCommand, GetFactHandleCommand 等等

  • 运行时: GetGlobalCommand, SetGlobalCommand 等等

通常,你会调用高级 jBPM API 方法(使用会话或任务服务引用),但有时,使用命令类进行批量、调度或管理操作可能很方便。所有命令类都支持 XML 绑定,归功于标准注解(@XmlRootElement@XmlAttribute@XmlElement等),因此可以轻松序列化或远程传递。可以使用CommandFactory创建命令,或者简单地实例化特定的命令类,然后通过调用会话的execute方法执行它。

让我们现在看看如何从以下代码中创建和执行这些命令:

// create the command and execute
Command startProcess= CommandFactory.newStartProcess ("pizzadelivery");
ksession.execute(startProcess);

或者,你也可以自己实例化该命令:

GetProcessInstanceCommand getProcessInstanceCommand = new GetProcessInstanceCommand();
getProcessInstanceCommand.setProcessInstanceId( processInstance.getId());
ProcessInstance processInstance = ksession.execute( getProcessInstanceCommand );

批量执行

会话支持通过专门的BatchExecutionCommand执行一批命令。在这里,我们通过使用三个不同的命令(参见CommandsTaskTest.testRuleWithCommand方法)重写了第五章的RuleTaskTest测试类,BPMN 结构

StringBuffer orderdesc = new StringBuffer();
List<Command> batchcmds = new ArrayList<Command>();
batchcmds.add(CommandFactory.newSetGlobal("newnote", orderdesc));
Order order = new Order();
order.setCost(200);
batchcmds.add(CommandFactory.newInsert(order));
batchcmds.add(CommandFactory.newSetGlobal("orderglobal", order));
batchcmds.add(CommandFactory.newStartProcess("rule"));
ExecutionResults results = ksession.execute(CommandFactory .newBatchExecution(batchcmds));

BatchExecutionCommand按照它们被添加的确切顺序执行列出的命令:

List<Command> pizzabatchcmds = new ArrayList<Command>();
pizzabatchcmds.add(CommandFactory.newStartProcess("pizzadelivery"));
pizzabatchcmds.add(CommandFactory.newStartProcess("pizzadelivery"));
ksession.execute(CommandFactory.newBatchExecution(pizzabatchcmds));

注意,BatchExecutionCommand,像所有命令类一样,支持序列化,因此你可以远程执行它或轻松地将其持久化以进行计划处理。

小贴士

即使CompositeCommand也可以顺序执行多个命令,但它只支持人类任务命令(继承自TaskCommand)。此命令由任务服务内部使用(参见人类任务服务部分)。

事件监听器

KIE 会话可以注册多个针对不同类型事件通知的专用监听器:

  • 流程ProcessEventListener与流程实例执行相关(我们在KieLoggers部分看到了ProcessEventListener)。

  • 规则RuleRuntimeEventListener用于与事实相关的事件。

  • 议程AgendaEventListener用于与 Drools 议程相关的事件。议程是 Drools 组件,用于跟踪规则激活(规则匹配)和规则动作执行。

通过提供这些接口之一的自定义实现,你可以获取过滤后的会话事件。

注意

KieLoggers服务获取的所有日志类都扩展了抽象的WorkingMemoryLogger类,该类实现了前面的三个接口。我们将在审计和日志历史记录部分看到更多关于事件和审计的内容。

我们可以在kmodule.xml文件中声明性注册监听器:

<ksession name="ksession">
<listeners>
<ruleRuntimeEventListener type="com.packt.masterjbpm6.RuntimeEventlistener" />
<agendaEventListener type="com.packt.masterjbpm6.AgendaEventlistener" />
<processEventListener type="com.packt.masterjbpm6.ProcessEventlistener" />
</listeners>
</ksession>

通道

通道是可以在你的 jBPM 应用程序和引擎之间建立的通信管道,通过你的 KIE 工作会话。它们的主要目的是允许将对象从会话工作内存发送到通用的外部过程或函数。

让我们看看基础知识:你必须实现org.kie.api.runtime.Channel接口,并将其与会话注册,为通道分配一个名称:

public class RulesAppChannel implements Channel {
    // handle the channel object sent
public void send(Object object) {

    }
  }
ksession.registerChannel("appChannel", new RulesAppChannel());

该通道可以用来通知 jBPM 应用程序,例如,规则执行如下:

when
        $o: com.packt.masterjbpm6.pizza.model.Order (cost>100)
    then
    {

      channels["appChannel"].send("urgent!");
    }

可以通过调用会话来检索现有通道的列表:

  Map <String, Channel> channels=ksession.getChannels();

检查ChannelsTaskTest.testRuleWithChannel()方法以获取一个工作示例。

人类任务服务

我们在上一章介绍了人类任务;我们描述了人类任务状态转换、任务规则分配以及利益相关者执行的任务操作。

您可以从RuntimeEnginegetTaskService()方法)获取org.kie.api.task.TaskService任务服务,这是一个服务包装器;任务服务由RuntimeManager创建并包装到RuntimeEngine(及其底层会话)中,它使用TakServiceFactory

InternalTaskService internalTaskService = (InternalTaskService) taskServiceFactory.newTaskService();

用于实例化TaskService的工厂如下:

  • LocalTaskServiceFactory:用于非 CDI 环境

工厂使用以下引用配置任务服务:

  • EntityManagerFactory实例(从RuntimeEnvironment获取)。

  • UserGroupCallback(从RuntimeEnvironment获取)。如果没有提供自定义的UserGroupCallback实现,则使用默认的MvelUserGroupCallbackImpl;它从jbpm-human-task-core-6.2.0.Final.jarorg\jbpm\services\task\identity包)加载UserGroupsAssignmentsOne.mvel文件。

  • 注册任务事件监听器(TaskLifeCycleEventListener接口的实例)。

  • TaskDeadlinesService:此服务负责管理为任务定义的截止日期以及触发通知(我们在第五章中讨论了升级和通知,BPMN 构造)。

工厂还在环境级别上共享以下内容:

  • 默认的DefaultUserInfo实例(如果存在,则从类路径中的userinfo.properties文件加载数据)

任务服务通过命令来执行所有常见的任务操作;命令通过CommandService执行。让我们来看看这个服务类及其内部工作细节。

命令服务

运行时管理器创建了两种不同的TaskService类型:

  • CommandBasedTaskService:默认的任务服务实现

  • SynchronizedTaskService:一个同步的任务服务实例,它包装了CommandBasedTaskService。它使用单例运行时策略创建

CommandBaseTaskService将 API 执行委托给其内部的CommandService执行器。这是命令执行器,它执行所有任务 API 调用,就像在 KIE 会话中一样(例如,请参阅专门的命令执行器接口部分):

public void start(long taskId, String userId) {
  executor.execute(new CompositeCommand<Void>(
    new StartTaskCommand(taskId, userId),
    new CancelDeadlineCommand(taskId, true, false)));
}

例如,启动(任务)方法作为由两个任务命令特殊化构建的CompositeCommand执行:

  • StartTaskCommand:通过改变状态、在监听器上触发事件等方式执行任务启动操作

  • CancelDeadlineCommand: 取消此任务启动事件的匹配截止日期(如果有)(参见 第五章 中的 截止日期和升级通知 部分)

CompositeCommand 首先执行可变参数 commands 中的命令,然后执行 mainCommand 命令。其构造函数签名如下:

public CompositeCommand(TaskCommand<T> mainCommand, TaskCommand<?>...commands) { 

}

TaskCommand 和 UserGroupCallback

所有任务命令都继承自 UserGroupCallbackTaskCommand 类;它们在 execute 方法上调用特定的父类方法,以触发对 ORGANIZATIONALENTITY 数据库表的更新。

TaskContext 命令

在实例化时,每个任务命令都从 CommandExecutor 获得了 TaskContext;上下文职责如下:

  • 提供一个 JPATaskPersistenceContext 实例,该实例处理所有数据库相关操作

  • 触发任务生命周期相关事件

  • 为命令提供核心任务相关服务

提供的主要任务服务如下:

  • 实例服务 (TaskInstanceService): 核心服务,它根据任务生命周期实现了 WS Human Task 规范

  • 查询服务 (TaskQueryService): 它返回只读任务实例信息,例如分配给用户的任务列表、给定任务的潜在所有者等

  • 内容服务 (TaskContentService): 它管理任务内容数据(输入和输出)

  • 截止日期服务 (TaskDeadlineService): 它管理任务截止日期的调度

  • 附件服务 (TaskAttachmentService): 它处理任务附件管理

  • 管理服务 (TaskAdminService): 它提供标准任务生命周期操作之外的操作,例如任务删除和任务存档

任务可以被删除(实际上是从 jBPM 持久存储中删除,使其对用户分配不可用),但只有在它们被标记为“存档”之后。

事务和拦截器

TaskService CommandService 由 TaskCommandExecutorImpl 类实现,在初始化期间,它被 org.jbpm.services.task.persistence.TaskTransactionInterceptor 拦截器装饰。拦截器将命令服务的每个 execute 方法调用封装在由 Drools JtaTransactionManager 管理的事务边界之间。

通知服务

在 第五章 中,我们讨论了人类任务升级和通知功能。jBPM 通知服务依赖于电子邮件消息;因此,为了成功工作,它需要以下条件:

  • 配置正确的邮件会话

  • 定义了一个 UserInfo 类实例;这提供了用户的电子邮件地址以供通知

jBPM 通过 Java 命名和目录接口 (JNDI) 查找来加载邮件会话;您可以通过 org.kie.mail.session 系统属性设置 JNDI 名称,或者,作为替代,在您的应用程序服务器中提供 JNDI 名称 mail/jbpmMailSession

如果没有找到 JNDI 资源,jBPM 将回退到从类路径加载的普通 email.properties 文件。

该文件定义了以下属性(提供了示例值):

mail.smtp.host=localhost
mail.smtp.port=2345
mail.from=alerts@packt.com
mail.replyto=replyTo@packt.com

我们在第五章,BPMN 构造中介绍了 UserInfo 类。

TaskFluent 类

TaskFluent 类是一个辅助类,它允许您方便地配置和创建一个新的 Task 实例,并通过任务服务将其添加到持久存储(参见 FluentTest 测试类):

TaskFluent fluent = new TaskFluent();
fluent.setName("adhoc Human Task");
fluent.addPotentialUser("Luigi").setAdminUser("Administrator");
Task task = fluent.getTask();
// add the task
long taskid = taskService.addTask(task, new HashMap<String, Object>());

提示

WS-HumanTask 规范定义了管理员的角色,即可以管理任务的生命周期,即使他可能没有被列为任务潜在的所有者。在 jBPM 6 之前的版本中,jBPM 默认创建了一个“管理员”用户。

addTask 操作要求您至少添加一个潜在的业务管理员(通过 setAdminUser()setAdminGroup() 方法)。业务管理员实体(用户或组)由运行时通过当前的 UserGroupCallback 进行验证,以检查其是否存在。要检查业务管理员是否是任务潜在的所有者,请搜索 PEOPLEASSIGNMENTS_BAS jBPM 数据库表。

为了简化测试配置,我们所有的 jUnit 测试类都配置了自定义用户回调,这使得所有用户都能通过(参见 MyUserCallback 类),因此“管理员”、“老板”或任何其他名称都没有区别。

运行时将评估任务分配及其截止日期,并将存储任务数据;引擎将任务分配给初始状态 CREATED

FluentTest 示例展示了如何在流程定义范围之外(临时任务)以编程方式创建、添加、分配、启动和完成新任务。

TaskFluent 允许您将一个新任务附加到给定的流程实例。由于新任务没有传入/传出连接,这可能用途有限,但它非常适合临时流程(参见第五章,BPMN 构造),例如:

  ftask.setWorkItemId("default-singleton");
  ftask.setProcessId(PROCESS_ID);
  ftask.setProcessInstanceId(processinstance.getId());
  ftask.setProcessSessionId(ksession.getId());

审计和历史日志

审计是引擎让用户配置与运行时流程执行相关的事件收集和检索的能力。我们在第四章,运营管理中介绍了审计和 BAM,现在,我们将看看如何利用引擎服务在各个级别实现审计。

当启用持久性时,可以从 RuntimeEngine 中借用现成的预配置 AuditService(由 JPAAuditLogService 实现),它返回来自三个标准 jBPM 审计数据库表(ProcessInstanceLogNodeInstanceLogVariableInstanceLog)的流程、节点和变量运行时审计数据(参见类图)。审计服务在每个日志条目上使用 OwnerID 属性进行标记,该属性与其 RuntimeManager 的唯一标识符相匹配。

AuditLogService auditservice=engine.getAuditLogService();
List<ProcessInstanceLog> pizzadeliveryLogs= auditservice.findActiveProcessInstances("pizzadelivery");

这种 AuditService 的使用方法(基本上作为一个查询服务)是一个适合 BAM 或历史分析的好解决方案;然而,如果我们想实时收集审计数据,我们必须在 KIE 会话中注册一个监听器(参见事件监听器部分或下一部分)。

AuditLoggerFactory

此工厂可以创建可用的 JPA-或 JMS 启用审计日志器(参见以下类图):

  • JPA: 一个同步日志器,默认情况下在引擎 JTA 事务边界之间持久化审计事件

  • JMS: 面向 JMS 队列集成的异步日志器

记住,日志器必须绑定到 KieSession 才能开始记录:

  ksession.addEventLister(listener);

我们可以通过向直接实例化的(无工厂)JPAAuditLogService提供自定义的EntityManagerFactory,并可选地选择一个审计策略,来在 JPA 日志器上设置不同的EntityManager

EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.packt.masterjbpm6.persist enceunitApp");
AuditLogService logService = new JPAAuditLogService(emf, PersistenceStrategyType.STANDALONE_LOCAL);

这使我们能够将引擎日志服务适配到我们的持久层配置和事务管理:本地实体管理器和 JTA。此策略的主要目的是指导 jBPM 在不同应用程序环境中进行审计时管理事务。

KIE 审计策略如下:

  • KIE_SESSION: 当你仅使用 KieSession 的实体管理器时,请选择此策略(默认行为)

  • STANDALONE_LOCAL: 当你的应用程序在 KieSession 作用域之外使用实体管理器时,请选择此策略

  • STANDALONE_JTA: 与前一个策略相同,但使用java:comp/UserTransactionjava:jboss/UserTransaction来解析事务(仅适用于应用服务器)

我们在以下类图中展示了相关的类:

AuditLoggerFactory

自定义日志器

要实现审计事件的自定义日志记录,你必须扩展AbstractAuditLogger类或为ProcessEventListener接口提供一个新实现。

支持的审计事件如下:

Process:
   BEFORE_START_EVENT_TYPE = 0;
   AFTER_START_EVENT_TYPE = 1;
   BEFORE_COMPLETE_EVENT_TYPE = 2;
   AFTER_COMPLETE_EVENT_TYPE = 3;
Nodes:
   BEFORE_NODE_ENTER_EVENT_TYPE = 4;
   AFTER_NODE_ENTER_EVENT_TYPE = 5;
   BEFORE_NODE_LEFT_EVENT_TYPE = 6;
   AFTER_NODE_LEFT_EVENT_TYPE = 7;
Variables:
   BEFORE_VAR_CHANGE_EVENT_TYPE = 8;
   AFTER_VAR_CHANGE_EVENT_TYPE = 9;

事件以以下ProcessEvent实现类形式传递:ProcessNodeEventProcessStartedEventProcessCompletedEventProcessVariableChangedEvent。为了将传入的已类型化事件转换为日志 JPA 实体类(即VariableInstanceLog),你可以使用一个辅助类(DefaultAuditEventBuilderImpl):

AuditEventBuilder builder = new DefaultAuditEventBuilderImpl();
VariableInstanceLog variablelog = (VariableInstanceLog) builder.buildEvent(event);
// get process variable properties: processId, var. name and value
String processId= variablelog.getProcessId();
String variableId=variablelog.getVariableId();
String value= variablelog.getValue();
// persist
em.persist (variablelog);

持久性和事务

jBPM 引擎的默认持久化机制基于 JPA 2/Hibernate 实现。每个引擎操作(启动流程、启动任务、完成任务等)都在事务的作用域内运行。TransactionInterceptor标记每个命令执行,并最终根据所使用的事务管理(容器管理事务CMT)或用户事务Bean 管理事务BMT)),将 EntityManager 引擎注册到当前事务中。我们已经通过CommandService和拦截器架构看到了会话和任务持久化的工作方式。

默认的引擎持久化配置归结为引擎持久化单元(在 persistence.xml 文件配置中定义)以及通常在应用服务器级别的 JTA 数据源定义。jBPM 不对定义的实体管理器数量施加任何限制;你显然可以在你的应用程序中定义多个持久化单元,并使多个实体管理器与 jBPM 实体管理器共存。你可以为 jBPM 和你的应用程序持久化层使用单个数据库(单个实体管理器),或者使用专门的 jBPM 数据库(和实体管理器),并使引擎参与你的业务应用程序事务。

注意

第八章, 与企业架构集成 jBPM,的 jBPM 6.2 用户指南详细解释了事务的配置和使用。

使用 Bitronix 进行本地事务

我们将看到一个使用本地事务的完整的 jBPM 持久化示例配置。Bitronix 是一个开源的事务管理器框架;它支持 JTA API 和扩展架构XA)协议,非常适合需要直接持久化配置的所有情况。所需的配置步骤如下:

  1. 创建数据源(池化):数据源将被绑定到 jdbc/localjbpm-ds JNDI 名称如下:

    PoolingDataSource pds = new PoolingDataSource();
    pds.setUniqueName("jdbc/localjbpm-ds");
    pds.setClassName(LrcXADataSource.class.getName());
    pds.setMaxPoolSize(5);
    pds.setAllowLocalTransactions(true);
    pds.getDriverProperties().put("user","sa");
    pds.getDriverProperties().put("password","");
    pds.getDriverProperties().put("url","jdbc:h2:tcp://localhost /~/jbpm-db;MVCC=TRUE");
    pds.getDriverProperties().put("driverClassName","org.h2.Driv er");
    pds.init();
    
  2. 在你的类路径资源中创建 jndi.properties 文件,其中包含以下代码:

    java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory
    

    这使得 Bitronix 上下文工厂初始化环境并将事务服务对象绑定到默认 JNDI 名称,特别是以下名称:

    • 用户事务管理器位于 java:comp/UserTransaction

    • 事务同步注册表位于 java:comp/TransactionSynchronizationRegistry

  3. 编辑你的 persistence.xml 文件,指定 Bitronix 数据源名称如下:

    <persistence-unit name="localjbpm-persistenceunit"
        transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <!--  match the bitronix datasource uniqueName -->
    <jta-data-source>jdbc/localjbpm-ds</jta-data-source>
    
  4. 现在,你可以按照以下方式创建你的 EntityManagerFactory

    EntityManagerFactory emf =Persistence.createEntityManagerFactory("localjbpm- persistenceunit");
    

管理事务

jBPM 为企业的应用程序提供了一个现成的交易服务,它能够从调用应用程序开始参与现有的事务,因此,在发生错误的情况下(例如,自定义工作项处理器抛出异常或流程节点脚本失败),引擎事务将被标记为回滚,并将异常发送给调用者。

让我们看看一个常见的例子,实体管理器EM)和 jBPM(由 Bitronix 管理)是如何一起工作的(请参阅 AuditTxTest 测试类):

AuditEntity audit = new AuditEntity();
audit.setDesc("startAudit1");
UserTransaction ut;
try {
  ut = (UserTransaction) new InitialContext()
      .lookup("java:comp/UserTransaction");
  ut.begin();
  em.joinTransaction();
  em.persist(audit);
// start process
  ProcessInstance pi = ksession.startProcess("auditTxProcess");
// new application database insert
  AuditEntity auditproc = new AuditEntity();
  auditproc.setDesc("Audit1:process started");
  em.persist(auditproc);
// commit both process instance and audit entity
  ut.commit();

锁定

默认使用的 JPA 事务锁定方案是乐观的。如果你需要切换到悲观锁定模式,请将你的 org.kie.api.runtime.Environment 实例中的以下参数设置为 TRUE

EnvironmentName.USE_PESSIMISTIC_LOCKING

这迫使引擎在实体上保持锁(锁定类型为 LockModeType.PESSIMISTIC_FORCE_INCREMENT),以确保对象没有被修改。

锁定

摘要

在本章中,我们对核心引擎组件和服务进行了广泛的探索,并未忽视一些引擎内部实现细节。现在您应该能够理解引擎是如何工作的,以及当您使用特定引擎功能时“幕后”发生了什么。

下一章将介绍引擎定制和扩展过程,以便将 jBPM 系统功能调整到您的解决方案。

第七章。定制和扩展 jBPM

本章详细介绍了 jBPM 的扩展点。并非每个 jBPM 用户都使用整个工具栈。用户将需要定制/扩展 jBPM 以适应其解决方案架构。本章将向您展示如何定制和扩展 jBPM 的功能。

本章将涵盖的主题列表如下:

  • 领域特定流程

  • 编写您的自定义工作项处理程序

  • 定制流程设计器

  • 扩展变量持久性

  • 扩展用户管理

自定义流程节点

在第五章中,我们介绍了 jBPM 的一个功能,该功能允许您将特定的 Java 类实现绑定到特定流程任务节点类型的执行:发送/接收任务、服务任务和临时任务。

这类可扩展的任务节点通常被称为自定义工作项,在流程幕后执行实际工作的实现类被称为工作项处理程序。这种架构使得 jBPM 在适应特定领域时更加灵活,无论是从功能还是工具 UI 方面。让我们首先回顾 jBPM 工作项和处理程序的基本知识。

工作项和处理程序

jBPM 将工作项定义为在流程范围内定义但可以在引擎外部执行的工作单元;特别是:

  • 它接受一组参数

  • 它执行某些操作

  • 它可以可选地返回一个结果

工作项只是工作单元的抽象定义,在 jBPM 中有几种具体的、实用的实现,例如:人工任务、sendMessage 任务等。除了强制实现org.kie.api.runtime.process.WorkItemHandler接口外,引擎对工作项处理程序没有限制。

引擎运行时通过WorkItemManager.registerWorkItemHandler(String workItemName, WorkItemHandler handler)方法指示绑定新的处理程序实现,其中workItemName参数必须匹配自定义节点名称,因为它作为处理程序键。

jBPM 本身广泛使用处理程序,如LocalHTWorkItemHandler(工作项名称Human Task)、WebServiceWorkItemHandler(工作项名称WebService)或RESTWorkItemHandler(名称Rest)。此功能有效地简化了引擎定制过程,使用户能够增强(或替换)jBPM 功能。您可以在jpbm-workitems-6.2.0.Final.jar库中找到几个 jBPM 工作项处理程序类(有关类图中的包详细信息,请参阅以下内容)。

工作项和处理程序

AsyncWorkItemHandler(我们在第五章的异步任务部分讨论过)可以在jpbm-executor-6.2.0.Final.jar库中找到。

生命周期

工作项状态转换如下:ACTIVEPENDINGCOMPLETEDABORTED

WorkItemHandler 调用序列相当简单(见以下交互图),当处理程序调用完成或中止时,引擎再次接管控制,流程执行继续。处理程序必须实现两个方法:

  • executeWorkItem: 工作项管理器调用executeMethod处理程序,并在executeMethod结束时,处理程序必须调用管理器上的回调方法completeWorkItem(可选地传递输出参数):生命周期

  • abortWorkItem: 此方法在取消或错误事件发生后被调用。处理程序必须执行清理操作(如有必要)并通过abortWorkItem方法调用管理器,该方法指示管理器将工作项设置为已中止状态:生命周期

可缓存和可关闭接口

jBPM 6.2 引入了一个新功能,允许用户通过实现以下接口方法钩入工作项处理程序的生命周期:

  • org.kie.internal.runtime.Closeable.close(): 在 WorkItemManager(和会话)释放时调用。在这里,您可以执行典型的维护工作(释放资源、关闭连接等)。

  • org.kie.internal.runtime.Cacheable.close(): 当 jBPM 内部缓存管理器关闭/释放时调用。通过实现Cacheable接口,我们使我们的 Workitem 处理程序定义能够被 jBPM 缓存。

小贴士

RuntimeManager 内部缓存了多个配置的对象类定义以优化初始化和启动时间:事件监听器、全局变量、序列化策略、Workitem 处理程序等。

对于Closeable接口的示例,请参阅以下段落中讨论的 PizzaTweet 处理程序实现。

处理程序在行动

工作项定制可以看作是一个两步过程:

  1. 代码实现和运行时注册:使处理程序实现可供运行时引擎使用,以便在引擎达到自定义节点类型时触发处理程序执行

  2. 设计器定制:启用从 UI 界面(流程设计器)使用自定义节点的功能

在深入一个详细示例(pizzatweet项目)之前,让我们看看工作项架构的基本知识,并回顾我们可以注册处理程序的三种不同方式:通过直接使用 API 注册、通过设置kmodule.xml文件以及通过添加处理程序配置文件。

直接处理程序注册

将处理程序实现注册到您的引擎会话中最简单、最直接的方法是直接调用 Kie 会话的 WorkItemManager:

// register MyWorkItemHandler for all ad hoc(None) task
ksession.getWorkItemManager().registerWorkItemHandler("task", new MyWorkItemHandler());

这为你提供了很大的灵活性;你不需要定义额外的配置文件或属性(更多内容将在下一节中介绍),并且你可以在执行过程中自由地初始化你的处理程序所需的一切。这是单元测试时的首选方式,尤其是在替换或定义系统工作项处理程序(human taskservicetask)时,因为你不需要调整 Kie 控制台以将新节点类型添加到 服务任务 菜单,这对于正确设计流程定义是强制性的。

声明性 kmodule.xml 处理程序配置

如果你的项目是 Kie 模块,并且你需要一种声明性、非硬编码的方式来定义处理程序,可以将 <workItemHandlers> 元素添加到 kmodule.xml 文件中,如下所示:

<kbase name="kbase" >
<ksession name="ksession">
<workItemHandlers>
  <workItemHandler name="pizzatweet" type="com.packt.masterjbpm6.pizzahandlers.PizzaTweetHandler"
</workItemHandlers>

</ksession>

处理程序配置文件

当你需要添加新的自定义节点类型时,将你的处理程序实现列出在标准处理程序配置文件中是首选和标准的方式:CustomWorkItemHandlers.conf 文件。

此文件必须包含处理程序实现类的构造函数以及将用于注册的工作项名称;以下是一个示例,即 jBPM 6.2 伴随的默认配置文件:

[
  "Log": new org.jbpm.process.instance.impl.demo.SystemOutWorkItemHandler(),
  "WebService": new org.jbpm.process.workitem.webservice.WebServiceWorkItemHandler(kse ssion),
  "Rest": new org.jbpm.process.workitem.rest.RESTWorkItemHandler(),
  "Service Task" : new org.jbpm.process.workitem.bpmn2.ServiceTaskHandler(ksession)
]

此文件使用 MVEL 表达式语言编写,并由 jBPM 控制台运行时从 jbpm-console.war\WEB-INF\classes\META-INF 文件夹加载;请注意,文件名被添加到名为 drools.session.conf 的同级文件中,其内容如下:

drools.workItemHandlers = CustomWorkItemHandlers.conf

小贴士

注意,通过定义一个接受 ksession 参数的构造函数,从系统默认处理程序定义(Web ServiceService Task)中,KieSession 将在运行时自动注入到你的处理程序实例中。

同样的属性 drools.workItemHandlers 用于加载处理程序配置文件,例如,在 KieSessionConfiguration 初始化期间,如下所示:

// create the session configuration
Properties props = new Properties();
props.setProperty("drools.workItemHandlers", "MyWorkItemHandlers.conf");
KieSessionConfigurationconfig = KieServices.Factory.get().newKieSessionConfiguration (props);
   // create the session
KieSessionksession = kbase.newKieSession(config, EnvironmentFactory.newEnvironment());

或者,使用运行时构建类(有关运行时类的详细信息,请参阅第六章 [Chapter 6. Core Architecture],核心架构),你可以有如下所示:

RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder()
   .addConfiguration("drools.workItemHandlers", "MyWorkItemHandlers.conf");

.conf 文件在 META-INF/ 类路径或 user.home 系统文件夹中搜索。

小贴士

该属性支持多个用空格分隔的条目,如下所示:

addConfiguration("drools.workItemHandlers", "MyWorkItemHandlers.conf OtherWorkItemHandlers.conf");

处理程序定义文件

定义 WorkItemHandler 流程定义节点属性的文件是工作项定义文件(具有 .WID 扩展名),它使用 MVEL 表达式语言编写。

当处于 项目创作 模式时,KIE 控制台会在你的项目 resources 文件夹中创建一个默认的 WorkDefinitions.wid 文件,该文件定义了 emaillogwebservicerest 处理程序,其自定义节点可以在流程设计器调色板的服务任务菜单下找到。额外的资源(自定义节点图标)在 global 文件夹中创建。

在这里,您定义自定义节点属性和工作项抽象定义:name(用作处理程序的关键字),其参数(输入),结果参数(输出),displayName(用于节点的标签),以及节点图标资源。让我们看看我们的PizzaTweet自定义节点(我们将在示例部分讨论这个话题):

[
    [
    "name" : "pizzatweet",
    "parameters" : [
  "tweetMsg": new StringDataType (),
  "tweetTags" : new ListDataType (),
  "tweetOrder" : new ObjectDataType
("com.packt.masterjbpm6.pizza.model.Order")
    ],
    "results" : [
       "details" : new ObjectDataType("java.util.Map"),
   "tweetOK": new BooleanDataType()
    ],
    "displayName" : "Pizza Tweet",
    "icon" : "../../../global/defaultservicenodeicon.png"
 ]

"参数""结果"属性是类型化参数(名称-类型对)的映射。

  • parameters:定义工作项的输入参数集

  • results:定义输出参数集

允许参数的类型是 Drool 核心类型类所支持的类型:StringDataTypeIntegerDataTypeFloatDataTypeBooleanDataTypeEnumDataTypeObjectDataTypeListDataTypeUndefinedDataTypeObjectDataType封装一个类型,而ListDataType封装java.util.List

工作项处理程序实现

您的处理程序必须实现org.kie.api.runtime.process.WorkItemHandler接口或扩展AbstractWorkItemHandler抽象类。这个类在您的处理程序需要从流程或其节点获取运行时信息时提供了一组有用的辅助方法。

处理输入参数

处理程序可以使用WorkItem.getParameter(String name)方法读取输入参数。输入参数由引擎运行时在评估您的节点数据输入集映射时传递。

将结果返回给流程

处理程序返回对象("results"”)定义为参数集合。每个属性名称必须与DataOutputSet`节点设置中的数据输出参数匹配(例如:"details"和"tweetOK"),例如:

Map<String, Object> operationresults = new HashMap<String, Object>();
  operationresults.put("twitterCode", "200");
  results.put("details", operationresults);
  results.put("tweetOK", Boolean.TRUE);

detailstweetOk键必须与节点数据输出参数名称(参见流程和任务参数映射部分的截图)以及工作项处理程序的"results"属性相匹配:

    "results" : [
      "details" : new ObjectDataType("java.util.Map"),
      "tweetOK": new BooleanDataType()
    ]

PizzaTweet 示例

示例引导您通过定义和安装一个完整的可工作示例项目,该项目包含一个自定义的工作项,该工作项向 Twitter 发送消息(为了清晰,未实现 Twitter API 集成)。

注意

您可以在 Red Hat jBPM 服务仓库中找到一个 Twitter 处理程序实现:people.redhat.com/kverlaen/repository/Twitter/

主要项目(pizzatweet KIE 模块)包含流程定义和 WID 文件,并且它依赖于在pizzamodel项目中定义的自定义类型(一个普通的实用 JAR)。这些类型也被PizzaTweetHandler处理程序(在pizzahandlers项目中定义,另一个普通 JAR)用作参数类型。

主要项目(KIE 模块)

主要项目依赖于两个供应商项目:pizzamodelpizzahandlers。由于这两个项目不是 KIE 模块,并且不需要 KIE 运行时进行额外处理(它们不包含 jBPM 资源),我们已经将它们的 Maven 依赖范围设置为provided(参见pom.xml PizzaTweet 项目文件)。这加快了我们的主要 KIE 模块的 Maven 构建;实际上,kie-mave-plugin仅在它们的 Maven 范围是runtimecompile时搜索 KIE 模块依赖项。

Maven 的 KIE 插件(kie-maven-plugin)是构建 KIE 模块的首选方式。它确保所有模块业务资源在编译时有效,并且模块可以在运行时成功加载。请确保您始终在 KIE 模块的pom.xml文件中设置了kie-maven-plugin(参见以下PizzaTweet pom.xml摘录):

<dependency>
  <groupId>com.packt.masterjbpm6</groupId>
  <artifactId>pizzamodel</artifactId>
  <version>1.0.0-SNAPSHOT</version>
 <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.packt.masterjbpm6</groupId>
  <artifactId>pizzahandlers</artifactId>
  <version>1.0.0-SNAPSHOT</version>
 <scope>provided</scope>
</dependency>

<build>
  <plugins>
 <plugin>
 <groupId>org.kie</groupId>
 <artifactId>kie-maven-plugin</artifactId>
 <version>6.2.0.Final</version>
 <extensions>true</extensions>
 </plugin>
  </plugins>
</build>

流程和任务参数映射

在上一节(处理器定义文件)介绍了pizzatweet自定义任务定义之后,现在让我们看看它如何与流程定义相匹配。流程定义很容易理解;它包含自定义推文任务和一个作为调试步骤的脚本任务。流程变量(msgordertags)映射到自定义任务输入参数,而结果参数(tweetOKdetails)映射回流程变量(successresults)。以下截图显示了Pizza Tweet自定义节点(参见PizzaTweet流程定义)的分配属性面板:

流程和任务参数映射

推文任务完成后,执行脚本任务。如前所述,它只是将更新的流程变量输出到控制台供您查看。现在我们将查看两个依赖项目(处理器和模型)。

处理器项目(pizzahandlers)

此项目仅包含处理器实现(PizzaTweetHandler类),负责发送推文。为了部署依赖的pizzahandlers处理器项目,我们必须执行 Maven 的“clean build install”。然后,JAR 文件将被安装到您的系统 Maven 仓库中。

小贴士

确保所有实现类和所需依赖项也存在于应用程序 war(在这种情况下,war 是我们的 KIE 控制台 war)的类路径上,例如,通过将所需的 JAR 文件复制到/lib文件夹中。

模型项目(pizzamodel)

模型项目定义了流程定义变量和处理项目参数的 Java 类型。为了部署依赖的pizzamodel项目,我们必须执行 Maven 的“clean build install”。因此,JAR 文件被安装到您的系统 Maven 仓库中,以便在运行时依赖项解析时可用。

IDE 自定义

为了配置 KIE 工作台(业务流程编辑器)工具并能够在流程编辑器中使用我们的自定义节点,我们必须创建一个工作项处理程序文件。我们在 WEB-INF\classes\META-INF\PACKTworkItemHandlers.conf 文件(在 jbpm-console deployment 文件夹中)中添加以下内容:

[
   "pizzatweet": new com.packt.masterjbpm6.pizzahandlers.PizzaTweetHandler(ksession)
]

然后,我们通过将我们的自定义处理程序 .conf 文件名添加到 drools.workItemHandlers 属性中,编辑 WEB-INF\classes\META-INF\drools.session.conf 文件。这样,两个文件中的处理程序定义都被加载。请注意,处理程序配置文件名必须由空格分隔:

drools.workItemHandlers = CustomWorkItemHandlers.conf PACKTworkItemHandlers.conf

小贴士

drools.session.conf 文件在 KIE 控制台初始化期间被选中并读取;有关更多详细信息,请参阅 处理程序配置文件 部分。

将安装的 pizzahandlers-1.0.0-SNAPSHOT.jarpizzamodel-1.0.0-SNAPSHOT.jar 文件复制到 jBPM 控制台的 WEB-INF\lib 文件夹中(例如,wildfly-8.1.0.Final\standalone\deployments\jbpm-console.war\WEB-INF\lib)。这使得自定义 Java 类型和处理程序类对 Kie 控制台可用(需要重启 jBoss)。请注意,Pizza Tweet(名称:pizzatweet)自定义任务节点现在显示在对象库的 服务任务 部分中:

IDE 自定义

控制台测试运行

截至 jBPM 6.2.0 版本发布,KIE 控制台在让我们测试我们的流程方面帮助不大,因为生成的任务表单不支持自动处理复杂类型参数(我们的流程接受 Order 类型的输入参数);我们无法轻松从这里创建新的流程实例。

注意

jBPM 用户指南(第十三章,表单)解释了与 KIE 控制台表单模型器一起提供的功能(docs.jboss.org/jbpm/v6.2/userguide/chap-formmodeler.html),并提供了有关如何创建自定义人类任务表单和启动流程表单的有用说明。

然而,在离开控制台之前,让我们检查流程是否可以成功部署而不会出现任何问题。从 工具/项目详情 视图中转到 pizzatweet 项目,并执行 构建 & 部署。流程定义已注册到运行时,我们应该能在 流程管理/流程定义 选项卡中看到它。

控制台测试运行

独立测试运行

PizzaTwitter 项目获取 PizzaTweetTest 测试类并运行(JUnit)的 newTweet 方法:

// boilerplate code omitted for clarity;
// register the handler
session.getWorkItemManager().registerWorkItemHandler("pizzatweet", newPizzaTweetHandler(session));
// init parameters

// start the process
ProcessInstanceinstance = session.startProcess("pizzatweet.tweet", params);

控制台打印以下文本;首先,我们有以下处理程序日志跟踪:

PizzaTweetHandler.executeWorkItem
PizzaTweetHandler.order=order: note=urgent cost=15.0

然后,我们有脚本任务日志跟踪,显示以下处理程序结果:

tweet success:true
twitterCode:200

在处理程序本地测试完成后,我们可以继续前进并与开发团队共享;这就是服务仓库发挥作用的地方。

服务仓库

jBPM 赋予我们向公共服务仓库添加任何处理器的功能;这些是一组处理器定义,可以通过 HTTP 或本地(文件协议)访问,以便与其他开发者共享处理器。

在撰写本书时,KIE 工作台支持两个仓库:people.redhat.com/kverlaen/repositorypeople.redhat.com/tsurdilo/repository。另一个仓库服务可在 docs.jboss.org/jbpm/v6.2/repository/ 找到。这些仓库托管了多个处理器定义;其中一些是外部定义的处理器(这意味着实现 JAR 文件物理上托管在仓库中),而其他处理器已在 jBPM 运行时中定义(例如,Java、REST 和转换处理器),仓库仅发布扩展处理器定义(.WID)文件。服务仓库可通过业务流程编辑器中的连接到服务仓库按钮访问。在这里,您可以看到一个示例仓库内容对话框窗口:

服务仓库

我们将看到如何设置一个额外的自定义本地服务仓库。

注意

关于服务仓库的更多详细信息,请参阅 jBPM 6.2 用户指南的第二十一章“特定领域流程”(服务仓库部分)。

自定义服务仓库

服务仓库基本上是一个包含处理器的文件夹结构。文件夹结构和要加载的内容由一组index.conf文件指定。请参阅书中源代码中包含的repo.rar示例。

在本地文件夹(例如,c:/temp/packtservicerepo)中为我们的仓库创建一个文件夹结构,包含pizzatweet处理器文件夹;在处理器文件夹内,我们添加增强的pizzatweet.wid文件,这基本上是一个标准的 WID 文件,具有以下附加条目:

"defaultHandler" : "com.packt.masterjbpm6.pizzahandlers.PizzaTweetHandler",
"documentation" : "index.html",
"category" : "examples",
"dependencies" : [
      "file:./lib/pizzahandlers-1.0.0-SNAPSHOT.jar",
      "file:./lib/pizzamodel-1.0.0-SNAPSHOT.jar"
    ]

依赖路径相对于处理器文件夹(/lib),在那里我们复制两个 JAR 文件:包含处理器定义的 JAR 文件和定义 Java 模型的 JAR 文件。

注意

请参考PizzaTweet示例段落中关于披萨处理器和模型项目的详细信息。

值得注意的是,WID 文件必须具有处理器文件夹名称。在创建文件后,我们可以从 KIE 工作台打开服务仓库,给出以下本地文件系统路径:file:///c:/temp/packtservicerepo

自定义服务仓库

现在,我们可以使用安装选定项 :。这会使 KIE 工作台将资产复制到内部 KIE 仓库,从而使处理器可用。

jBPM 身份管理

在第四章中,运营管理,我们看到了 KIE 工作台如何通过user.propertiesroles.properties文件使用基于 JAAS 的用户身份验证和 RBAC 来为 UI 功能提供功能。

jBPM 引擎在创建流程或任务操作时没有内置的身份验证或细粒度授权功能。TaskService 以及用户和组在任务操作方面的任务管理委托给UserGroupCallback接口的定制实现。在这里,开发者能够通过挂钩到自定义的身份管理系统或临时实现来实现自己的任务授权机制。

jBPM 提供了一套现成的、可配置的UserGroupCallback实现:

  • DBUserGroupCallbackImpl:使用 SQL 查询从数据库获取用户和组数据的实现

  • LDAPUserGroupCallbackImpl:LDAP 系统集成

  • MvelUserGroupCallbackImpl:当未指定回调时,默认的 jBPM 实现;读取并评估UserGroupsAssignmentsOne.mvel文件

  • JAASUserGroupCallbackImpl:用于容器的基于 JAAS 的实现:jBPM 身份管理

用户回调配置

UserGroupCallback实现是一个单例,可以设置在创建RuntimeEngine时使用的环境中:

// create the environment builder
RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder()
.userGroupCallback(usergroupcallback);
// create the manager
RuntimeManagermanager = RuntimeManagerFactory.Factory.get()
.newSingletonRuntimeManager(builder.get(), "manager");
// create the runtimeEngine (omitted)

注意

本书的所有源代码示例都使用默认的定制回调类(MyUserCallback:您可以在test-common项目中找到它)。回调由PacktJUnitBaseTestCase类设置,每个测试用例都从该类借用运行时引擎、会话等。

jBPM UserGroupCallback 实现依赖于jbpm.usergroup.callback.properties系统属性用于属性文件名,或者,作为替代,依赖于jbpm.usergroup.callback.properties属性文件进行自动配置。回调类可以定义一组属性;让我们回顾一下 jBPM 附带的一些类。

DBUserGroupCallbackImpl 类

此回调具有以下四个属性(假设usersgroups是定义我们的用户和组数据的表):

  • db.ds.jndi.name:要使用的数据源 JNDI 名称,例如,jdbc/jbpm-ds

  • db.user.query:用于验证用户存在的查询(区分大小写,期望在位置 1 上有一个参数),例如:

    "select userId from users where userId = ?"
    
  • db.roles.query:查询以检查组的存在(区分大小写,期望在位置 1 上有一个参数),例如:

    select groupId from Groups where groupId = ?"
    
  • db.user.roles.query:用于获取给定用户的组(区分大小写,期望在位置 1 上有一个参数),例如:

    select groupId from Groups where userId = ?
    

LDAPUserGroupCallbackImpl 类

此回调依赖于几个属性(参数描述):

  • ldap.bind.user(如果 LDAP 服务器接受匿名访问则为可选)

  • ldap.bind.pwd(如果 LDAP 服务器接受匿名访问则为可选)

  • ldap.user.ctx (必需),例如,ou=Staff,dc=packt,dc=com

  • ldap.role.ctx (必需),例如,ou=Roles,dc=packt,dc=com

  • ldap.user.roles.ctx (可选;如果没有提供,将使用 ldap.role.ctx)

  • ldap.user.filter (必需),例如,=(uid={0})

  • ldap.role.filter (必需),例如,(cn={0})

  • ldap.user.roles.filter (必需),例如,(member={0})

  • ldap.user.attr.id (可选;如果没有提供,将使用 uid)

  • ldap.roles.attr.id (可选;如果没有提供,将使用 cn)

  • ldap.user.id.dn (可选;用户 ID 是否为 DN?;指示回调在搜索角色之前查询用户 DN;默认为 false)

  • ldap.search.scope (可选;如果没有提供,将使用 OBJECT_SCOPE); 可能的值如下:OBJECT_SCOPEONELEVEL_SCOPESUBTREE_SCOPE

  • java.naming.factory.initial

  • java.naming.security.authentication

  • java.naming.security.protocol

  • java.naming.provider.url,例如,ldap://localhost:10389

jBPM 序列化

我们已经看到,在启用持久化的情况下,引擎具有以下功能:将会话、流程、任务和变量数据保存到数据库,并且上下文中相关的对象状态数据在实体保存时进行序列化,然后在实体加载时反序列化,以便在长期内、跨系统重启的情况下保存引擎执行状态。在这里,术语 序列化 是因为 jBPM 序列化层使用了 Google Protobuf 框架,该框架最初用于 RPC。让我们看看默认的 jBPM 序列化是如何工作的,以及我们如何能够将我们的序列化机制钩入 jBPM。

序列化

CommandService (第六章, 核心架构) 和相关的拦截器被调用以以事务方式持久化实体;内部对象序列化阶段发生在事务内部。

在会话的保存(或更新)过程中,例如,其实例被序列化到其 RULEBYTESARRAY 列(SessionInfo 表)中,以便在重启后可以加载 Knowledge Session 实例。对于流程实例也是如此;其实例(包括变量、节点定义、泳道等)被序列化到 PROCESSINSTANCEBYTEARRAYProcessInstanceInfo 表)。任务数据被序列化到 WORKITEMBYTEARRAYWorkItemInfo 表)。

实际执行序列化任务的引擎类分别是SessionMarshallingHelperProtobufProcessMarshaller;这些类由ProtobufMarshaller类内部封装,该类通过写入处理程序(ProtobufOutputMarshaller)和输入处理程序(ProtobufInputMarshaller)管理操作。稍后显示的类图展示了这些类以及KieMarshallers(我们在第六章中介绍了它,核心架构)如何融入画面。它只是一个默认的 marshaller 和策略实例的工厂。策略是控制变量序列化过程的类。

变量持久化

jBPM 没有提供现成的流程和任务变量持久化到 ER 模型的功能,这主要是出于性能原因。主要缺点是您不能通过流程变量的值来搜索流程实例。为了添加此类功能,您必须为JPAPlaceholderResolverStrategy策略提供实现(我们将在稍后讨论它)。

策略

正如我们刚才看到的,默认的 jBPM 序列化过程将结果写入数据库的字节。这可能对我们应用程序的兴趣有限,但幸运的是,jBPM 提供了工具,通过控制变量(至少)序列化的方式来挂钩此机制,通过使用或添加所谓的策略。

在序列化过程中,实际上,jBPM 将流程和任务变量的序列化委托给策略类;jBPM 附带了一些现成的策略:

  • org.drools.core.marshalling.impl.SerializablePlaceholderResolverStrategy:在实现Serializable接口的对象上提供默认的 Java 序列化。jBPM 默认将此策略添加到启用策略列表中。

  • org.drools.persistence.jpa.marshaller.JPAPlaceholderResolverStrategy:一种将变量作为实体在 JPA 持久化存储之间进行管理的策略。

  • org.jbpm.document.marshalling.DocumentMarshallingStrategy:此策略管理org.jbpm.document.Document类型参数的序列化。文档参数类型在 KIE 表单模型器中用作上传文件参数。这些功能在jbpm-document-6-2-0.Final.jar中可用。

jBPM 同时支持多个策略;它按照配置会话时给出的顺序(责任链模式)依次调用它们(更多内容将在下一节中介绍)。每个策略(ObjectMarshallingStrategy)必须指定它处理的对象(accept方法)并提供marshalunmarshal方法(请参阅前面的类图)。

现在让我们通过一个使用 jBPM JPAPlaceholderResolverStrategy 的实际示例来查看如何配置策略,以便将我们的流程和任务变量持久化到我们的领域数据库表中。请参阅 jbpm-marshalling 示例项目以获取一个有效的 marshalling 示例。

配置 marshalling 策略

jbpm-marshalling 示例包含一个流程定义(rule_marshall.bpmn),它使用实体类作为流程变量和任务参数。我们希望引擎透明地将我们的领域变量(OrderEntity 类)持久化到一个新的领域数据库表(ORDERENTITY 表)中。必须将 OrderEntity 实体类添加到我们的持久化单元中(检查 persistence.xml 项目),例如:

<class>com.packt.masterjbpm6.pizza.model.entity.OrderEntity</class >

我们通过向用于创建 KieSession 的环境中传递一个 ObjectMarshallingStrategy 数组来设置 marshalling 策略;在下面的示例(MarshallTest 示例类)中,我们配置了 JPAPlaceholderResolverStrategySerializablePlaceholderResolverStrategy 策略(有关详细信息,请参阅第六章中的RuntimeManager 和引擎部分,核心架构)。

RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder();
builder.entityManagerFactory(super.getEmf())
builder.addEnvironmentEntry(
  EnvironmentName.OBJECT_MARSHALLING_STRATEGIES,
  new ObjectMarshallingStrategy[] {
    new CustomJPAPlaceholderResolverStrategy (super.getEmf()),
    new SerializablePlaceholderResolverStrategy(
    ClassObjectMarshallingStrategyAcceptor.DEFAULT)});

注意,我们提供了一个扩展的 CustomJPAPlaceholderResolverStrategy;这个类扩展并委托所有功能到默认的 JPAPlaceholderResolverStrategy,并在 marshalling 过程中通过向控制台发送相关信息添加了一些日志功能。其构造函数接受 EntityManagerFactory,它是由与引擎使用相同的持久化单元创建的。这意味着我们的实体表将位于同一个引擎数据库模式中。ClassObjectMarshallingStrategyAcceptor 实例(用于 SerializablePlaceholderResolverStrategy)执行接受(过滤)对象实例的逻辑。请记住,始终将 SerializablePlaceholderResolverStrategy 策略作为最后一个策略添加,因为它是由引擎使用的。

配置 marshalling 策略

持久化变量

MarshallTest 类继承自 RuleTaskTest(请参阅第五章中的规则开始事件部分,BPMN 构造);它设置一个全局会话变量,然后创建一个流程,传递两个参数,即一个普通的 Order 实例和一个 OrderEntity 实例,这些实例随后被传递给用户任务。在用户任务完成之后,业务规则被触发,并评估全局会话变量。

如果我们运行示例 jUnit 测试,我们可以看到在触发人工任务之前,我们的策略类的 marshall 方法被调用多次:

18:19:42.757 [main] accepted com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: null desc= First order amount=20.0]
18:19:42.757 [main] marshal com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: null desc= First order amount=20.0]
18:19:42.788 [main] accepted com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:19:42.788 [main] marshal com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:19:44.318 [main] accepted com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:19:44.318 [main] marshal com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:19:44.350 [main] accepted com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:19:44.350 [main] marshal com.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]

订单实体首先被插入,然后更新了多次;在数据库表中,我们可以看到我们的记录。

持久化变量

维护实体状态在这些调用之间的一致性是策略的责任。在任务完成后,unmarshall 方法被调用两次:第一次是在工作项在完成前从数据库中加载时,第二次是在从会话中加载流程实例时:

luigi is executing task User Task 1
18:27:00.220 [main] unmarshalcom.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]
18:27:00.251 [main] unmarshalcom.packt.masterjbpm6.pizza.model.entity.OrderEntity object: [OrderEntity Id: 1 desc= First order amount=20.0]

摘要

jBPM 是一款开放且可配置的软件。在本章中,我们回顾了平台的核心特性,这些特性在定制 jBPM 系统以满足特定应用需求时通常会被扩展:领域流程和自定义 BPMN 节点、流程和任务变量的自定义持久化,以及基于自定义实现或遗留系统的人事授权。下一章将为用户提供实际的 jBPM 解决方案。

第八章:将 jBPM 与企业架构集成

我们已经建立了企业基础设施,现在我们希望将流程管理分离并集中到单个组件中,当然,我们的选择是 jBPM。所以,那个价值百万的问题将是“我们如何将 jBPM 集成到现有的企业应用程序中?”

这个问题的答案因需求和企业应用程序的构建方式而异。架构描述了应用程序是如何构建的,从更广泛的角度来看,使用一系列架构模式(单独或组合使用)作为指导原则来建模架构。本章重点介绍 jBPM 为与遵循这些架构模式的应用程序集成提供的功能。

本章首先讨论企业应用程序集成的背景,然后详细讨论以下内容:

  • 将 jBPM 集成到基于 JEE 的应用程序中

  • 将 jBPM 集成到面向服务的架构中

  • 将 jBPM 集成到事件驱动架构中

设置上下文

软件组件与现有软件架构的系统集成表示我们应该提供两个窗口(接口),如下所示:

  • 为了访问新组件提供的服务。在 jBPM 的情况下,它由 jBPM 提供的各种服务表示,例如,用于管理业务流程生命周期的流程运行时提供。JBPM 将这些服务作为其核心引擎的 API 公开。

  • 为了使 jBPM 能够访问应用程序架构中其他组件提供的服务。JBPM 为与外部组件集成提供的扩展点是工作项处理器。我们可以创建处理器并编写访问外部组件的逻辑。

下图展示了此背景:

设置上下文

jBPM 提供的服务

正如我们在上一节中讨论的,与 jBPM 系统集成的一个关键部分是访问 jBPM 特性的能力。JBPM 提供了一个应用程序编程接口来访问这些特性。此 API 可以在同一个 JVM 中直接调用,如果需要从系统边界之外访问,则必须将其包装并提供为远程可访问的服务。为此,我们有各种选择,从 企业 JavaBeansEJB)远程接口到基于 REST 的 Web 服务。这些将在本章后续部分中详细介绍。

以下是由 jBPM 提供的服务:

  • 定义服务:这有助于定义流程并分析其内容

  • 部署服务:这有助于部署业务流程及其相关工件

  • 流程服务:这有助于从流程定义中启动流程实例,管理实例的生命周期,并使用信号与它们交互

  • 用户任务服务:这有助于管理人工任务的生命周期

  • 运行时数据服务:这有助于在 jBPM 运行时获取有关流程、流程实例、任务和审计跟踪的数据细节

每个服务在以下部分中详细说明,包括(重要)操作:

  • org.jbpm.services.api.DefinitionService:此服务有助于从 BPMN 文本定义流程并提供分析业务流程定义的操作:

    操作 操作签名 描述
    buildProcessDefinition
    ProcessDefinition buildProcessDefinition(String deploymentId, String bpmn2Content, ClassLoader classLoader, boolean cache) throws IllegalArgumentException;
    
    从给定的流程定义内容(bpmn2Content)构建流程定义
    getReusableSubProcesses
    Collection<String> getReusableSubProcesses(String deploymentId, String processId);
    
    获取流程定义内部可重用子流程的流程标识符
    getProcess Variables
    Map<String, String> getProcessVariables(String deploymentId, String processId);
    
    获取业务流程中所有流程变量的名称和类型
    getServiceTasks
    Map<String, String> getServiceTasks(String deploymentId, String processId);
    
    获取与业务流程定义中关联的所有服务任务标识符
    getTasks Definitions
    Collection<UserTaskDefinition> getTasksDefinitions(String deploymentId, String processId);
    
    获取业务流程中定义的所有任务
  • org.jbpm.services.api.DeploymentService:此服务有助于部署和管理应用程序部署单元:

    操作 操作签名
    deploy
    void deploy(DeploymentUnit unit);
    

    |

    undeploy
    void undeploy(DeploymentUnit unit);
    

    |

    activate
    void activate(String deploymentId)
    

    |

    deactivate
    void deactivate(String deploymentId);
    

    |

    IsDeployed
    boolean isDeployed(String deploymentUnitId)
    

    |

  • org.jbpm.services.api.ProcessService:此流程服务用于管理生命周期并与已启动的流程实例交互:

    操作 操作签名
    startProcess
    Long startProcess(String deploymentId, String processId);
    

    |

    startProcess
    Long startProcess(String deploymentId, String processId, Map<String, Object> params);
    

    |

    abortProcessInstance
    void abortProcessInstance(Long processInstanceId);
    

    |

    abortProcessInstances
    void abortProcessInstances(List<Long> processInstanceIds);
    

    |

    signalProcessInstance
    void signalProcessInstance(Long processInstanceId, String signalName, Object event);
    

    |

    signalProcessInstances
    void signalProcessInstances(List<Long> processInstanceIds, String signalName, Object event);
    

    |

    completeWorkItem
    void completeWorkItem(Long id, Map<String, Object> results);
    

    |

    abortWorkItem
    abortWorkItem(Long id);
    

    |

  • org.jbpm.services.api.UserTaskService:此服务有助于执行用户任务的生命周期管理操作:

    操作 操作签名
    activate
    void activate(Long taskId, String userId)
    

    |

    claim
    void claim(Long taskId, String userId)
    

    |

    Complete
    void complete(Long taskId, String userId, Map<String, Object> params)
    

    |

    Delegate
    void delegate(Long taskId, String userId, String targetUserId)
    

    |

    exit
    void exit(Long taskId, String userId)
    

    |

    fail
    void fail(Long taskId, String userId, Map<String, Object> faultData)
    

    |

    Forward
    void forward(Long taskId, String userId, String targetEntityId)
    

    |

    release
    void release(Long taskId, String userId);
    

    |

    resume
    void resume(Long taskId, String userId);
    

    |

    skip
    void skip(Long taskId, String userId);
    

    |

    start
    void start(Long taskId, String userId);
    

    |

    stop
    void stop(Long taskId, String userId);
    

    |

  • org.jbpm.services.api.RuntimeDataService:此 API 用于检索有关 jBPM 运行时的信息,包括流程实例、任务和审计日志的数据:

    操作 操作签名
    getProcesses
    Collection<ProcessDefinition> getProcesses(QueryContext queryContext);
    

    |

    getProcessInstances
    Collection<ProcessInstanceDesc> getProcessInstances(QueryContext queryContext);
    

    |

    getProcessInstance FullHistory
    Collection<NodeInstanceDesc> getProcessInstanceFullHistory(long processInstanceId, QueryContext queryContext);
    

    |

    getVariableHistory
    Collection<VariableDesc> getVariableHistory(long processInstanceId, String variableId, QueryContext queryContext);
    

    |

    getTaskEvents
    List<TaskEvent> getTaskEvents(long taskId, QueryFilter filter);
    

    |

    getTasksOwned
    List<TaskSummary> getTasksOwned(String userId, QueryFilter filter);
    

    |

创建自定义工作项处理器

为了让 jBPM 访问应用程序中其他组件的服务,我们可以使用 jBPM 提供的工作项处理器扩展点。工作项处理器用于指定特定领域的服务到 BPMN 活动。jBPM 中预建了几个内置的通用工作项处理器。

创建工作项处理器时,我们必须实现 org.kie.runtime.instance.WorkItemHandler 接口。此接口包含两个需要实现的方法:

  • WorkItemManager.completeWorkItem(long workItemId, Map<String, Object> results)

  • WorkItemManager.abortWorkItem(long workItemId)

必须使用工作项管理器将自定义工作项注册到引擎中。例如,为了注册一个客户任务,我们可以使用以下方法:

ksession.getWorkItemManager().registerWorkItemHandler("Notification",
new NotificationWorkItemHandler());

总结来说,我们已经讨论了 jBPM 中可用于将其与通用软件架构集成的配置。在接下来的章节中,我们将讨论如何将 jBPM 集成到广泛使用的企业架构中。

与 JEE 集成

Java 企业版为企业应用程序的开发和部署提供了一个 API 和运行时环境。此外,EJB 定义了一组轻量级 API,可用于构建应用程序并利用事务、远程过程调用、并发控制和访问控制等能力。

EJB 可以通过两种模式访问:

  • 远程接口:这是想要访问 EJB 的组件没有与 jBPM 打包在一起的地方

  • 本地接口:这是想要访问 EJB 的组件与 jBPM 服务打包在一起的地方

JBPM 为 JEE 集成提供了开箱即用的支持。它提供了访问上述列出的服务的 EJB 远程和本地接口。

EJB 远程接口

EJB 远程接口如下:

服务名称 EJB 远程服务类
定义服务
org.jbpm.services.ejb.api.DefinitionServiceEJBRemote

|

部署服务
org.jbpm.services.ejb.api.DeploymentServiceEJBRemote

|

流程服务
org.jbpm.services.ejb.api.ProcessServiceEJBRemote

|

运行时数据服务
org.jbpm.services.ejb.api.RuntimeDataServiceEJBRemote

|

用户任务服务
org.jbpm.services.ejb.api.UserTaskServiceEJBRemote

|

这些远程服务可以从其他 Java 应用程序中访问。首先,我们需要访问ejb远程接口。

例如(特定于jboss应用程序服务器),以下代码显示了ProcessService的查找:

final Hashtable<String, String> jndiProperties = new Hashtable<String, String>();
  //Set the JNDI properties
  jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

  final Context context = new InitialContext(jndiProperties);
  //Set the bean name
  String beanName = "ProcessServiceEJBImpl!org.jbpm.services.ejb.api.ProcessServiceEJB Remote";

  String jndi = "ejb:/" + application + "/" + mappedName;
  ProcessService bean = (ProcessService) context.lookup(jndi);

查找服务后,可以无缝地访问该服务。

EJB 本地接口

EJB 本地接口可以通过两种方式访问。一种是通过使用javax.ejb.EJB注解并指定企业 Bean 的本地业务接口名称:

例如:

@EJB
ProcessService processservice;

容器将为 API 注入 EJB 访问。

访问本地 EJB 服务的另一种语法方式是使用 JNDI 查找和javax.naming.InitialContext接口的lookup方法:

ProcessService processservice = (Processservice)
  InitialContext.lookup("java:jbpm/Processservice");

在 SOA 和 EDA 环境中集成

本节的第一部分介绍了如何将作为客户端的 jBPM 集成到外部服务中;第五章,BPMN 构造,和第六章,核心架构,介绍了专门设计用于从流程定义中调用外部 Web 服务的 jBPM 元素:服务任务和 WS 或 REST 工作项处理器。后者是 jBPM 的现成、可配置组件,但请注意,jBPM 为用户提供所有工具来开发自定义处理器,以便与通用外部服务进行交互(参见第七章,自定义和扩展 jBPM)。本节第二部分将探讨如何使用 REST、SOAP 和 JMS 将 jBPM API 作为服务器进行集成。我们将为您提供两个示例项目(jbpm-remote-clientjbpm-remote-server),以便将这些 jBPM 功能付诸实践。

我们将展示如何连接到 REST 和 SOAP 服务。

集成 REST 服务

在开始对集成 REST 服务的 jBPM 应用程序进行注释的逐步游览之前,让我们回顾一下 jBPM 在 REST 集成方面提供的基本支持。jBPM REST 工作项处理器(类org.jbpm.process.workitem.rest.RESTWorkItemHandler)旨在与 REST 服务(既安全又非安全)进行交互;它支持以下参数:

  • Url:目标资源端点

  • Method:HTTP 方法(默认为GET

  • ContentType:发送数据时的数据类型(与POSTPUT一起使用时必需)

  • Content:要发送的数据(与POSTPUT一起使用时必需)

  • ConnectTimeout:连接超时(默认为 60 秒)

  • ReadTimeout:读取超时(默认为 60 秒)

  • Username:认证用户名

  • Password:认证密码

处理器返回一个输出结果,该结果定义了以下属性:

  • Result:REST 服务文本响应体

  • Status:整数 HTTP 响应代码

  • StatusMsg:操作结果的字符串描述

我们的示例应用程序设置了一个 REST 服务器并启动了一个具有 REST 服务任务节点的流程:REST 节点执行一个 HTTP POST 操作,将Order实例(作为 XML 字符串)传递给 REST 服务器;服务器修改订单的备注并返回订单。

REST 服务

我们的 REST 服务器是在测试类(RestTest.initializeRestServer方法)内部启动的,使用的是 JAX-RS Apache CXF 实现(CXF 版本为 2.7.14,请检查项目pom.xml文件以获取依赖项);初始化代码设置了一个 JAXB 提供程序,以便支持 bean 的数据绑定。

注意

请查阅 Apache CXF 的 JAX-RS 文档,网址为cxf.apache.org/docs/jax-rs.html

服务器是围绕一个 REST 资源(RestResource类)设置的,该资源通过 JAX-RS jax.ws.rs包注解定义了可用的操作。

客户端 – REST 处理器配置

  • 示例测试类方法 RestTest.testRestProcess 启动一个流程实例(参见 rest.bpmn2 流程定义);流程中配置了一个 REST 任务节点,以下为必需的参数:

    • Url: http://localhost:9998/pizzarestservice/order

    • ContentType: application/xml

    • Content: <order><note>my note</note></order>

    • Method: POST

节点处理器执行对 RestResource 类的 postOrder(Order order) 方法的 REST 调用;该方法注解为 @Path("/order"),XML 实体序列化由 JAXB 负责处理,正如我们之前所说的。REST 任务输出变量映射回流程实例,并由脚本任务打印出来。

使用 jUnit 测试类 (TestRest),你可以在流程定义之外练习 REST 处理器和 REST 服务(testPOSTOperation 方法)。

如果默认的 jBPM REST 处理器无法满足你的要求(因为序列化约束、框架锁定等问题),重要的是指出,开发者可以提供一个全新的处理器实现:遵循 第七章,定制和扩展 jBPM 指南描述的工作项处理器开发过程。现在让我们看看如何从流程定义中设置和调用 SOAP Web 服务。

SOAP WebService

jBPM 随带一个专门的 ServiceTaskHandler(参见 第五章,BPMN 构造),它基于 WSDL 特征 Web 服务交互。服务任务被标记为具有 WebService 类型的实现(任务还支持通过 Reflection 类执行纯 Java 实现执行)。请查看 第五章,BPMN 构造服务任务 部分,以获取更多详细信息和工作示例描述。我们的 jUnit 类 (WsTest) 设置一个 Web 服务(startWebService 方法),然后启动一个包含两个服务任务节点的流程:一个调用 Web 服务 addSmallOrder 操作,另一个调用 addLargeOrder 操作:这两个操作都接受一个 Order 实例作为输入,并返回一个布尔结果,该结果由脚本任务打印出来。服务任务位于不同的流程分支上,由评估提交订单的总金额的排他网关执行。

JAX-WS 服务

TestWebService 服务是一个注解的 JAX-WS 服务;它从 WsTest.startWebService 方法类启动,其端点设置为 http://127.0.0.1:9931/testwebservice/order(你可以在单元测试类中轻松配置此设置)。链接 http://127.0.0.1:9931/testwebservice/order?WSDL 返回服务 WSDL 接口。该服务公开了上述两个方法:addSmallOrderaddLargeOrder。让我们看看如何从我们的流程定义中调用我们的 Web 服务操作。

客户端 – 流程和服务任务处理器配置

为了调用 Web 服务操作,我们必须执行以下步骤,通过编辑流程定义及其服务任务节点元素:

流程定义

需要注意的是,我们需要导入服务 WSDL 定义。在流程定义导入部分,添加服务 WSDL 位置和命名空间。WSDL 传递给 Apache CXF JaxWsDynamicClientFactory,它在创建动态 Web 服务客户端时对其进行解析。

流程定义

服务任务处理器

服务任务处理器通过适当地设置其参数自动调用 Web 服务;这加快了集成过程,但在针对具有复杂类型的服务接口进行开发时可能不足,因为我们已经指出,处理器利用了 Apache CXF 动态客户端模式。在这种情况下,强烈建议您开发一个自定义处理器,以集成您选择的 Web 服务框架。我们按以下方式设置处理器参数:

  • 实现WSDL

  • serviceInterfaceTestWebService

  • serviceOperationaddSmallOrder (addLargeOrder)

mode 参数值保留为 SYNC(默认),这表示阻塞操作;当设置为 ASYNC 模式时,处理器被迫在线程上执行 Web 服务调用,并将控制权交还给流程引擎,工作项在远程调用返回后立即完成。

WebServiceWorkItemHandler 类

jBPM 通过 WebServiceWorkItemHandler 类提供了一个以 Web 服务为导向的服务任务处理器替代方案。此处理器在参数数组处理、Web 服务端点设置(它接受 Endpoint 参数)和快捷 WSDL 位置加载(使用 UrlNamespace 参数而不是在流程定义级别定义 WSDL URL)方面优于服务任务处理器。serviceInterfaceserviceOperation 参数分别重命名为 InterfaceOperation

jBPM 作为远程服务

jBPM 平台提供了一系列现成的远程 API,旨在为开发者在设计需要即插即用 jBPM 集成的解决方案时提供更高的灵活性。此远程服务层为提供灵活、开放的架构开辟了许多可能性,以满足并快速响应不断变化的应用程序需求,例如:

  • 许多外部应用程序系统可能需要偶尔连接到 jBPM 运行时,以检查某些任务或检索某些流程信息

  • jBPM 操作管理器可能仅通过提交通过 HTTP 的命令批处理来执行管理任务

jBPM 随带以下远程服务接口:

  • REST API

  • JMS API

  • Java 远程 API:此 API 为开发者提供 KieSessionTaskServiceAuditService 核心引擎服务的本地存根。这些 API 方法的服务存根是针对较低级别的 REST 或 JMS API 调用的包装。

  • SOAP API

所有这些服务都由 jBPM KIE 工作台公开,因此它们仅在 jbpm-console 网络应用程序在容器中部署时才可用。

备注

远程服务项目的源代码托管在 github.com/droolsjbpm/droolsjbpm-integration/tree/master/kie-remote

远程服务客户端所需的 Maven 依赖项如下:

<dependency>
  <groupId>org.kie.remote</groupId>
  <artifactId>kie-remote-client</artifactId>
  <version>6.2.0</version>
</dependency>

现在我们来回顾主要的远程服务功能以及如何访问它们。

REST API

此 API 在以下领域提供功能:

  • 运行时:(/runtime/ 路径)为用户提供流程实例创建、流程实例查询和工作项操作

  • 历史记录:(/history/ 路径)提供审计数据

  • 任务:(/task/ 路径)提供任务操作和任务查询方法

  • 部署:(/deployments//deployment/ 路径)提供部署管理操作

如需更多详细信息,请参阅 jBPM 用户手册参考(第十七章jBPM 流程定义语言 (JPDL))。

认证

在调用时,REST 服务操作会检查当前 HTTP 会话的基本认证用户 ID。例如,假设您正在执行以下命令行代码以在未经授权的会话中执行 REST 操作:

curl -v http://localhost:8080/jbpm-console/rest/deployment/com.packt.masterjbpm6:pizzadelivery:1.0

您将获得以下 HTTP 401 未授权 错误(输出已编辑以清晰;可能有所不同):

< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Basic realm="KIE Workbench Realm"
<html><head><title>Error</title></head><body>Unauthorized</body></html>

备注

Kie 工作台默认的安全机制利用 JAAS;对于 jBoss WildFly 和 EAP,默认配置存储在应用程序服务器的 XML 配置文件中(standalone 等)。有关用户和角色配置,请参阅 第四章,操作管理

否则,设置用户 ID 和密码(工作台领域)如下:

http://admin:admin@localhost:8080/jbpm-console/rest/deployment/com.packt.masterjbpm6:pizzadelivery:1.0

这将返回以下响应:

<deployment-unit>
  <groupId>com.packt.masterjbpm6</groupId>
  <artifactId>pizzadelivery</artifactId>
  <version>1.0</version>
  <kbaseName/>
  <ksessionName/>
  <strategy>SINGLETON</strategy>
  <status>DEPLOYED</status>
</deployment-unit>

备注

要获取完整的 jBPM REST 参考,请参阅 jBPM 官方文档(第十七章远程 API)。

远程 Java API

远程 Java API 是一个高级 API,它使用 REST 或 JMS 与远程引擎服务交互,以便为用户提供熟悉的服务 API 类(TaskServiceKieSession 等)。

依赖项

API 依赖于 jBoss RESTEasy REST 实现和 HornetQ JMS 客户端库。与远程 API 交互所需的 Maven 依赖项是我们之前提到的 kie-remote-client 模块和额外的 kie-remote-common 艺术品。请确保不要有对 Apache CXF 框架的依赖,这可能会与 jBoss RESTEasy 框架引起问题。

REST 客户端

初始化是通过从RemoteRuntimeEngineFactory获得的流畅API 完成的:

// the deploymentId identifies the KIE module
public static String deploymentId = "com.packt.masterjbpm6:pizzadelivery:1.0";
RemoteRestRuntimeEngineBuilder restEngineBuilder = RemoteRuntimeEngineFactory.newRestBuilder()
.addDeploymentId(deploymentId)
.addUrl(instanceurl).addUserName(user)
.addPassword(password);
RemoteRestRuntimeEngineFactory engineFactory = restEngineBuilder
.buildFactory();
// get the engine
RemoteRuntimeEngine engine = engineFactory.newRuntimeEngine();
// and the services
TaskService taskService = engine.getTaskService();
KieSession ksession = engine.getKieSession();
ProcessInstance processInstance = ksession.startProcess(processID);

注意

请参阅jbpm-remote-server Maven 项目及其RestTest jUnit 类以获取完整的工作示例。

jBPM JMS 服务客户端

当使用 JMS 远程 API 客户端时,我们需要添加一系列库依赖项,特别是 HornetQ 和 jBoss 远程客户端。我们将看到如何配置和运行远程客户端应用程序,该应用程序创建一个 jBPM 流程实例。

注意

请参阅jbpm-remote-server Maven 项目及其JmsTest jUnit 类以获取完整的工作示例(需要 WildFly 8.1 运行)。

服务器 JMS 配置

WildFly 自带 HornetQ 作为 JMS 消息队列中间件;为了使 JMS 正常工作,我们需要检查 jBPM JMS 队列是否已注册到 JNDI 服务,并且用户安全设置已设置。默认情况下,HornetQ 将使用“其他”JAAS 安全域,这是 KIE Workbench Realm 用于认证的域(回想一下user.propertiesroles.properties文件)。此外,HornetQ 在 WildFly 的standalone-full.xml配置文件中的以下元素中定义了授权设置(位于消息子系统下):

  <security-settings>
    <security-setting match="#">
      <permission type="send" roles="admin guest"/>
      <permission type="consume" roles="admin guest"/>
      <permission type="createNonDurableQueue" roles="admin guest"/>
      <permission type="deleteNonDurableQueue" roles="admin guest"/>
    </security-setting>
  </security-settings>

在这里,我们只是添加了 KIE 控制台admin角色(以及默认的guest角色);admin角色已经配置了 JAAS。

现在,为了检查我们的 JMS 用户是否配置正确,请打开 jBoss 管理控制台(http://localhost:9990/console)并选择配置/子系统/消息/目的地,然后在顶部导航栏上选择默认提供者安全设置;您应查看定义的用户。

服务器 JMS 配置

WildFly jBPM JMS 队列配置定义在jbpm-console.war\WEB-INF\bpms-jms.xml文件中;远程可访问的队列注册在java:jboss/exported JNDI 命名空间中。

检查 jBPM JMS 队列是否正确绑定到 JNDI,请打开 jBoss 管理控制台(http://localhost:9990/console)并选择运行/状态/子系统/JNDI 视图;在这里,您应查看KIE.AUDITKIE.SESSIONKIE.RESPONSEKIE.TASK队列。在这里,您还应看到列出的RemoteConnectionFactory;此工厂允许远程连接到 jBoss JNDI 命名空间(我们将在稍后看到这一点)。

服务器 JMS 配置

注意

对于 WildFly 消息安全配置,请参阅docs.jboss.org/author/display/WFLY8/Messaging+configuration。有关官方 HornetQ 参考,请参阅最新文档docs.jboss.org/hornetq/2.4.0.Final/docs/user-manual/html

JMS 客户端实现

要设置远程 JMS 客户端连接,我们使用与 REST 客户端相同的方法;我们配置了一个由老牌的RemoteRuntimeEngineFactory提供的专用构建器。

注意

请参阅jbpm-remote-server Maven 项目和它的JmsTest jUnit 类,以获取完整的示例。

// the deploymentId identifies the KIE module
public static String deploymentId = "com.packt.masterjbpm6:pizzadelivery:1.0";
/* the remoteInitialContext is an instance of the jBoss Naming
 service (InitialContext) and gives you access to the container
 remoting services for JMS */
/* the connectionfactory represents the JMS connection configuration settings */

RemoteJmsRuntimeEngineBuilder jmsEngineBuilder = RemoteRuntimeEngineFactory
  .newJmsBuilder().addDeploymentId(deploymentId)
  .addRemoteInitialContext(remoteInitialContext)
  .addUserName(jms_user).addPassword(jms_password)
  .addConnectionFactory(connectionfactory)
  .addTimeout(maxTimeoutSecs);

我们从构建器中获取工厂,从工厂中获取引擎:

RemoteJmsRuntimeEngineFactory engineFactory = jmsEngineBuilder
  .buildFactory();
RuntimeEngine engine = remoteJmsFactory.newRuntimeEngine();

然后,我们从引擎中获取服务类:

TaskService taskService = engine.getTaskService();

为了使 jBPM 远程客户端解析远程 jBPM 队列,我们需要将 jBoss JNDI 提供者 URL 配置如下:

initialProps.setProperty(InitialContext.PROVIDER_URL,
"http-remoting://" + jbossServerHostName + ":8080");

WildFly 使用 HTTP 升级和端口复用功能来支持其几乎所有协议。历史上 jBoss 远程 JNDI 监听在 4447 端口,但现在在 8080 端口。

注意

对于完整的 WildFly 参考,请参阅docs.jboss.org/author/display/WFLY8/Documentation

SOAP API

jBPM 工作台通过暴露一个 SOAP 服务增加了额外的互操作性,该服务由/jbpm-console/CommandService?WSDL端点描述;该服务实现了一个单一的execute操作。在撰写本书时,由于 WSDL 中存在一些错误,因此 jBPM 6.2.0 版本的 WSDL 无法用于生成客户端类。

客户端 Maven 依赖如下:

<dependency>
  <groupId>org.kie.remote.ws</groupId>
  <artifactId>kie-remote-ws-common</artifactId>
  <version>6.2.0.Final</version>
</dependency>

为了完整性,我们现在将描述如何使用其 SOAP API 调用 jBPM。我们的jbpm-remote-server测试项目,SOAPTest jUnit 测试类,创建了一个 Web 服务客户端,然后启动一个新的流程实例。

首先,我们按照以下方式从端点 URL 获取 WSDL 资源:

URL commandWsdlUrl = new URL(
"http://localhost:8080/jbpm-console/CommandService?WSDL");

execute命令操作接受JaxbCommandsRequest命令,这是一个用于普通 jBPM 命令类的 DTO(可序列化)包装器(参见第六章,核心架构)。所有 jBPM 命令类也是 JAXB 注解类。

StartProcessCommand startProcessCommand = new StartProcessCommand();
startProcessCommand.setProcessId(processID);
JaxbCommandsRequest request = new JaxbCommandsRequest(deploymentId, startProcessCommand);

JaxbCommandsRequest也可以接受一批命令,与 REST 或 JMS 远程 API 不同。

事务

当通过 REST、SOAP 或远程 Java API 调用 jBPM 时,你控制着事务管理。如果 jBPM 调用应该作为事务的一部分,并且这个调用失败或抛出异常,你必须处理它并执行回滚操作或补偿你那边的业务逻辑。

所有的远程 API 方法都会抛出RemoteApiException异常,以指示远程调用(无论是 REST 还是 JMS)已失败。

SOAP API 的execute操作会抛出CommandWebServiceException异常。如果你需要一个紧密集成的和事务传播机制,你应该考虑迁移到一个包装了完整 jBPM 服务的 EJB 层(参见本章开头的与 JEE 集成部分)。

概述

在本章中,我们扩展了针对企业架构集成的 jBPM 特性。我们讨论了 jBPM 暴露的核心服务以及如何使用不同的技术,如 JEE、SOAP、REST 和 JMS 来访问这些服务。

在下一章中,我们将关注在将 jBPM 部署到生产环境中需要特别注意的细节。

第九章。生产中的 jBPM

在前面的章节中,我们探讨了 jBPM 的各种功能方面,也看到了我们如何扩展和定制 jBPM 以添加更多功能。现在,是生产时间了,应用被其利益相关者看待的角度发生了变化。

现在的重要问题不是功能特性,而是非功能特性。人们考虑的是应用程序的稳定性和弹性,而不是它提供的灵活性。人们思考的是应用程序如何快速且成本效益地进行扩展,以便为更多用户提供服务,以及服务的延迟如何不那么关键。

应用程序的韧性受到考验。jBPM 是现成的生产软件,在本章中,我们将讨论 jBPM 中可用的各种设施,使其符合生产软件的要求。本章的结构基于在生产中必须注意的系统的主要品质。

我们将讨论以下主题:

  • 如何进行扩展

  • 如何使应用程序安全

  • 如何满足可用性要求

  • 如何将新的更改纳入系统

  • 系统如何处理运行时错误

可扩展性

可扩展性可以描述为系统以受控和成本效益的方式处理不断增长的服务提供量的能力。在 BPM 系统中,有两个主要的使用场景可能会引发扩展的需求。

  • 扩展建模设施,即工作台

  • 调整流程运行时间,这是应用程序的最终用户与之交互的部分

扩展应用程序通常涉及两种方法:

  • 垂直扩展:这是通过向提供服务的服务器添加资源来实现的

  • 水平扩展:这是通过添加多个服务器来提供相同的服务来实现的

垂直扩展在实现上涉及较少的复杂性,因为它要求提高硬件(通常是)并配置应用程序以使用这些资源。然而,垂直扩展通常受到在构建资源时成本和技术限制的限制。在 jBPM 的背景下,可以添加的资源包括内存、处理器核心和辅助存储机制。jBPM 不提供现成的功能来明确地适应这些资源改进,但通过利用 jBPM 使用的底层平台,如 jBPM 部署的应用服务器和运行应用服务器的 JVM,可以带来吞吐量和性能的提升。

在本书的范围内,显然横向扩展需要更好的方法,接下来的几节将纯粹专注于 jBPM 功能的横向扩展。

扩展业务流程建模设施

扩展建模工具意味着增加可以同时执行建模的用户数量。用户可以选择用于建模的 Web 工具或 Eclipse 工具,并且可能存在建模用户创建单个应用程序或多个应用程序的场景。

考虑到先前的因素和约束,提高建模工作台服务吞吐量的最明显方法就是增加提供服务的单元数量。因此,我们增加了服务器,并跳转到面对集群的典型问题。每个服务器都有一个独立的资产库,如果用户协作创建相同的应用程序,我们需要确保库中的资产始终同步。

jBPM 作为资产库提供的开箱即用功能是基于 Git 的虚拟文件系统VFS),为了保持文件系统同步,jBPM 建议使用 Apache Helix,一个集群管理框架。

以下图表展示了此场景中的部署架构:

扩展业务流程建模设施

Apache Helix 充当集群管理解决方案,将所有服务器注册到集群中并启用仓库的同步。

Helix 内部使用 Apache ZooKeeper 来管理系统状态并管理节点之间的通知。

VFS 集群配置的详细信息已在 jBPM 用户指南中明确提供;请参阅它以获取配置详情。

Apache Helix 提供了一套功能,使我们能够开发一个容错性强、可扩展的分布式系统。更多详情请参阅helix.apache.org/Architecture.html

扩展流程运行时

当我们谈论扩展任何软件应用时,它涉及到增加系统容量以服务越来越多的用户交互。在基于 BPM 的应用中,随着用户交互的增加,业务流程的复杂性和内容也会增加,从而增加了系统的容量。

因素和考虑事项

以下部分强调了确定系统部署架构所涉及的因素,并讨论了 jBPM 架构在满足这些日益增长需求时所做的考虑。

进程数量/进程实例

是的,这是一个明显的因素:作为应用程序一部分的流程定义数量以及从这些流程定义创建的流程实例数量都会使用系统容量。流程定义存储在资产库中,我们已经在扩展业务流程建模设施部分讨论了这一点,但流程定义的增加会导致系统需要管理的流程实例数量增加。反之亦然,即相对较少的流程定义但大量的流程实例。

流程实例携带流程执行的状态,默认情况下在内存中。然而,在运行时状态可用性至关重要的实际场景中,这并不是一个选项,因此 jBPM 提供了将流程实例持久化到数据库的机制。在我们讨论的上下文中,我们必须注意,随着流程实例数量的增加,我们必须做以下事情:

  • 增加数据库的容量

  • 增加内存的容量

以下图显示了示意图部署架构,其中包含多个具有复制 VFS 存储库用于资产存储的 jBPM 运行时实例以及一个集中式数据库用于存储运行时信息:

流程/流程实例数量

用户交互的数量

用户与流程运行时的交互包括以下内容:

  • 与流程引擎的交互以启动流程(创建流程实例)或向已启动的流程实例发送信号

  • 与任务服务交互以处理人工任务的生命周期方法

注意

另一种可能性是与特定领域的异步工作项的交互,这需要自己的容量规划。

通过专用的 KieSession API 与流程引擎交互,并带有特定的约束;即只能通过创建流程实例的 Kie 会话与流程实例进行交互。每次交互都需要创建它的 Kie 会话实例,jBPM 提供了多种策略来处理流程交互的扩展。jBPM 允许你在创建运行时管理器时选择策略,该管理器随后用于访问运行时引擎并创建用于交互的会话。

运行时管理器的种类

  • 单例策略

    通过选择此策略,我们选择维护单个运行时实例和单个 Kie 会话以处理所有交互。这是最简单的策略,并且对于低负载和中负载最有利。

    单例策略可以通过以下方式编程选择:

    RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(runtimeEnvironment);
    

    此外,如果您使用 jBPM 控制台(KIE 工作台),您可以配置<runtime-strategy>部署描述符标签为SINGLETON。以下是一个示例部署描述符:

    <deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" >
      <persistence-unit>org.jbpm.domain</persistence-unit>
      <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit>
      <audit-mode>JPA</audit-mode>
      <persistence-mode>JPA</persistence-mode>
      <runtime-strategy>SINGLETON</runtime-strategy>
      <marshalling-strategies/>
      <event-listeners/>
      <task-event-listeners/>
      <globals/>
      <work-item-handlers/>
      <environment-entries/>
      <configurations/>
      <required-roles/>
    </deployment-descriptor>
    

    SINGLETON是 jBPM 控制台中的默认策略。

    注意

    我们可以通过在服务器启动时使用 Java 选项来覆盖默认的部署描述符,如下所示:Dorg.kie.deployment.desc.location=file:/application/configuration/deployment-descriptor.xml

  • 按请求策略

    提供了一个新的运行时管理器实例,并为请求范围创建了会话并维护。

    这种策略是无状态的,非常适合水平扩展 jBPM 流程运行时实例,但流程中的功能仅限于无状态事实,不允许除了启动流程之外的用户交互。

    我们可以在创建运行时期间通过以下代码程序化地选择按请求策略:

    RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(runtimeEnvironment);
    

    此外,对于 jBPM 控制台,部署描述符可以定制如下:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" >
      <runtime-strategy>PER_REQUEST</runtime-strategy>
    </deployment-descriptor>
    
  • 按流程实例策略

    这是考虑到系统可扩展性和开销之间的权衡而采取的最先进策略。正如其名所示,ksession 会粘附在流程实例上,只要流程实例存在,ksession 就会存在。它没有 singleton 策略中的可扩展性约束,尽管它有较高的开销,但它没有开销限制,并且不像按请求策略那样可扩展。因此,按流程实例策略位于上述两种策略的中间,并且在大多数使用 jBPM 的情况下使用。

    我们可以在创建运行时期间通过以下代码行程序化地选择按流程实例策略:

    RuntimeManagerFactory.Factory.get().newPerProcessInstanceRuntimeManager(runtimeEnvironment);
    

    此外,对于 jBPM 控制台,部署描述符被定制如下:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" >
      <runtime-strategy>PER_PROCESS_INSTANCE</runtime-strategy>
    </deployment-descriptor>
    

任务服务

任务服务是管理人工任务服务的专用组件。可以通过人工任务与应用程序进行交互。jBPM 提供了一个基于 WS-Human Task 规范的默认人工任务服务实现。任务服务客户端轻量级,与我们所选择的运行时管理器策略相匹配,并且所有客户端共享相同的数据库;因此,人工任务的扩展与为运行时管理器选择的策略以及数据库存储容量的增加保持同步。

计时器事件数量

计时器事件的功能是通过调度服务实现的。jBPM 提供了多个调度服务的实现。基于 Quartz 调度器的实现适合生产环境。Quartz 调度器支持集群模式,提供高可用性和可扩展性,它通过在节点之间的共享数据库中维护它所处理调度(或作业)的数据或状态来实现。

注意

Quartz 是一个开源的作业调度库,可以集成到 Java 应用程序中。Quartz 可以用于创建计划任务,并提供对 JTA 事务和集群的支持。有关更多详细信息,请参阅 quartz-scheduler.org/

可以通过提供 quartz 定义文件的绝对路径来启用 Quartz 调度程序,相对于 org.quartz.properties 系统属性。

以下提供了一个示例 quartz 定义文件,该文件配置用于与 PostgreSQL 数据库一起使用。

#============================================================================
# Configure Main Scheduler Properties
#============================================================================

org.quartz.scheduler.instanceName = jBPMClusteredScheduler
org.quartz.scheduler.instanceId = AUTO

#============================================================================
# Configure ThreadPool
#============================================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 5

#============================================================================
# Configure JobStore
#============================================================================

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.dataSource=managedDS
org.quartz.jobStore.nonManagedTXDataSource=notManagedDS
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval = 20000
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.managedDS.jndiURL=jboss/datasources/psjbpmDS
org.quartz.dataSource.notManagedDS.jndiURL=jboss/datasources/quartzNotManagedDS

注意

当使用 Quartz 调度程序时,作为先决条件,我们必须创建 Quartz 用于持久化其作业数据的数据库模式。与 Quartz 分发版一起提供的数据库脚本(jBPM 使用 Quartz 1.8.5。DB 脚本)通常位于 QUARTZ_HOME/docs/dbTables

可以通过在运行环境中对 GlobalSchedulerService 进行配置来程序化地配置调度程序服务:

RuntimeEnvironmentBuilder.Factory.get()
  .newDefaultBuilder().entityManagerFactory(emf)
  .knowledgeBase(kbase).schedulerService(globalSchedulerService);

在这里,globalSchedulerService 对象是 org.jbpm.process.core.timer.GlobalSchedulerService 的实现,而 Quartz 实现是 org.jbpm.process.core.timer.impl.QuartzSchedulerService

可用性

应用程序或系统的可用性可以被视为它提供服务的总时间与它预期提供服务的总时间的比率。系统可用性受多种因素的影响,从硬件/软件故障导致的系统故障到维护和升级的已知停机时间。

通过使用故障转移机制,在应用程序中实现高可用性,该机制使得系统在发生故障后能够恢复到其服务提供状态。一个更优化的系统会考虑备份机制,在故障情况下可以立即切换,从而提高可用性。可以通过滚动升级进行计划维护以确保高可用性。这些解决方案通常以部署架构的形式表示,根据软件解决方案而变化,并考虑非功能性需求中的权衡。

下图展示了可以应用于 jBPM 工作台和运行时的示例部署架构,该架构可以满足高可用性和高吞吐量场景。该架构通过拥有所有数据的持久化、分布式存储和具有被动备份的负载均衡器来考虑故障转移机制,以确保在部分故障(节点故障)时切换节点。jBPM 不提供此部署架构中所需的所有组件,但必须获取第三方软件并进行集成。我们将在后续章节中讨论这些组件的适用性。

可用性

对工作台适用性

考虑工作空间的可用性时,我们需要考虑以下两点:

  • 基于 Web 的用户界面建模的可用性

  • 在建模期间创建资产的存储库的可用性

提供基于网页的用户界面的服务托管在应用服务器上,其可用性指的是应用服务器提供的可用性配置。通常,这些配置是以基于负载均衡器的故障转移机制的形式存在,当提供服务的节点失败时,负载均衡器会将流量切换到另一个节点,并且故障转移机制确保节点将在一定时间范围内恢复,以提供完整的潜在吞吐量。

资产存储在虚拟文件系统中,默认情况下,它是一个基于 Git 的仓库。正如我们在扩展业务流程建模设施部分所讨论的,基于 Git 的仓库可以集群化,并且资产可以同步。因此,即使一个节点失败,最新的资产也将从其他节点恢复。

对流程持续改进的适用性

从使用建模设施开发的应用程序的角度来看,应用程序流程可以持续改进。流程的新版本可以通过两种方式部署到 jBPM 运行时。

  • 流程的新版本可以作为新的流程定义部署,旧版本可以通过从知识库中移除它来退役,这样就不会部署这个流程版本的新实例。然而,这种方法绝对不能处理现有的流程实例。它们应该被允许继续使用早期版本,或者应该被终止并重新处理。选择方法的决策应根据这些流程定义所处理的企业场景逐案处理。

  • 使用 jBPM 提供的流程迁移设施。然而,该设施仅限于非冲突的流程更改。

因此,流程升级不是一个非常顺利的过程,需要谨慎处理。此外,为了实现可用性,升级必须在低流量期间进行,或者我们必须进行滚动升级。

对流程运行时的适用性

流程运行时的可用性包括与流程实例交互的服务,如流程实例生命周期和用户交互。类似于我们关于工作台设施的讨论,有两件事情我们需要考虑:

  • 流程实例交互的可用性

  • 流程实例数据的可用性

对于流程实例交互的可用性,我们需要有一个负载均衡机制,在节点失败时可以将一个节点的流量切换到另一个节点。此外,我们需要能够将流程实例数据持久化到非易失性存储中以便故障转移。正如在扩展流程运行时部分所讨论的,jBPM 支持在关系数据库中持久化运行时信息。

大多数流行的关系型数据库都内置了对集群存储的支持,以确保可用性。这可以用来确保持久化流程实例数据可以具有高度可用性。

安全

在此上下文中,安全或应用程序安全是指保护 jBPM 提供的服务和数据免受未经授权的访问(身份验证),同时确保用户可以访问授权给他们的服务和数据集(授权)。

在 BPM 系统中,我们必须考虑的另一个重要方面是为所有用户交互提供不可否认性。jBPM 通过提供所有运行时数据更改的审计日志功能来支持这一点。

注意

不可否认性确保用户不能否认在系统中执行的动作或操作。

保护应用程序资产访问

jBPM 通常部署在应用程序服务器中,使用 JEE 兼容的标准,即Java 认证和授权服务JAAS),来提供应用程序安全。应用程序服务器提供这项服务以及一个注册用户的机制。

用来保存应用程序资产的默认资产存储库是 Git 存储库。此外,Git 存储库确保由它处理的存储库更改(无论是文件的添加还是文件更改)仅允许授权人员执行。

对通过工作台登录的用户进行身份验证,以及通过 Eclipse 对存储库进行更改的用户必须使用 SSH。

工作台用户管理支持以下角色:

  • 管理员:具有完全访问权限的角色。扮演管理员角色的用户管理 BPMS。

  • 分析师:可以进行建模并关联业务分析师以创建流程、规则、实体、表单等的角色。

  • 开发者:承担从业务分析师那里传递流程工件的角色,并使用后端服务和处理器开发完全可执行的代码。

  • 业务用户:通过使用任务管理功能执行操作管理的角色。被分配此角色的人是应用程序的最终用户,他们利用应用程序提供的功能。

  • 仅查看用户:可以查看流程及其性能统计信息的角色,是统计仪表板的初级用户。

以安装在 JBoss 应用程序服务器(现更名为 WildFly)中的 jBPM 为例,它提供了add-user等实用工具来添加用户并分配他们的角色。

保护流程运行时

下面的子节详细说明了保护流程运行时的规定,即保护特定于操作管理的操作。

专门针对人工任务的安全

工作台仅允许授权人员访问上一节中讨论的流程运行时功能。除了流程管理访问之外,在流程定义的每个部分中,每个人工任务都分配给一个用户或一个角色(组)。

在使用包括流程模型器在内的整个 jBPM 软件套件的环境中,我们可以通过使用应用程序服务器的功能添加更多角色或组到系统中来驱动此操作。在嵌入式模式下,jBPM 提供了一个实现特定于应用程序的访问安全机制的扩展点。

可以通过实现org.kie.api.task.UserGroupCallback接口并嵌入验证用户执行任务权限的应用特定逻辑来完成此扩展。为了实现此集成,我们需要实现以下特定方法:

boolean existsUser(String userId)

此接口方法用于确定附加到任务的用户是否有效。为了与我们的应用程序集成,我们可以通过使用用于身份验证的逻辑或服务来实现此方法。

boolean existsGroup(String groupId)

此接口方法用于解决附加到任务的组或角色是否有效。

List<String> getGroupsForUser(String userId,List<String> groupIds, List<String> allExistingGroupIds)

这用于解决对用户有效的组(或角色)。

可以通过使用HumanTaskService工厂将userGroupCallback接口实现附加到流程运行时。执行此操作的代码片段如下:

UserGroupCallBack userGroupCallBack= new CustomUserGroupCallback();
TaskService taskService = HumanTaskServiceFactory.newTaskServiceConfigurator().entityManagerFactory(emf).userGroupCallback(userGroupCallBack).getTaskService();

此机制的优势在于,我们不受 jBPM 提供的用户管理功能的限制,可以开发自己的。此外,jBPM 提供了一组预构建的userGroupCallBack函数,可以在生产中使用:

  • org.jbpm.services.task.identity.LDAPUserGroupCallbackImpl,正如其名称所示,可以用于配置与您的 LDAP 服务。

  • org.jbpm.services.task.identity.JAASUserGroupCallbackImpl,正如其名称所示,可以用于配置与您的 JAAS 标准特定用户认证机制,这些机制在应用程序服务器环境中广泛使用。jBPM 为 Oracle WebLogic、IBM WebSphere 和 JBoss 应用程序服务器提供了适配器。

注意

LDAP(代表轻量级目录访问协议)是一个开放标准,在小型和中型组织中广泛用于在服务和系统之间共享用户信息。

审计日志

在使用 BPM 的业务领域,过程定义了业务本身。使用这些过程,组织中的多个系统和人员相互交互。在任何组织中,关于由人或系统执行的动作的争议都很常见。从应用安全的角度来看,这些场景通过不可抵赖机制来解决,这些机制确保没有任何用户或系统能够否认这些动作。审计日志是广泛使用的不可抵赖机制之一,其中系统上执行的每个动作都被存储,并随后用于解决争议或分析争议的根本原因。另一个优点是,我们可以使用这些数据来分析和找出业务流程的性能和质量指标。

审计日志帮助我们检索有关过程实例发生情况、发生时间以及谁触发了它的信息。

jBPM 提供了一个通用的审计日志机制,全面覆盖业务流程的生命周期。审计日志以三个数据模型存储:

  • 过程实例日志:存储与过程实例生命周期交互对应的数据,例如,过程实例的启动、停止或终止过程实例。使用实例日志的属性,我们可以回溯过程定义、过程版本、过程实例、用户身份等信息,这些信息与生命周期变更相关。

  • 节点实例日志:存储与过程中节点生命周期对应的数据。节点通常指业务过程中的活动。这些数据的属性帮助我们回溯到过程定义、过程版本、过程实例、用户身份、时间以及发生此事件的工作项。

  • 变量实例日志:存储与过程实例中过程变量变化对应的数据。

以下表格列出了审计日志数据模型中可用的数据:

过程实例日志
字段
ID
持续时间
结束日期
外部 ID
用户身份
结果
父过程实例 ID
过程 ID
流程实例 ID
流程名称
流程版本
开始日期
状态
节点实例日志
字段
ID
连接
日志日期
外部 ID
节点实例 ID
节点名称
节点类型
流程 ID
流程实例 ID
类型
工作项 ID
变量实例日志
字段
ID
日志日期
外部 ID
旧值
流程 ID
流程实例 ID
变量 ID
变量实例 ID

除了用于安全之外,这些日志信息可以用来分析流程和组织的各种性能指标。仪表板构建者可以用来从这些日志中构建报告。

可维护性

系统的可维护性可以被视为一个衡量指标,用来确定修复操作可以多么容易地执行。当我们说到修复时,我们需要讨论以下内容:

  • 在部署系统中修复问题的容易程度(如果有)

  • 系统的改进以适应不断变化的企业需求

  • 应对部署环境中的基础设施变化

在我们考虑的系统中,业务逻辑的变化更为频繁。因此,从可维护性的角度来看,主要因素之一是提高流程可执行性的容易程度。这是 jBPM 的一项优势;正如我们在前面的章节中已经讨论过的,jBPM 提供了一个完整的建模、模拟和部署工具环境。从这个角度来看,参与者,即业务分析师和开发者,可以使用这些工具来建模、模拟、测试和部署流程变更。

另一个方面是 jBPM 在生产环境中部署和维护的基础设施或环境。jBPM 支持多种部署架构,如第八章整合 jBPM 与企业架构所述,默认情况下,它侧重于在 JEE 环境中的部署,其中它部署在一个 JEE 应用程序容器内部,持久数据存储在传统的关系型数据库中。

系统的架构基于以下标准:

  • 基于 BPMN 的建模

  • 基于 BPsim 的模拟

  • 基于 WS-HT 的人类任务

  • 基于 JPA 的持久性

  • 基于 JTA 的事务管理

优势在于 jBPM 可以轻松地适应我们当前的 生产环境,并且随着环境的演变,jBPM 也随之发展,其开发社区在 企业中间件架构中发挥着积极的作用。对标准和系统模块化的遵守确保了我们的客户不会陷入供应商锁定场景,系统的一部分可以轻松替换。

在前面的章节中,我们已经解释了本节所讨论功能的“如何”。

容错性

容错性表示在系统发生一个或多个故障时能够以预测方式运行的能力。在基于 Java 的应用程序中,这些故障是通过异常处理机制来管理的。jBPM 也不例外;它使用异常处理方法来实现容错性。

流程定义中的异常处理

我们可以使用 BPMN 元素来指定业务流程中发生和处理的机制,如下所示:

  • 可以使用错误事件来指定意外情况的发生。与 Java 编程相比,这类似于抛出错误。

  • 可以使用补偿来指定当发生错误时应该做什么;这类似于 Java 程序中的 catch 操作结构。

在流程级别使用异常处理的优势在于,异常场景在流程中是可见的,从而使得对这些场景的监控和分析更加容易,从而有助于流程的持续改进。

针对特定领域流程的异常处理

当我们定义新的自定义工作项处理程序,这些处理程序在流程中形成自定义、业务特定的活动时,我们可以指定处理异常场景的机制。jBPM 默认提供以下装饰器来处理活动中的异常场景:

  • SignallingTaskHandlerDecorator:这个装饰器在活动的生命周期方法中捕获异常,并使用可配置的事件通知流程实例。这些事件可以在流程定义中被捕获,并采取后续行动。这个装饰器可以在将工作项处理程序注册到会话时指定。

    例如:

    String eventType = "Mail-Service-failed";
    SignallingTaskHandlerDecorator signallingTaskWrapper = new SignallingTaskHandlerDecorator
    (MailServiceHandler.class, eventType); signallingTaskWrapper.setWorkItemExceptionParameterName
    (ExceptionService.exceptionParameterName);
    ksession.getWorkItemManager().registerWorkItemHandler
    ("Mail Task", signallingTaskWrapper);
    

    在这个示例中,我们通过使用MailServiceHandler.class注册一个发送邮件的处理程序,并在异常场景下,将"Mail-Service-Failed"事件通知给流程实例。

  • LoggingTaskHandlerDcorator:这个装饰器在日志机制活动的生命周期方法中捕获异常。这个特性可以在不那么关键的区域使用,其中流程异常在日志中只是一个警告。

摘要

在本章中,我们讨论了 jBPM 的非功能性特征,这些特征对于基于 BPM 构建生产就绪的应用至关重要。此外,我们还讨论了使用 jBPM 满足各种需求和配置的示例部署架构,以及可用于在 jBPM 系统中包含某些特性的定制选项。

附录 A. 未来

当我们到达《精通 jBPM6》的结尾,回顾 jBPM 的为什么、是什么和怎么做时,讨论未来所持的观点变得必要。当然,我们不是预言家,但当我们谈论的是我们企业架构的核心技术,并且是我们在企业中传播的技术时,我们需要了解这个系统在未来的计算中的位置。

计算机世界正在经历快速演变,自动化是关键,物联网IoT)就在门口,正在改变我们已知的世界。在这简短的附录中,我们通过详细说明以下计算趋势来讨论 jBPM 及其相关技术在未来的位置:

  • 业务编程

  • 企业架构的收敛

业务编程

企业应用程序是由业务需求驱动的。换句话说,我们编写这些应用程序是为了满足某些业务需求。进一步来说,在传统的软件开发生命周期中,需求是由业务用户传达给软件开发者的,软件开发者将这些需求整合并转化为可执行的实现。

使用编程语言开发应用程序的传统方式已经被业务级别的执行语言所取代,这些语言能够谈论业务(或使用业务友好型术语),例如 BPEL。在当前领域,为了提高可用性和可维护性,它们正在演变为支持可视化业务导向编程的应用程序,其中 jBPM 是一个标志性代表。

“一图胜千言”——这句话可以解释为什么我们需要可视化编程。显然,目标是有效地沟通。编程不再被视为巫术,程序及其逻辑是业务运营的一部分,应该对组织中的每个人都是易于获取的。

此外,我们可以自信地说,这正是编程正在被引导的地方。用户必须能够查看操作是如何进行的,决策是如何做出的,以及变化的影响是什么。jBPM 建模功能是一次巨大的飞跃,进入了一个完全可视化的编程体验。目前,jBPM 工具箱允许我们建模业务流程和业务规则。我们可以通过有限的但有用的功能来建模数据和用户界面,从而最大限度地减少开发和部署成本。我们还可以模拟一个业务流程,并查看它是如何工作的,同时分析相关的性能指标。

已经趋势化的词汇,如 IoT 或无处不在的计算,从 BPM 的角度来看,这些都是业务运营中的活动。可视化编程可以帮助我们封装与这些无处不在的设备通信的逻辑,并为业务提供一个图像,展示与流程相关联的每个人以及操作流程的每个人。

企业架构在实时和预测分析方面的收敛

那些 IT 基础设施包含用于操作、分析和管理的独立应用程序的日子已经过去了。今天的 IT 基础设施需要能够一起提供操作、分析和管理的企业应用程序套件,并且能够协作共享信息(实时),这使得它们的运行更加高效。例如,操作数据被分析用于检测欺诈操作,从而使操作更加安全。进一步来说,未来需要这些操作之间更加无缝的交互。

从技术角度来看,我们可以将这些视为多种设计模式的融合。我们可以看到系统如何有效地融合面向服务的架构SOA)和事件驱动架构。例如,LAMBDA 架构将批处理、分析和服务提供合并在一起。

这种融合可以导致更有效的技术,例如预测分析。基于 BPM 的系统可以用于产生、消费事件,并与服务交互。因此,对这些事件的预测分析是一个明显的改进。此外,BPMs 可以用于采取这些预测分析引擎获得的结果(或决策)的行动。这可以在以下领域产生可预见的应用:

  • 欺诈检测。

  • 推荐营销。

  • 更有弹性的制造自动化,等等。

我们已经在第一章中讨论过的 BLIP,即业务流程建模 – 连接业务与技术,无疑正在向这个目标迈进。它提供了一种将分散在操作中的业务逻辑集成起来的方法,并使它们能够协作以实现高效的业务运营;jBPM 是 BLIP 的重要组成部分。

附录 B. jBPM BPMN 构造参考

本附录是 jBPM 支持的 BPMN 构造的快速参考。对于完整的 BPMN 2.0 指南,请参阅对象管理组提供的规范和指南,网址为www.bpmn.org。您还可以参考本书的第五章,BPMN 构造,以深入了解 jBPM 构造的示例驱动讨论。为了便于索引和搜索,本参考将构造描述为在 KIE 流程定义编辑器中显示和分组的。

任务

一个任务代表需要执行的动作。

用户

用户

  • BPMN 元素<bpmn2:userTask>.

  • 描述:需要与 UI 或程序化 API 进行人工交互的任务。

  • 配置:使用ActorsGroups将任务分配给用户。使用AssignmentsDataInputSetDataOutputSet属性将任务参数映射到封装的流程实例。

发送

发送

  • BPMN 元素<bpmn2:sendTask>

  • 描述:用于发送消息操作的通用任务

  • 配置MessageRef属性是message元素(bpmn2:message)ID 属性的键,必须在流程范围内定义

注意

它需要一个自定义的 WorkItemHandler,并使用send键进行注册。

接收

接收

  • BPMN 元素<bpmn2:receiveTask>

  • 描述:用于接收消息操作的通用任务

  • 配置MessageRef属性是message元素(bpmn2:message)ID 属性的键,必须在流程范围内定义

注意

它需要一个自定义的 WorkItemHandler,并使用receive键进行注册。

手动

手动

  • BPMN 元素<bpmn2:manualTask>

  • 描述:其目的是记录必须手动执行的动作,并且可以被引擎忽略的任务

服务

服务

  • BPMN 元素<bpmn2:serviceTask>.

  • 描述:支持 Java 或 SOAP WebService 调用的服务任务。

  • 配置:使用ServiceImplementationServiceInterfaceServiceOperation属性配置可调用的服务/类。对于 Web 服务实现,需要在全局范围(流程导入)中导入 WSDL URL。

业务规则

业务规则

  • BPMN 元素<bpmn2:businessRuleTask>

  • 描述:执行 Drool 规则的任务

  • 配置:使用Ruleflow Group属性选择定义所需执行规则集的规则组名称

脚本

脚本

  • BPMN 元素<bpmn2:scriptTask>

  • 描述:执行 Java 或 MVEL 脚本的任务

  • 配置:使用On Entry ActionsOn Exit ActionsScript language属性

无

  • BPMN 元素: <bpmn2:task>

  • 描述: 临时任务

  • 配置: 你必须使用 task 键注册一个 WorkItemHandler

子流程

子流程表示一组协同执行流程一部分的任务。

可重用

可重用

  • BPMN 元素: <bpmn2:callActivity>.

  • 描述: 允许你调用定义在当前流程之外的流程。

  • 配置: 使用 CalledElementIndependentWaitForCompletion 来配置要调用的现有流程定义及其调用方式:作为调用过程的一部分或作为一个新流程实例(Independent),以同步或异步方式(WaitForCompletion)。使用 AssignmentsDataInputSetDataOutputSet 将变量从/到调用过程映射。

多实例

多实例

  • BPMN 元素: <bpmn2:subProcess>

  • 描述: 允许你循环(创建一组元素的多个实例)

  • 配置: 使用 CollectionExpressionVariable DefinitionsVariable Name 来配置循环并将变量分配给循环内部传递

临时

临时

  • BPMN 元素: <bpmn2:adHocSubProcess>

  • 描述: 允许你定义一个无结构的子流程定义

  • 配置: AdHoc ordering 属性告诉引擎以并行或顺序方式执行多实例子流程

嵌入

嵌入

  • BPMN 元素: <bpmn2:subProcess>

  • 描述: 允许你定义一个嵌入式的流程定义(不可从其他流程定义中重用)

  • 配置: 变量定义 允许你在子流程范围内配置变量

事件

事件

  • BPMN 元素: <bpmn2:subProcess>

  • 描述: 允许你定义一个可以由特定事件(例如,Signal)触发并在异步方式下执行的嵌入子流程

  • 配置: 无特定配置要求

开始事件

  • BPMN 元素: <bpmn2:startEvent> 以及定义事件类型的子元素,如图所示:开始事件

  • 描述: 作为流程触发器,只能是一个捕获事件。

支持的开始事件如下(参见前述图像,从左到右):

  • 消息: <bpmn2:messageEventDefinition>

  • 计时器: <bpmn2:timerEventDefinition>

  • 升级: <bpmn2:escalationEventDefinition>

  • 条件: <bpmn2:conditionalEventDefinition>

  • 错误: <bpmn2:errorEventDefinition>

  • 补偿: <bpmn2:compensationEventDefinition>

  • 信号: <bpmn2:signalEventDefinition>

结束事件

  • BPMN 元素: <bpmn2:endEvent> 以及定义事件类型的子元素,如图所示:结束事件

  • 描述: 作为流程触发器,只能是一个抛出事件。

支持的结束事件如下(参见前面的图像,从左到右):

  • 消息

  • 升级

  • 错误

  • 取消<bpmn2:cancelEventDefinition>

  • 补偿

  • 信号

  • 终止<bpmn2:terminateEventDefinition>

捕获中间事件

  • BPMN 元素<bpmn2:intermediateCatchEvent> 和一个定义事件类型的子元素(参见前一个事件列表)

  • 描述:捕获匹配抛出事件的触发事件捕获中间事件

支持的事件如下(参见前面的图像,从左到右):

  • 消息:可以是边界事件

  • 计时器:可以是边界事件

  • 升级:可以是边界事件

  • 条件:可以是边界事件

  • 错误:可以是边界事件

  • 补偿:可以是边界事件

  • 信号:可以是边界事件

抛出中间事件

  • BPMN 元素<bpmn2:intermediateThrowEvent>

  • 描述:抛出事件触发的事件抛出中间事件

支持的事件如下(参见前面的图像,从左到右):

  • 消息

  • 升级

  • 信号

网关

网关控制流程的流向。

基于数据的排他(XOR)

基于数据的排他(XOR)

  • BPMN 元素<bpmn2:exclusiveGateway>

  • 描述:用于选择替代序列流

基于事件的网关

基于事件的网关

  • BPMN 元素<bpmn2:eventBasedGateway>

  • 描述:用于在某个事件发生时触发序列流

并行

并行

  • BPMN 元素<bpmn2:parallelGateway>

  • 描述:用于创建所有路径都评估的并行序列流

包容

包容

  • BPMN 元素<bpmn2:inclusiveGateway>

  • 描述:用于创建所有路径都评估的替代流

数据对象

数据对象

  • BPMN 元素<bpmn2:dataObject>

  • 描述:数据对象向读者显示在活动中需要或产生的数据

游泳道

游泳道表示根据流程任务对流程参与者或角色的分组。

通道

通道

  • BPMN 元素<bpmn2:lane>

  • 描述:用于根据用户或用户组组织组内的活动

艺术品

艺术品是用于文档目的的有用元素。

组

  • BPMN 元素<bpmn2:group>

  • 描述:组将不同的活动视觉上排列在一起;它不影响图表中的流向

注释

注释

  • BPMN 元素<bpmn2:annotation>

  • 描述:注释用于向图表的读者提供可理解的注释/描述

posted @ 2025-09-12 13:57  绝不原创的飞龙  阅读(7)  评论(0)    收藏  举报