在本系列的 上一部分中,您利用 WebSphere Integration Developer 构建了一个简单的面向服务的订单处理应用程序。您已经了解了如何结合使用其概念和工具来构造应用程序的构件。您使用业务状态机实现了一个组件 ProcessOrder,但对于在构建它时进行的具体操作只给出了非常少的背景信息。选择状态机来实现此组件的原因在于:对于每个订单,用于处理该订单的步骤取决于该订单的当前状态。
一般来说,如果组件的活动是事件驱动的,并且所发生的每个活动都取决于该组件的当前状态,那么状态是非常有用的。事件驱动 在这一情景中意味着该组件等待对其操作的调用来了解什么时候应当采取下一个步骤。现在应当深入介绍每个状态机概念,以帮助您了解可以利用哪些知识来构建您自己的更大、更可靠的状态机。
有深入介绍状态机概念之前,首先来快速介绍一下可视代码片段编辑器。在业务状态机中,以及在业务处理中,有时会需要自定义逻辑。例如,在您的进程可以确定其是应当沿一条特定路径前进,还是帮助您的状态机确定应进行哪一种状态变换时,经常需要计算一些业务数据。因为不要求 WebSphere Integration Developer 的用户具有关于任何编程语言的详尽知识,所以可视代码片段编辑器在这一情况下扮演着重要角色。
在构建自己的应用程序时,您可能希望在继续进行后续工作之前先测试各个部分,以确保它们能够按预期工作。在上一部分中已经看到测试应用程序是多么简单,甚至在它们中的一部分已经准备就绪之前也是如此简单。您已经从第一次运行某个应用程序的体验中了解到:非常可能出现问题。所以,我们将让您再次运行订单处理应用程序,但这一次将向您展示调试组件是多少容易。我们还将向您介绍一些在使用测试客户端测试状态机时的技巧。
在接下来的几个部分中,您将了解到以下各项内容:
- 可视代码片段编辑器及其特性。
- 业务状态机的所有部分。
- 如何调试一个状态机(或其任意组件)。
如果您已经尝试了上一部分中的示例,那么您已经使用过可视代码片段编辑器。如同业务状态机一样,我们没有花费大量的时间来研究此编辑器,所以现在让我们来更详细地了解一下此编辑器如何帮助您开发自己的应用程序。
无论您正在创建一个 BPEL 过程,还是一个业务状态机,或者是几种其他类型组件中的任意一种,您都需要定义自定义表达式和行为。在大多数情况下,需要使用 Java™ 来表示它。但如果您不了解编写 Java 代码的细节呢?或者,您也许了解 Java 编码,但不了解那些可在这一上下文中使用的方法的细节。
WebSphere Integration Developer 提供了一个可视代码片段编辑器,可以用来在不必编写 Java 程序的情况下定义自定义行为。可视代码片段编辑器使用一种图形视图,使您在概念级别工作,而不需要了解如何用文本 Java 代码来表示逻辑的细节。例如,图 1 显示了一个可视代码片段,其向 Java 控制台打印信息。请注意,为了获得 the customerID,您不需要了解打印 Java 的语法,也不必了解如何访问 Order 业务对象的属性。此图显示了 myOrder 变量的所有可用属性。该代码片段将myOrder 变量的 customerID 追加到字符串Shipping order to,然后将其打印到 Java 控制台。
图 1. 一个简单的可视代码片段
可视代码片段编辑器通常用于向一个关系图中添加节点,以执行所要求的工作。最简单的关系图可以由单个节点组成。您可以向关系图中添加多个节点。节点的相对位置非常重要,因为节点是按照由上至下的顺序执行的。
当节点执行时,它们常常需要计算一个值。节点右侧的终端代表结果值。回顾图 1,您可以将追加文本节点的计算值看作是由追加到字符串 Shipping order to. 的 customerID 所组成的值(一个字符串)。
类似地,节点可能需要一些特定值来执行。您通过将结果终端连接到输入终端来提供这些值。您可以扩展这一模式,以创建已连节点的整个组。例如,由追加文本 节点计算的值被作为输入传递给输出到日志 节点,因为我们将追加文本 终端连接到了该节点。
在大多数情况下,可以将可用节点集分为三类:表达式、代码片段和控制结构:
- 表达式 节点包含一个文本表达式。
- 代码片段 节点计算一个值或执行某一任务。
- 控制结构 节点在可视代码片段逻辑中产生分支或循环。
表达式节点支持包含算术和布尔(真或假逻辑)运算符的简单表达式。它们还支持以点分隔的表示法,以深入到数据结构中的值之内。例如,当从图 1 中的清单选择 myOrder 变量的customerID 属性时,此表达式显示为 myOrder.customerID。如果 customerID 也是一个业务对象(即包含其自己的属性),那么可以采用同一方式对其进行进一步扩展。
图 2 给出了其他表达式的一些示例。在第一示例中,变量total被递增 2.5 倍的 newvalue。在第二示例中,如果 checked 和 safe 两个变量均为 True,则将变量ok 指定为 True,否则它被指定为 False。
图 2. 一个显示变量赋值的简单表达式
如前面所述,表达式节点还支持简单的算术和逻辑表达式。最简单的表达式之一仅确定一个变量(或者,如果该变量是一个业务对象,则是一个变量的字段,而不仅仅是一个简单类型)。在这种情况下,该表达式同时具有输入终端和结果终端。被连接到此种表达式的输入终端的值表示对该变量或字段的赋值。该输入值可以是一个简单表达式,或者是由另一节点计算的一个值。图 2 显示了这一情景的两个示例。
在可视代码片段编辑器中有两种变量:局部的和全局的。局部变量 是一种仅能在声明该变量的代码片段中使用的变量。只需要创建一个仅包含变量名称的表达式,并为其指定一个取值,就可以声明一个局部变量。您所赋的值决定了该变量的类型,它可以是 Java 或 BO 类型。例如,图 3 显示了一个称为 status 的局部变量,其类型为 string,因为指定了一个字符串作为其取值。然后,该变量被用作追加文本节点的输入。
另一方面,全局变量 是一种可在同一组件内的任一代码片段中使用的变量。业务状态机或流程编辑器中的清单列表包含全局变量。因此,由于status 变量是一个局部变量,所以它不会显示在全局变量列表中,供业务状态机中其他代码片段使用。要创建一个全局变量,可以右键单击“Business Process editor”的“Variables”部分,然后单击 Add Variable。
图 3. 在代码片段中声明一个局部变量的示例
简单地说,代码片段表示和产生可执行代码。标准可视代码片段在将它们按类别进行组织的库中可供使用,如图 4 所示。回顾图 3,您可以看到 append 文本标准可视代码片段的使用。
图 4. 选择标准可视代码片段
在大多数情况下,现有标准可视代码片段很可能提供所有需要的通用功能。对于未能提供所需功能的情况,您可以选择任意 Java 方法、构造函数或字段,以便在代码片段中使用。图 5 显示了可以在其中选择一个可视代码片段的对话框,在本例中,选择了 Java 字符串类的 endsWith 方法。
图 5. 用于添加 Java 代码片段的对话框
图 6 说明了如何使用 endsWith 代码片段来检查msg 变量是否等于 some value 字符串,然后将该结果(True 或 False)存储在 isSomeValue 变量中。在图 5 中的对话框中,您还可以选择其他可视代码片段(或任意 Java 代码),它们可以是您自己所创建的,并且指定自定义行为。
图 6. 使用 Java 代码片段的示例
可视代码片段关系图也支持代码组的条件执行,可以通过添加条件控制结构来创建。这一结构需要一个 Boolean 输入。如果输入值为 True,则执行控制结构上部的代码组。否则,执行该结构下部的代码组。例如,在图 7 中,如果 list 包含 234DF, 则 found 打印,否则,missing 打印。
图 7. 使用条件控制结构的示例
对于甚至有点奇特的代码片段,您可以使用支持循环的控制结构。例如, for --
each 控制结构可以接受一个集合或数组作为输入,然后重复其中所包含的组,用于输入中的每一项。利用这一控制结构,可以计算一个订单清单中所有物品的总和,如图 8 中所示。
图 8. 使用循环控制结构的示例
前面已经总结了可视代码片段编辑器的每个特性。现在我们可以讨论业务状态机了,这是一个使用可视代码片段的典型之处。
在上一部分中曾经提到,业务状态机是一种特殊类型的业务流程。所以,我们的问题是,什么时候使用状态机,而不是业务流程?答案是,如果一个流程涉及对事件做出响应,并且其响应取决于该流程的当前状态,那么将流程实现为业务状态机可能非常有用。
术语状态机 听起来可能技术性很强,但其基于一种非常简单且易于理解的设计。WebSphere Integration Developer 中的状态机类似于 UML2 中定义的状态机。其思想是,流程将始终仅处于任意数目特定状态中的一种状态。状态机如何响应一个给定事件取决于该状态机的当前状态。
与大多数 WebSphere Integration Developer 组件一样,编写一个业务状态机意味着定义其接口及实现。这些接口共同定义了状态机将对其做出响应的操作集,在 SOA 术语中,它是由服务提供和由状态机组件实现的操作集。但是,请记住,并不是所有操作都对当前状态有效。例如,考虑图 9 中的状态机。您可以仅调用 placeOrder 操作来开始该流程,而且当其处于 OrderBeingShipped 状态时,只能调用 orderShipped。如果使用状态机服务的客户端为当前状态调用了错误的操作,则该客户端将接收一个错误,使其知道将发生什么情况。
图 9. 简单业务状态机
构成状态机接口的操作必须是请求-响应 类型。您使用请求 向状态机发送消息,以触发状态转换。除非您在一个操作中进行了设计(请参见下一部分),否则响应 不会返回任何数据,但有可能返回一个错误,比如在为当前状态调用错误操作时。所以,您需要具有至少一个作为该操作的一部分的错误。
与发送给业务状态机的每个请求操作一起提供的数据必须提供足够的信息,以确定哪个状态机实例应当是该数据的接收者。毕竟,可能会有数千个同时运行的实例,每个实例用于您的一位客户。随每个请求操作一起提供的数据也称为关联数据,所有状态机均需要它,稍后将对此进行解释。
业务状态机的一个实例在任意给定时刻只能处于一种状态。对于每一状态,可能存在与状态进入及退出相关联的操作。每当进入或退出该状态时,就会分别执行这些操作。组合状态 是包含其他状态的状态。三种特殊类型的状态是:初始状态、最终状态和终止状态。正如您可能猜测到的,初始状态 是一个状态机开始时所处的状态。最终状态 应当用于确定状态机的正常或期望完成状态,而终止状态 应当用于确定异常或意外完成状态。
状态机的初始状态必须具有单一传出转换。此转换必须具有一个称为初始化操作 的关联操作,如图 10 中所示。当调用此操作并发生该转换时,将创建此状态机的一个新实例。这一实例直到状态机到达最终或终止状态时才会结束。所有从一种状态到另一状态的其他转换都可以具有一些操作,在调用这些操作时,会导致向下一状态转换。您还可以具有在进入源状态或者因为超时或者因为没有触发操作的转换而自动触发的转换。
图 10. 带有初始化操作的初始状态
您还可以将一个条件(称为监护)与转换关联在一起。为使转换继续进行,此监护必须计算为 True。您还可以具有多个来自一个与同一操作关联(或不与任何操作关联)的状态的转换;但在此情况下,每个转换必须具有一个监护。监护 是一段代码(可视的或文本 Java),或者是属于一个转换的服务调用,其既可以为 True 也可以为 False。在即将发生一次状态变换时,返回 True 的监护确定所发生的转换。应当由您来确保这些监护中只有一个的状态计算为 True,并记住,无法保证哪个监护将首先被计算。图 11 显示了用于从 CustomerBeingChecked 状态转换到其他状态的监护。Good Customer 监护返回 isCustomerOK 变量的值(在CustomerBeingChecked 状态中,其被设置为 True 或 False ),而Bad Customer 监护返回相反的值。
图 11. 具有相关条件的转换
在发生转换时,它可以触发一个与此转换关联的操作。图 11 还显示了与这些监护一起的 Print Info 操作。您可以利用可视或文本 Java 代码来实现这些操作,或者将其实现为 SCA 服务的一个调用。请注意,关于转换的条件和操作可以访问属于触发操作的消息部分。
例如,假定 Order 业务对象是触发一个转换的操作的输入。于是,您可以直接在此操作中访问 Order 对象,它是该转换的一部分。如果您希望在状态机的其他位置使用 Order,比如要在稍后的一个状态中使用,可以将 Order 的值赋值给一个全局变量。您还可以使用一个操作将值指定给用于回应操作的消息的部分(接口的输出部分)。
一个转换的目标状态可以与其源状态相同。这称为自转换。在状态机编辑器中,您可以将自转换标记为该转换属性中的内部。这意味着状态进入或退出操作将不会被转换触发。
您可以触发来自一个组合状态的转换,而不用考虑此组合状态中的哪个状态是当前状态。组合状态还可以具有不带操作的缺省转换。当机器到达一个组合状态内的最终状态时,它自动触发此缺省转换。
前面我们提到,必须为状态机接口的每一个操作都指定相关信息。为此,您可以执行以下两个步骤:
- 指定将出现在所有操作中的属性。
- 对于每个操作,指定将用于为该属性提供取值的部分。
为了表达得更为清楚,图 12 显示了具有int 类型的 OrderNumber 属性。每个作为状态机接口一部分的操作(消息)都具有一个部分,其被指定为 OrderNumber 属性的一个别名。这意味着当您调用 placeOrder 或 orderShipped 操作时,Order 业务对象的 orderNumber 属性的值(它是作为输入参数的一部分被获得的)确定状态机实例将使用哪一状态。
图 12. 将一个消息部分与相关属性关联
让我们再次运行和测试该应用程序,但这一次我们将“调试”我们的应用程序。此应用程序没有任何错误,至少我们不知道有任何错误,但如果存在错误,将有助于了解如何跟踪在什么位置发生错误。我们已经在本文的末尾提供了第 3 部分中的模块供您下载,以免您的工作区中没有该模块。请记住,我们在此处所进行的测试和调试对于任何组件类型都是有用的,而不是仅仅针对业务状态机。
首先,请回顾此应用程序,以便您可以了解在其运行时发生什么情况。如图 13 所示,订单处理应用程序 (OrderProcessing) 从外部客户接收订单信息。然后将检查客户资信状态是否良好(最好是极为富有),如果是,则将订单转发到配送部门。当配送部门确认了订单已配送后,应用程序将输出一条消息。
图 13. OrderProcessing 模块
为了使各项工作准备就绪,在一个新的工作区中启动 WebSphere Integration Developer,并采用以下步骤导入所下载的文件:
- 下载下载部分中的 orderprocessing.zip 文件。
- 选择 File - Import - Project Interchange,然后单击 Next。
- 单击 From zip file 字段旁边的 Browse。浏览到刚刚下载的 orderprocessing.zip 文件,并单击 Open。此 zip 文件是 Project Interchange 文件。
-
检查 SimpleOrderProcessing,并单击 Finish。SimpleOrderProcessing 模块在 Business Integration 视图中打开,而且在生成项目时,您可以在 Workbench 的右下角看到
Building workspace消息。
project interchange 文件是一个 zip 文件,其中包含为一组模块编写的构件。编写的构件 的意思是指为应用程序创建的元件,例如组件或业务对象。这与生成的构件(如 Java 类文件)相对。project interchange 文件包含 WebSphere Integration Developer 重新创建工作区中每一模块所需要的足够信息。对于那些不能访问公共源代码库或版本控制系统的开发人员,在他们之间共享模块是非常有用的。您还可以利用以下步骤,将任意模块作为项目交换文件导出:
- 在工作区完成构建之后,展开 Business Integration 视图中的 SimpleOrderProcessing,并逐层展开到 Business Logic - State Machines - ProcessOrder。
- 要为 ProcessOrder 组件打开业务状态机编辑器,请双击 ProcessOrder。
- 右键单击 OrderBeingShipped 状态,然后单击 Debug - Add Entry Breakpoint。
如图 14 所示,一个蓝点显示在 OrderBeingShipped 组件的右上角。这意味着,在进入此状态时,状态机将停止运行。然后,您可以检查甚至改变它们在该点退出时的变量内容。当在一个断点停止时,您还可以检查执行堆栈。堆栈 包含对每个已经被调用到该点的组件的跟踪。
图 14. 在状态机中设置一个断点
我们只希望碰到状态机中的断点,所以对于运行其他组件不感兴趣。如果您正在开发组件,但它们还没有为测试做好准备,而您又希望测试模块中的其他组件,则下面是一个用于这种情景的方便技巧:每次选择程序集编辑器中的 Test Component(s) 时,未选择的组件(在本例中是除 ProcessOrder 组件之外的所有组件)将被模拟。由第 3 部分可以想起,模拟意味着测试客户端不是调用用于一个组件的服务,而是显示被发送给该服务的输入,然后提示您进入输出。您还可以定义将自动运行的模拟器,甚至可以做得更好一些,向模拟器添加一些逻辑,以根据输入返回不同的值(举例来说)。
在模拟状态机中的所有引用时,另一个方面也非常重要。从第 3 部分可以知道,当调用 placeOrder 操作时,将最终调用 orderShipped 操作,以触发到OrderComplete 状态的转换。但是,这一次不会发生这种情况。因为您将模拟 Shipping 组件,所以该组件将不能对 orderShipped 操作进行预期调用。模拟器不知道它正在模拟的组件将要进行什么操作,所以它没有办法进行任何服务调用。所以,乍看起来,当您以这种方式进行测试时,似乎您将永远处于 OrderBeingShipped 状态。现在,您需要做的事情就是再次使用测试客户端来为您进行 orderShipped 服务调用。我们将向您介绍在通过断点之后进行该调用的正确方式。
现在,我们已经设置了一个断点,可以按照以下步骤到达该断点:
要打开程序集编辑器,双击 SimpleOrderProcessing 项目中的 SimpleOrderProcessing。
右击 ProcessOrder 组件,并单击 Test Component。
对于该操作,选择 placeOrder,然后填充一些值,如图 15 所示。
图 15. 为 placeOrder 操作设置输入
要开始测试,请单击 Continue。
当 Deployment Location 对话框打开时,请确保选择了 WebSphere Process Server v6.0。
为此模式选择 Debug,如图 16 所示。这一点非常重要,因为如果模式被设置为“Run”,您的断点将被忽略。
图 16. 选择部署位置
单击 Finish。
为输入使用什么值并没有关系,但 orderNumber 例外。回顾对 ProcessOrder 状态机的调用与 orderNumber 关联的情况,所以您输入的值非常重要,当调用其他 ProcessOrder 操作时必须使用相同值,正如您使用测试客户端发送一个 orderShipped 消息时将会看到的。
状态机开始于 WaitingForOrder 状态,然后您对 placeOrder 操作进行的调用导致转换到 CustomerBeingChecked 状态。在状态输入时有一个操作,用以通过调用 CustomerInformationPartner 检查客户信息。从 CustomerInformation 服务返回的值被保存在 isCustomerOK 变量中。但是,因为我们没有选择测试此组件,所以它将被模拟,它是在模拟器中输入的值,存储在 isCustomerOK 中。下面的步骤说明如何进行此操作:
-
当测试客户端停止在 CustomerInformationPartner 的手动模拟事件时,为 Output参数键入
true。 - 要返回该值,请单击继续。
从 CustomerBeingChecked 状态开始的转换都是自动的(不存在触发其中任何一种状态的操作),所发生的转换取决于 isCustomerOK 的值,它在模拟器完成之后刚刚被设置为 True。发生到 OrderBeingShipped 状态的转换(如在进入 OrderBeingShipped 状态时),发生两件事情:对 Shipping 服务进行调用,对这一服务也进行模拟,并且到达断点(这意味着在该断点处停止执行)。下面的步骤说明此时如何检查这些变量:
- 当测试客户端在下一个手动模拟事件停止时(它在遇到断点并且进入该状态时发生,所以它可能发生在遇到断点之前或之后),单击 Continue。没有要输入的返回值;在一些后续步骤中,我将会告诉您如何触发下一状态变化。
- 当系统提示您切换到调试透视图时,单击 Yes。
- 在 Variables 视图中,展开 placeOrder_Input_order 和 isCustomerOK。
现在,您可以看到您的状态机变量的值,同时处于 OrderBeingShipped 状态,如图 17 所示。请注意,蓝色的断点标记被突出显示为黄色,以指示已经到达该断点。作为 Order 业务对象的操作输入而输入的值出现在 Variables 视图中,该值也就是您在模拟器中设置的值,其保存在 isCustomerOK 变量中。还显示一些内部变量值;您一般不需要关心这些变量值。
如果在遇到断点之前有很多组件调用,将会在 Debug 视图中看到调用堆栈。此时,一个组件内只有一个服务,所以在图 17 中,在堆栈中仅看到一件物品,ProcessOrder.OrderBeingShipped。
图 17. 在 OrderBeingShipped 状态中检查变量
正如前面所讨论的,当您让状态机继续通过断点时,它将停留在 OrderBeingShipped 状态,直到调用 orderShipped 操作为止。下一组步骤说明如何使用测试客户端触发到 OrderComplete 状态的转换。请记住,您需要让状态机知道您正在将消息发送到哪个运行实例。因此,您需要确保在调用 placeOrder 操作时,使用了相同的关联 ID。Order 业务对象中的 orderNumber 值(它用作这两个操作的输入)都已经被设置为关联 ID。
- 右键单击 Debug 视图中的 ProcessOrder.OrderBeingShipped,并选择 Resume。状态机再次运行,并等待对 orderShipped 的调用。请记住,如果第二手动模拟事件在到达该断点之后发生,则您仍然需要在测试客户端中单击 Continue。
-
切换回测试用户端,单击 Invoke 图标
。
-
对于此操作,选择 orderShipped。为 orderNumer 输入
15,然后为此操作的其余输入键入任意值。您还为placeOrder操作输入了值 15,它用作相关 ID,以确保对正确实例进行调用。 - 单击 Continue。
状态机现在转换到 OrderComplete 状态,然后该实例完成。您应当看到,Order has been shipped 和 Order is complete 打印在控制台中。
在本文中,您了解到如何通过 WebSphere Integration Developer 使用业务状态机来实现组件。您还了解到如何使用可视代码片段编辑器向状态机中添加更多详细逻辑。然后,您了解了如何测试和调试一个应用程序,包括一些测试状态机的要点。请阅读下一部分,在这篇文章中,您将了解到如何使用业务流程来实现一个组件。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| Project interchange file | orderprocessing.zip | 24 KB | FTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- WebSphere Integration Developer 指导教程,第 1 部分:WebSphere Integration Developer 概览
- WebSphere Integration Developer 指导教程,第 2 部分:使用 WebSphere Integration Developer 进行 SOA 开发
- WebSphere Integration Developer 指导教程,第 3 部分:构建面向服务的简单应用程序
- Web Services and Web Service Description Language
- 使用服务组件体系结构构建 SOA 解决方案
- Tuscany Service Component Architecture
- developerWorks: WebSphere Process Server and WebSphere Integration Developer resources
- WebSphere Integration Developer product information
- developerWorks: WebSphere 业务集成专栏
- developerWorks: WebSphere development tools zone
- 专家访谈: Paul Pacholski 谈 WebSphere Integration Developer
获得产品和技术
讨论

Randy Giffen 是 IBM Ottawa Lab 的 WebSphere Integration Developer 团队的一名顾问软件开发人员。他负责 WebSphere Integration Developer 的业务状态机工具和可视代码片段编辑器部分。在此之前,他曾是 WebSphere Studio Application Developer Integration Edition、Eclipse 和 VisualAge for Java 的用户界面团队的一名成员。

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

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