目前,您已经通过前三篇文章了解了 WebSphere Integration Developer 及其配套工具的主要概念,已经为深入研究业务流程做好了准备。如果您尚未阅读介绍业务状态机的第四篇文章,请不必担心;稍后可以回过头来阅读它,因为它不是学习本文的先决条件。事实上,如果您粗略地阅读了以前的文章,但没有时间完成简单的应用程序,稍后您可以在本文结尾处下载 OrderProcessing 模块,并将它用作构建业务流程的起点。
您可能已经知道,业务流程是组织用于完成更大业务目标的任何系统或程序。您可以自动执行业务流程,也可以通过需要由一个或多个用户手动完成的多个步骤来执行。业务流程运行的时间可以很短,也可以是数小时、数天、数周或更长时间。业务流程能够很好地驱动您的业务,但是在面向服务的体系结构 (SOA) 中构建业务流程会使您的应用程序使用过度。总之,如果能够将业务流程与其他组件和模块无缝集成,则可以产生非常强大的功能,从而使您的工作自动完成。
通过 WebSphere Integration Developer,您可以使用业务流程编辑器创建业务流程。业务流程提供了协调企业服务和描述业务逻辑的主要方法。那么,这意味着什么?业务流程是由按特定顺序运行的一系列步骤或活动组成的服务组件的实现。这些活动可以调用其他服务、执行计算或执行任何种类的编程工作。流程中的活动可以按顺序运行或并行运行。您也可以在活动之间的控制流中实现分支或循环。一个业务流程可以是较大业务流程的一部分,它也可以包含嵌套的业务流程。
在下面的部分中,您将学习:
- 业务流程的各个组成部分
- 业务流程的概念
- 业务流程编辑器
- 构建自已的业务流程。
如果您阅读了以前关于业务状态机的文章,那么您可能会知道业务状态机是业务流程的特殊情况。先透露给您一个小秘密,业务流程实际上是实现一个状态机。业务状态机和业务流程都是定义业务逻辑的重要技术。您也许对以下问题百思不得其解:“如果它们都非常重要,而且又非常类似,那么我怎样知道何时使用业务流程,何时使用状态机?”
答案是,如果业务逻辑包括对事件的响应,并且该响应取决于该流程的当前状态,那么将逻辑实现为业务状态机可能非常有用。当逻辑在本质上是循环逻辑(也就是说,对象或系统的一部分重复执行一系列状态)时,状态机也非常有用。例如,自动售货机先等待投入硬币,接着允许您购买饮料,然后找回零钱,之后再耐心等待下一个购物者。另一方面,业务流程对于所有其他情况非常有用,尤其是您的业务逻辑是需要按顺序或并行执行一系列步骤的情况。
SOA 由许多连接在一起的服务组成,这些服务可以相互通信,以实现总体目标。正如您从以前的文章了解到的,业务流程是您实现服务组件的方法之一。
业务流程由以下元素组成,在后续部分中我们将探讨这些元素:
- 活动
- 合作伙伴
- 变量
- 相关集
- 处理程序
业务流程组件由一组活动 组成,每个活动执行某一项工作。这些活动组合在一起表示您的业务逻辑。每个活动执行的工作完全取决于您。其中包括执行计算、调用业务合作伙伴的服务或请求组织中的人员执行某一手动工作。
您可以将每个活动划分为更多的活动。例如,流程中的某个活动可以为客户的订单开具票据。在您的公司处于前沿技术之前,整个开票活动可能划分为以下几步:查找客户的地址、打印发票,然后邮寄。前两个活动可能是服务调用,后一个可能是人工任务。活动可以按顺序执行或并行执行。例如,当正在并行执行发送活动时,订单流程中的活动可以通知库存系统需要重新定购商品。
对于每种活动,您都可以通过属性视图中的 Event Monitoring 选项卡启用事件监视。这可以使您的流程能够在运行时发出 Common Event Infrastructure 事件。根据活动类型,可以使用不同的选项。例如,调用活动可以发出进入和/或退出事件。当业务流程将要执行或进入调用活动时,调用活动会发出进入事件,而当业务流程将要完成执行调用活动时,会发出退出事件。
通过从面板中选择活动并将其放置到业务流程编辑器画布上,或右键单击画布并 Add - [activity type],可以将活动添加到流程。
现在,让我们看一下您在业务流程中可以使用的各种类型的活动。
服务活动使您的业务流程能够与其他服务通信,反之亦然。如果没有服务活动,您的流程会非常孤立。服务活动有以下三种类型:
- 接收
- 应答
- 调用
接收活动
接收 活动是流程的入口点,它是流程的开始点或继续点。您在流程接口中定义的每个操作都需要一个接收活动。在流程编辑器中,您可以指定哪一个操作对应于哪一个接收活动。这意味着当调用一个流程的操作时,对应的接收活动会接收该调用,并且流程会从那里继续运行。流程至少需要一个接收活动才能启动。接收活动也可以发生在业务流程的中间。在这种情况下,如果流程在运行时遇到接收活动,则流程会停止,并等待相应的操作被调用。
为了便于理解,让我们看一个示例。在图 1 的示例中,SimpleProcess 有一个接口,它包含两个操作:start 和 continue。这些操作是按先后顺序运行的接收活动。让我们看一看当流程运行时会发生什么情况。
-
当另一个组件调用
start操作时,它会创建一个新的流程实例。 -
接下来,输入参数(可以在该图的底部看到)将其值赋予变量
Input1。在创建该流程的同时也创建了输入和输出变量。 -
CallService活动运行,并且在该活动完成时,流程会停止运行,直到它收到对continue操作的调用。 -
一旦调用了
continue操作,该流程会继续执行PrepareResponse活动。
图 1. 服务活动
应答活动
当接收活动属于请求-响应操作时,应答 活动会返回操作的输出。图 1 将应答活动显示为简单流程中的最后一个节点。如图 2 所示,它使用 Output1 作为输出参数来发送对 start 操作的响应。应答活动不必位于流程的结尾。流程可以从接收活动开始,然后在继续执行其他工作之前,返回响应。每个接收活动都可以有多个应答活动,例如您的流程具有多个路径的情况。这里的思路是,当另一个组件调用流程接口的请求-响应操作时,它需要最终获取该操作的响应(可能返回一个错误,而不是应答;我们稍后会讨论该问题)。
图 2. 应答活动的详细信息
调用活动
调用 活动仅调用另一个服务的操作。使用调用活动可以调用的服务取决于流程的合作伙伴(稍后对其进行解释)。
图 3 显示了您实现调用活动的方法,其中,CallService 调用活动被定义为调用 ServicePartner 的 doService 操作。
图 3. 调用活动的详细信息
结构化 活动包含其他活动。结构化活动包括:
- 序列
- 选择
- 接收选择
- 并行
- While 循环
- 范围
序列活动
最简单的结构化活动是序列活动。序列 活动包含其他活动,这些活动按它们在序列中出现的顺序运行。它包含的活动可以是简单的活动,也可以是其他结构化活动。需要指出的一点是:请回头看一下图 1,完整的流程由初始节点和结束节点之间的一系列活动组成。编辑器画布实际上是一个大型结构化活动,您可以在其中添加更多的简单或复杂的活动。可以隐藏序列活动,以保持图表清洁。事实上,下面的所有结构化活动(并行活动除外)都包含隐藏的序列活动(浅灰色箭头连接的活动)。
图 4 显示了一个包含两个节点的简单序列活动。首先运行 CallFirst 活动,然后运行 CallSecond 活动。
图 4. 序列活动
选择活动
选择 结构化活动(也称为切换)根据条件控制流程采用的路径。简单地说,选择活动允许您确定要运行的下一个活动集。选择活动包含 case 元素,这些 case 元素由计算结果为 True 或 False 并且后跟一系列活动的表达式。计算结果首先为 True 的第一个 case 元素会先运行,接着会运行它的一系列活动。选择活动也可以包含 Otherwise 元素,它是在所有 case 元素的计算结果都不为 True 时所采用的路径。
以图 5 为例,ShippingChoice 是一个选择活动,SmallOrder 是一个 case 元素。SmallOrder 包含一个使用可视代码片段表示的条件。该条件规定:如果数量大于或等于 1 并且小于 10,那么返回值将为 True,接着会运行 CourierOrder 活动。第四篇文章详细描述了可视代码片段编辑器,本例就是使用该编辑器为 SmallOrder 情况定义了条件。图 5 还显示了 Otherwise 元素,当两个 case 元素的计算结果都不为 True 时,则采用它的路径。
图 5. 选择活动和 case 条件
接收选择活动
与选择和接收活动都相关的活动是接收选择活动。接收选择 活动(也称为挑选)在形式和工作方式上更像选择活动。不同点是它替代了 case 元素,其中含有一个或多个接收元素,而没有 Otherwise 元素。接收选择活动中的每个接收元素都接受特定类型的消息(流程接口的特定操作)。流程在到达接收选择活动时将停止执行,等待接收消息。普通的接收活动和接收选择活动之间的不同点是,使用接收选择活动可以接收若干操作中的任一操作。流程接收的第一个操作会首先执行,就像普通的选择活动,并且流程会按照其路径运行。
图 6 显示了一个称为 OrderAction 的接收选择活动的示例。当到达 OrderAction 时,流程会停止,并等待调用 Proceed 或 Cancel 操作。如果首先调用 Proceed 操作,那么 ProcessOrder 调用活动将运行,然后运行 Update 活动。
图 6. 接收选择活动
并行活动
有时,您不需要让所有的活动像在序列活动中那样按顺序运行。当您的流程有多组可以并行运行的活动,或者流可能分支到其他路径上的活动时,您可以将它们放置在并行 活动中(也称为 流)。并行活动中的活动通过链接连接在一起后仍可以按顺序运行。
链接 具有方向性,您可以将其指定为从源 活动到目标 活动。当源活动结束运行时,链接另一端的目标活动便开始运行。您可以创建从单个源活动指向多个目标活动的链接或从多个源活动指向单个目标活动的链接。您唯一不能做的是创建循环,即将源活动链接到目标活动,而目标活动又链接回源活动(类似于无限循环)。
链接可以有链接条件,链接条件是控制是否允许链接的表达式。如果链接条件返回 False,则意味着不能遵循该链接。在这种情况下,如果这是源和目标活动之间的唯一链接,则目标活动不运行。如果涉及到多个传入链接,则这种情况就会变得稍微复杂一些。
让我们设想这样一种情形:目标活动有多个传入链接,如图 7 中的 Activity4。如果有些链接得到遵循而有些没有得到遵循会发生什么情况?这里的遵循是指,链接的源位置的活动结束运行并且任何链接条件都没有返回 False。在缺省情况下,如果任何一个传入链接得到遵循,目标活动就会运行。联接条件 允许您指定一个或多个链接的目标应何时运行。您可以使用 Java 代码、可视代码片段,或从简单的选择列表中进行选择来创建联接条件。
如果不能满足联接条件,则会抛出联接失败 错误。通过在活动属性的 Join Behavior 选项卡上为 Suppress Join Failure 选择 Yes,可以指示流程不抛出联接失败错误,而改为跳过该活动,继续执行下一个活动。
说得不少了,现在让我们看一个例子吧。图 7 显示了一个包含四个活动的并行活动。因为在 Activity1 和 Activity3 之间的链接上没有任何条件,所以在 Activity1 完成后将运行 Activity3。Activity2 与之并行运行,但仅当 amount 的值小于 5 时才能并行运行(如该图底部的链接条件所示)。图 8 显示 Activity4 的联接条件被设置为 All,这意味着仅当传入的两个链接同时有效时,才运行 Activity4。不过,如果 Activity1 和 Activity2 之间的链接条件为 False,那么 Activity2 和 Activity3 之间的链接始终不能得到遵循。在这种情况下,联接条件永远不能满足,并且会发生联接失败。
图 7. 并行活动
图 8. 联接条件
While 循环活动
当您希望在某个条件不再为 True 之前多次运行一组活动时,您可以使用 While 循环活动。While 循环 活动包含其他活动和一个条件。当条件的计算结果为 False 时,循环终止,运行 While 循环之后的下一个活动。
让我们看一个简单的例子。图 9 显示了一个 While 循环,该循环在 isComplete 变量的值为 True 时停止执行(因为 inverse 节点返回 False)。每次执行该循环时,都会调用 CheckServiceComplete 服务,该服务会返回一个赋予 isComplete 变量的布尔值。这样,在服务返回 True 时就会退出循环,接着运行 Reply 活动。
图 9. While 循环活动
范围活动
范围活动是结构化活动,它可以封闭任何其他活动。范围 活动允许您定义本地变量,本地相关集和各种处理程序。范围中的活动有权访问属于该范围的任何变量。范围活动可以在其内部包含多个范围,因此范围中的活动也可以访问所有封闭范围的变量。
在范围的详细信息中,您可以启用两个选项:隔离和补偿。当您选择隔离 时,可以控制对变量的访问,这样,当多个活动同时运行时,一次只能访问一个变量。当选择补偿 时,您可以调用范围的补偿处理程序。
在文章的稍后部分中讨论变量、相关集和每种类型的处理程序时,我们还会提到范围活动。
不可避免地会存在一些阻止流程完成的情况。错误 是可能发生的预期错误。幸运的是,提供了处理这些情况的专门活动。
- 抛出
- 重新抛出
- 补偿
- 终止。
抛出活动
抛出 活动允许您通知流程中出现问题。如果操作是请求-响应类型,并且在接口的输入和输出部分存在错误部分,则抛出活动会向操作的调用方发出存在错误条件的信号。当您通过抛出活动抛出错误时,您可能希望作为流程的一部分来处理某些问题,而不是仅返回错误,在这种情况下,您可以创建一个错误处理程序(关于错误处理程序的详细信息,稍后将会介绍,但是现在您可能已经猜到它们可以执行什么操作)来捕获抛出的错误。错误必须有一个名称,它可以包含与错误相关的信息的变量,但该变量不是必需的。
在前面的选择活动部分中,我们在示例中已提到,如果订单大小不在每个 case 元素涵盖的范围内,则会抛出 OrderSizeFault 错误。图 10 显示了 ThrowOrderSizeFault 活动的详细信息。
图 10. 抛出活动
重新抛出活动
重新抛出 活动与抛出活动相同,只不过它是在错误处理程序中发生的。它可以使您重新抛出错误处理程序捕获的错误,以便流程的任何封闭范围或调用方都可以处理它。例如,OrderSizeFault 处理程序可能记录异常,然后重新抛出它,以便调用该流程的订单处理组件不会继续向客户开具票据。我们将在错误处理部分对其进行进一步阐述。
补偿活动
补偿 活动允许您调用活动的补偿操作或范围的补偿处理程序。您只需将其放置在补偿处理程序或错误处理程序中即可。补偿 是对已经成功完成的工作执行“撤消”操作。例如,假设在收到付款或从接受客户付款的活动成功完成之后,您的流程涉及发送订单。那么,这是出现了错误,不能发出完整的订单。补偿处理程序可以执行偿还客户缺少的商品之类的操作。我们稍后将详细讨论补偿处理程序。
对于补偿活动,您可以为补偿设置目标活动,它可以是单个活动或一个范围,如图 11 所示。
图 11. 补偿活动
终止活动
终止 活动允许您在不执行任何补偿或错误处理的情况下尽快停止流程实例。
其他一些有用的活动包括
- 赋值
- 代码片段
- 人工任务
- 等待
- 空
赋值活动
赋值 活动允许您将值从一个变量复制到另一个变量或复制到初始化变量。在第四篇文章中,在业务状态机中,将值赋予输入或从服务调用的输出中复制值时,就与这种情况类似。下面用一个示例说明赋值活动能够执行的操作,假设在发送完成之后您希望更新订单信息。图 12 显示了如何将 quantityShipped 值从 shippedOrder 业务对象复制到 order 业务对象的 quantity 属性。
图 12. 赋值活动
代码片段活动
当您需要的活动逻辑比其他活动能够提供的逻辑更复杂时,您可以使用代码片段 活动,它允许您将工作委派给您创建的小程序。您可以选择普通 Java™ 来定义代码片段活动执行的操作,或者使用我们在第四篇文章中介绍的可视代码片段编辑器。
人工任务活动
在第二篇文章和第三篇文章中,我们向您介绍了人工任务组件。您还可以在流程中直接包括人工任务。当工作需要由某个人执行时,可以使用人工任务 活动。
等待活动
当您需要让流程停止运行一段时间时,可以使用等待 活动。在等待活动的详细信息中,您只需指定要等待的时间,或指定流程要继续运行的特定时间和日期即可。您甚至可以使用可视代码片段编辑器来计算要等待的时间。图 13 显示一个等待活动,它一直等到 2006 年 12 月 1 日中午才调用 EmailSeasonsGreetings 服务。
图 13. 等待活动
空活动
您可能需要一个根本不执行任何工作的活动,例如,当您拥有一个需要某个活动的构造,但没有要执行的任何工作时。例如,像我们将要在错误处理部分描述的那样,您可能希望忽略某些错误,只需要继续执行您的流程。因为错误处理程序必须包含活动,所以您只需插入空活动来禁止错误即可。您也可以将空活动用作活动的占位符,以后填写其详细信息。
服务组件定义您的流程必须实现的一组接口。它还可以定义您的流程可以调用的其他服务的一组引用。在流程中,我们使用术语合作伙伴 来描述正在调用您的接口或您正在调用的其他服务。在您的流程所属的组件上的每个接口都将有一个接口合作伙伴。流程实现的组件的每个引用都对应于流程中的一个引用合作伙伴。简单地说,当客户机调用您的流程时,您可以将其当作调用您的接口合作伙伴。当您调用另一个服务时,您可以使用引用合作伙伴执行此操作。所以,接口合作伙伴包含传入的操作,而引用合作伙伴包含传出的操作。
例如,图 1 中流程的组件有一个带有 start 和 continue 操作(分别用于 Start 和 Continue 活动)的接口,这两个操作属于 SimpleProcess 接口合作伙伴。引用合作伙伴是用来调用其他服务的合作伙伴,如图 3 所示,其中我们定义了 CallService 调用活动来调用 ServicePartner 的 doService 操作。
在通过右键单击集成编辑器 (Assembly Editor) 中的组件,并选择 Generate Implementation - Process 生成组件的流程实现时,在 Reference Partners 下会出现所有组件的引用,并可供服务调用。类似地,当您先创建流程,然后将该流程拖到集成编辑器时,它将为所有引用合作伙伴创建组件引用。这是在创建和连接服务时,Websphere Integration Developer 使事情变得更为简单的众多方法之一。当然,在创建了组件和流程之后,您可以根据需要添加更多的引用和合作伙伴。
变量 是在流程(或任意组件类型)中使用的业务数据的容器。您可以通过在流程编辑器的 Variables 部分中右键单击,并选择 Add variable 来声明变量。我们在范围活动部分中已提到,您可以在范围中声明本地变量。本地变量 是仅可以在声明它的范围中和嵌套范围中使用的变量(也就是说,您可以向其赋值,也可以从中取值)。
当在 Variables 部分中添加变量时,会将该变量添加到当前选择的范围。例如,在选择不同的范围时,就会看到变量部分改为显示属于每个范围的变量。另一方面,您可以在流程中的任何位置访问全局变量。要声明一个变量,请确保在添加变量时没有选择任何范围活动。我们已经提到,当描述接收活动时,将自动为流程操作的输入和输出参数创建全局变量。
在创建变量时,需要声明变量的类型,然后才能使用该变量。有两种类型的变量:数据类型和接口。数据类型变量 是一种业务对象或一种简单的类型,例如字符串或整数。接口变量 的类型基于 WSDL 接口文件中的输入或输出消息类型。如果查看一下前面的图 3,您就会看到 Use Data Type Variables 已选中。这可让我们将 order 业务对象设置为变量。
处理程序 是与特定活动或整体流程关联的一组活动。处理程序在特定情况发生时运行。处理程序的类型有:
- 错误
- 事件
- 补偿。
当流程在运行过程中出现问题或发生异常情况时,可以使用错误处理程序 撤消发生错误的范围中的部分工作或未成功完成的工作。错误处理程序是范围或调用活动的可选部分。通过右键单击一个范围或调用活动并选择 Add Fault Handler 可以创建一个错误处理程序。当运行时基础结构抛出错误时、调用服务时或者在抛出活动运行时会引发错误处理程序。
错误处理程序包含一个或多个捕获元素。可以为范围或活动中可能发生的每个错误添加一个捕获。每个捕获都包含一个序列,其中可以添加活动来执行需要对特定错误执行的工作。在这里可以使用补偿、重新抛出或空活动,还可以使用任何其他活动。您还可以添加一个 catch all 元素来处理所有捕获 catch 都未捕获到的任何错误。
作为一个示例,图 15 显示了 ShipOrder 活动的简单错误处理程序。图 14 显示了包含处理程序的流程的接口。通常,该流程使用 shippedOrder 业务对象进行应答,但是如果 ShipOrder 服务(其操作也含有 invalidCustId 错误)抛出 invalidCustId 错误,则该流程改为使用 invalidCustId 错误进行应答。
图 14. 带有错误的接口
图 15. 错误处理程序
我们已了解了如何使用接收活动和接收选择活动来接受对流程的调用。您可能会问:“如果流程没有停止并等待特定的操作调用该怎么办?”这时就用到了事件处理程序。事件处理程序 非常类似于异常处理程序,只不过它在 OnEvent 元素接收到对相应操作的调用时才运行。OnEvent 元素 相当于一个接收活动,因此在流程的某个接口需要相应的操作。
另一个可以添加到事件处理程序的元素是 Timeout。Timeout 的实现方式与等待活动的实现方式相同。不同的是事件处理程序中的 Timeout 独立于其他流程工作。
当流程长时间运行时,您可以向任何范围添加事件处理程序(长时间运行 是指流程不能被中断并且可以等待外部异步输入)。事件处理程序在活动进入范围时开始运行,并在活动退出范围时停止运行。
在图 16 中,如果对 OrderProcessing 接口调用 cancelOrder 操作,并且 CheckInventory 或 ShipOrder 活动正在运行,则 terminate 活动就会运行并且流程结束。
图 16. 事件处理程序
我们在讨论补偿活动时已经提到,补偿可让您撤消完成的活动。您可以向调用或范围活动添加补偿处理程序,以执行回退完成的工作所需的步骤。定义补偿处理程序的调用或范围活动必须完成,补偿处理程序才可以运行。因此,如果调用或范围抛出错误,则对该活动的补偿操作就无法运行,原因是它根本就没有完成其工作。一旦该活动安装了补偿处理程序,就可以使用补偿活动来调用该处理程序。
在补偿处理程序运行时,它好像是范围活动继续执行。也就是说,您有权访问范围中可视的变量,而且它们将包含与范围活动完成时相同的值。
图 17 显示了内部范围的补偿处理程序(其名称为 CallAndNotifyScope),并且 包含 CorrectNotify 活动。在外部范围中,错误处理程序包含一个其目标设置为内部范围的补偿活动。如果在内部范围完成之后发生了错误,并且错误处理程序在运行,则会运行 CorrectNotify 活动。
图 17. 补偿处理程序
在前面的图 1 中,我们介绍了具有两个接收操作的流程。您可能会问这样一个问题:“如果一个流程有多个实例在运行,那么如何知道是哪个实例将接收对第二个操作的调用?”如果您阅读了介绍业务状态机的第四篇文章,就会记得在向正在运行的状态机的多个实例发送消息(调用操作)时也会碰到相同的问题。为解决此问题,状态机使用了一个相关集。毫无疑问,这一概念同样适用于业务流程(我们已经提到,业务流程提供状态机的基础实现)。
相关集 可让您指定哪些消息属于哪些流程实例。它由相关性所基于的属性 和该属性的别名组成。别名 是表示相关属性的消息的一部分。例如,图 18 中的流程是图 1 中所示流程的相关集,该活动使用 orderNum 属性关联传入的请求。图 19 显示了创建的别名(单击 New 得到的结果),它是 Order 业务对象的 orderNumber 属性。在图 18 中,orderNumber 同时与 start 和 continue 操作调用相关联。请注意,对于每个别名,您可以使用不同的业务对象。重要的是,每个别名都具有相同的类型,在本例中是 int。
图 18. 相关集
图 19. 相关属性别名
具有相关集之后,您需要指定应用程序将使用哪些别名来初始化 相关集。这意味着,当任一别名包含相同的值时,就会使用接收该别名值的流程实例。图 20 显示了在 Start 活动运行时要初始化的相关集设置。
图 20. 使用相关集
现在让我们看一下在运行时它们是如何工作的,以便对其有个了解。假设应用程序调用一次 start 操作,并且 orderNumber 的值为 123。该流程接着再次调用 start,并且 orderNumber 的值为 124。然后,在应用程序调用 continue 时,如果 orderNumber 的值包含 123,则第一个流程实例将接收该调用。
用于相关性的属性可以随着流程的继续而更改。例如,您的流程在开始时可能与客户标识符相关联。以后,如果客户订购了商品,则流程的其余部分可能需要与订单号相关联。注意,在图 19 中我们选择了 Input 作为方向(因此在图 20 中将 Direction 设置为 Receive)。当接收到调用活动的应答时,您还可以选择使用 Output 来启动另一个相关集。
共有两种业务流程:长时间运行流程和微流程。微流程 是作为单一事务运行的业务流程。微流程是不可中断的,这意味着它不能包含等待活动、人工任务活动或者多个接收活动,原因是每个这样的活动都需要暂停流程。而且,它只能在活动上具有补偿,而非补偿程序,因为在流程运行时不保留状态。
另一方面,长时间运行 流程可以停止运行并允许所有功能。在流程运行时可以保留状态。如果重新启动服务器,流程仍会运行并且处于其停止时的同一状态。一般情况下,实现为长时间运行流程的组件称为非同步 组件,这意味着客户机可以调用它们,并且能够在其等待应答时继续执行其他工作。
在返回工作并构建业务流程之前,最后需要指出的一点是:WebSphere Integration Developer 支持 Business Process Execution Language for Web Services (BPEL4WS) 规范,而且还提高了方便性。如果希望确保您的流程能够在另一台 BPEL 兼容服务器上运行,则可以在使用新业务流程向导时通过选中 Disable WebSphere Process Server BPEL Extensions 禁用这些扩展。另外还需要注意的是,如果您没有发现该实现,除了我们介绍的右键单击一个组件并选择 Generate Implementation 外,您还可以使用向导创建实现。
下面列出了提供的一些增强:
- 嵌入式人工任务
- Java 表达式和 Java 代码片段
- 微流程
- Valid-from 设置
- 每个流程元素的其他属性,如显示名称、描述和文档。
现在让我们进行一些有趣的工作,使用我们所学的知识来增强订单处理应用程序——即您在第三篇文章组装的应用程序。我们使用一个简单的业务流程实现了 ShippingProcess 组件:它仅调用了 ShippingTask 服务,然后通知 ProcessOrder 组件 ShippingTask 服务发送了订单。让我们在此基础上做进一步构建,并在流程中使用更复杂的逻辑来实现它。如果您希望跳到浏览完整的业务流程,我们还提供一个可下载的完整应用程序(orderprocessingcompleteprocess.zip)。
假设您的老板告诉您可能无法立即发送订单中的所有商品。如果某件商品已脱销,公司的策略是发送可以提供的商品,然后在进货后再发送其余商品。但是,如果要发送的商品五天以后才能重新进货,则流程完成,并通知客户没有发送所有商品。
首先,让我们导入在第三篇文章中构建的项目。为了节省您的时间,并将精力集中在新业务流程上,该模块的最新版本已添加了一些其他必要接口和业务对象,因此,请确保在 orderprocessingemptyprocess.zip 文件中启动该项目。
-
请从下载部分下载
orderprocessingemptyprocess.zip文件。 - 单击 File => Import => Project Interchange,然后单击 Next。
-
单击 From zip file 字段旁边的 Browse。浏览到刚下载的
orderprocessingemptyprocess.zip文件,并单击 Open。此 zip 文件就是 Project Interchange 文件。 - 选中 OrderProcessing,并单击 Finish。OrderProcessing 模块在 Business Integration 视图中打开,而且在构建项目时,您可以在 Workbench 的右下角看到“Building workspace”消息。等待构建完成。
- 展开 OrderProcessing 模块,然后双击 OrderProcessing 以打开集成编辑器。
- 在集成编辑器中双击 ShippingProcess 组件,以打开业务流程编辑器。图 21 显示了要增强的业务流程。
图 21. ShippingProcess 业务流程
让我们快速重新调整一下图 21 中的内容。该业务流程遵循 Shipping 接口。您可以在 Business Integration 视图中双击 Shipping 接口,以在接口编辑器中打开它并进行浏览。您会看到它有一个操作 shipOrder,该操作采用 order 业务对象作为输入。因此,我们使用此接口这一事实意味着调用 Shipping 服务的 shipOrder 操作将调用该流程。Order 对象中包含的数据将能够在该流程中使用。
由于 ShippingOrder 组件连接到 ProcessOrder 和 ShippingTask 组件,因此生成的流程实现分别创建了两个引用合作伙伴 ProcessOrderPartner 和 ShippingTaskPartner。这可能会让您想起第三篇文章中的内容,您将使用这些合作伙伴对那些组件执行服务调用。
根据前面指定的规则,您需要让流程继续尝试发送订单中的商品,直到所有商品均已发送,或者直到重新进货的时间太长而无法等待。可以对此使用 While 循环活动。需要确定该循环何时退出,因此在后续步骤中,还要创建 shippingComplete 变量,在将该变量设置为 True 时将导致退出循环。在继续之前,请回顾一下第四篇文章中有关可视代码片段编辑器部分,因为我们不再详细描述创建可视循环条件所需的步骤。
在实现之前,我们需要在整个流程中使用一些全局变量。我们将通过下列步骤创建这些变量:
-
右键单击 Variables 部分,并选择 Add Variable,然后将名称更改为
shippingComplete。 - 在新变量的 Properties 视图的 Details 部分,确保选中了 Data Type,单击 Browse,然后从类型列表中选择 boolean。
-
利用相同的方式,创建类型为 ShippingFault 的
shippingFault变量。
请注意最后一步:在最初的 OrderProcessing 模块中,ShippingTask 服务返回 Order 业务对象,但该业务对象不包含建议的更改所需的一些信息,即发送的数量或重新进货信息。因此,在下载的新版本应用程序中,提供了一个包含 quantityShipped 和 restockDays 属性的新 ShippedOrder 业务对象。我们还更改了 ShippingTask 组件的接口,以便返回这个新业务对象。
接下来,您需要初始化该流程后面要使用的一些变量。
-
右键单击 ShippingTask 活动,然后选择 Insert Before => Assign。将名称更改为
Initialize。 -
在初始化活动的详细信息中的 From 列表中,选择 Fixed Value,然后为该值键入
False。 - 在 To 列表中,选择 Variable,然后选择 shippingComplete,如图 22 所示。
图 22. Initialize 赋值活动详细信息
刚才已经指出,shippingComplete 变量在流程通过 Receive 活动之后就会立即得到一个 False 值。
现在我们介绍流程的循环部分:
-
右键单击 ShippingTask 活动并选择 Insert Before => While Loop。将其重命名为
LoopUntilShipped。 - 在循环的 Properties 视图的 Details 部分,单击 Create a New Condition。
- 将 shippingComplete 变量从可视代码片段编辑器的 Variables 部分拖到画布。
- 右键单击编辑器并选择 Add => Standard,在逻辑类别下选择 inverse,单击 OK,然后单击编辑器画布。
- 删除 false 节点(因为已不需要该节点)。
- 将 shippingComplete 节点连接到 inverse 节点,将 inverse 节点连接 return 节点,您的循环条件应该与图 23 相同。
- 保存业务流程编辑器
图 23. 循环条件
现在的流程看上去应该如图 23 所示。您需要看一看图 29,以了解最终流程的外观,做到心中有数。需要 inverse 节点的原因是您想让循环在 shippingComplete 为 True 时退出,但该循环需要 False 值才能终止。LoopUntilShipped 中出现红色“x”号是因为循环中没有活动。出现此错误的原因是现在运行了一个无限循环。总之,如果循环中没有逻辑,就无法将 shippedOrder 设置为 True。
下列步骤将开始在循环中填写逻辑,以调用 ShippingTask 服务,然后确定是等待还是重试。由于我们已在以前的两篇文章中介绍了可视代码片段,因此这里就不再赘述创建代码片段的详细步骤。
- 将 ShippingTask 活动拖到 LoopUntilShipped 活动中。
-
在 LoopUntilShipped 活动中单击右键,选择 Add => Snippet,然后将其重命名为
CalcTotalShipped。 -
如图 24 所示填写可视代码片段逻辑。
add节点位于标准可视代码片段的数学类别下。
图 24. 计算发送量
现在已经有了一个执行发送工作的活动。由 ShippingTask 活动返回的值包含 ShippingTask 发送的商品数,因此该代码片段将使 quantityShipped 变量递增。您将使用该变量来确定在创建选择活动之后接下来要做什么:
-
在 Loop 活动中再次单击右键,选择 Add => Choice,然后将其重命名为
DetermineNextAction。 - 右键单击 DetermineNextAction 活动,选择 Add => Case,再次单击右键并选择 Add => Otherwise。
-
在第一个 Case 属性的 Description 选项卡中键入
NotEnoughShipped,然后在 Details 选项卡中单击 Create a New Condition。这会打开可视代码片段编辑器。 -
如图 25 所示填写代码片段逻辑。
and节点位于标准可视代码片段的逻辑类别下。 -
通过同样的方式,将第二种情况重命名为
EnoughShipped,然后如图 26 所示填写代码片段逻辑。
图 25. NotEnoughShipped 情况
图 26. EnoughShipped 情况
如果没有足够的商品发送,并且所剩的重新进货的天数小于五天,则第一个 case 元素的逻辑将返回 True。循环中的其他 case 元素处理完整订单发送的位置。没有为 Otherwise 元素定义条件,因为它是在前两种情况没有计算为 True 时将采用的路径。
下一步是为每种情况创建一些活动。事实上,如果您保存了该流程,就会在 NotEnoughShipped 情况旁边看到一个红色“x”号,因为至少需要有一个活动。下列步骤将为您的逻辑在选择活动中可以采用的每个路径填写活动。
如果 NotEnoughShipped 情况为 True,则需要等待一会儿,然后再次尝试发送其余商品。因此,我们需要添加一个等待活动。要发送其余的商品,我们只需再重复一次循环即可,因为调用发送组件的活动在循环的开始处。
- 右键单击 NotEnoughShipped case 元素并选择 Add => Wait。
- 在 Properties 视图的 Details 部分,选择 Java 作为表达语言,然后选择 Duration。
-
在创建的表达式中将值
0更改为10。此值是要等待的秒数。
在到达等待活动时,该流程将停止运行十秒钟,由于在等待之后没有其他活动,而且 shippingComplete 变量仍为 false,因此将重新执行下一个循环。在所有商品都发送之后,我们需要退出循环。要执行此任务,只需将 shippingComplete 设置为 True。让我们填写发送足够商品的情况:
-
右键单击 EnoughShipped case 元素,选择 Add => Assign,然后将名称更改为
SetShippingComplete。 -
使用与前面 Initialize 活动相同的方法,将 shippingComplete 变量设置为
True。
当前两种情况均为 False 时,剩下的唯一一种情况就是发送的商品超过订购的商品,或者是没有足够的商品发送,而且重新进货的时间又太长。这是一个问题,因此我们需要在此处抛出错误。为简便起见,我们只报告出现了一个问题。
-
在 Otherwise 元素下添加另一个 Assign 活动,并将其重命名为
SetErrorMsg。 -
在 Assign 活动的详细信息中,将 From 列表设置为 Fixed Value,并为固定值键入
"Shipping problem"(包括引号)。 - 将 To 列表设置为 Variable,并选择 shippingFault => message。
-
在赋值详细信息左下角单击 New,然后为 shippingComplete 变量的固定值
True创建另一个 Assignment 活动。(Assignment 活动可以有多个赋值。) - 在 SetErrorMsg 活动下添加抛出活动。
-
在抛出活动详细信息中,选择 User-defined 作为错误类型,保留缺省的命名空间,并将错误名称设置为
shippingFault。 - 对于错误变量,单击 Browse,并从列表中选择 shippingFault。
下一步是定义错误处理程序来捕获 shippingFault:
- 右键单击 LoopUntilShipped 活动,然后选择 Insert Before => Scope。
- 左键单击 LoopUntilShipped 活动,然后将其拖到范围活动中。
- 右键单击范围活动并选择 Add Fault Handler。
- 在错误处理程序的 Catch 元素的详细信息中,选择 User-defined 作为错误类型。请注意,单击范围活动右上角的橘红色“x”号可以在错误处理程序的显示和隐藏之间进行切换。
-
为 Fault Name 键入
ShippingFault,然后为 Variable 键入shippingFault。 - 浏览到数据类型的 ShippingFault。
- 在错误处理程序的 ShippingFault 元素下,添加 Snippet 活动,然后如图 27 所示填写详细信息。
图 27. 错误代码片段内容
最后一步是通知客户机已使用更新的订单值发送订单(发送的数量现在将包含实际发送的数量)。NotifyShipped 活动已经作为流程的最后活动存在。现在您的流程应如图 28 所示。
图 28. 完整的发送流程
现在让我们快速测试一下该组件。如果对第三篇文章中介绍的运行整个模块比较熟悉,则可以尝试运行该模块。这里我们仅为您提供快速测试 ShippingProcess 组件的步骤。
- 在 OrderProcessing 集成编辑器中右键单击 ShippingProcess 组件,并选择 Test Component。
-
在打开的测试客户机中,为 orderNumber 输入
1,为 quantityOrdered 输入10,将 quantityShipped 保留为0,并为 productID 和 customerID 输入任何值。 - 单击 Continue。在部署位置对话框打开时选择一台服务器并单击 Finish。
-
当
ShippingTask组件的测试客户机接收模拟事件时(因为您选择的是测试模块中的一个组件,缺省情况下将模拟其余组件),为 quantityShipped 输入9,为 restockDays 输入3。 - 单击 Continue。
此数据满足 NotEnoughShipped 情况,因此会导致运行 Wait 活动。十秒钟之后,循环再次运行。
-
当
ShippingTask组件再次接收模拟事件时,为 quantityShipped 输入1,将 restockDays 保留为0,然后单击 Continue。 -
当
ProcessOrder组件接收模拟事件时,只需单击 Continue。
图 29. 测试业务流程
这时便完成了该流程。它为客户发送了十件商品并通知了 ProcessOrder 组件。
业务流程是业务集成应用程序的关键部分。您可以使用它们定义业务逻辑的步骤,协调和编排其他服务以及与人员有关的活动。业务流程可以按顺序或并行执行它们的活动,执行期限可以是较短的时间,也可以是跨数天、数周或更长的时间。本文和上一篇关于业务状态机的文章向您介绍了两种定义应用程序逻辑的重要方法。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| OrderProcessing - incomplete business process | orderprocessingemptyprocess.zip | 95 KB | FTP |
| OrderProcessing - complete business process | orderprocessingcompleteprocess.zip | 97 KB | FTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 用于 Web 服务的业务流程执行语言(1.1 版)
- 业务流程: 理解 BPEL4WS,第 1 部分:业务流程中的概念
- WebSphere Integration Developer 产品信息
- WebSphere Process Server 产品信息
- WebSphere Process Server:IBM 为 SOA 提供的新基础
- Build a Hello World SOA application
- 服务组件体系结构
- Common Event Infrastructure
获得产品和技术
讨论

Richard Gregory 是 IBM Toronto Lab 的 WebSphere Integration Developer 团队的一名软件开发人员。他的主要职责包括 WebSphere Integration Developer 的改进及其测试工具的交付。

Jane 是 IBM Canada Ltd 的一名高级软件开发人员,她负责开发 WebSphere Integration Developer 中的 Business Process Executable Language (BPEL) 和 Business Rules 调试工具。以前,她曾担任 WebSphere Studio Technical Support 团队的负责人。Jane 于 2000 年获得了滑铁卢大学电子工程学学士学位。她具有丰富的写作经验,包括许多 developerWorks 文章。Jane 是 IBM Press 出版的 An Introduction to Rational Application Developer, A Guided Tour 一书主要作者。

Greg Adams 是优秀的 Eclipse 平台中用户界面首席架构师,最近又担任了核心 WebSphere Business Integration Tools (包括 WebSphere Studio Application Developer Integration Edition 和 WebSphere Integration Developer)的首席架构师和开发负责人。Greg 领导了 IBM 的第一个完整的面向服务的体系结构 (SOA) 工具组和第一个支持 Business Process Editor 的 BPEL4WS 标准的交付,这两个项目都是 IBM 的随需应变策略中非常关键的产品。
