IBMChinaSkip to main content
搜索帮助
      IBM 主页   |   产品与服务   |   支持与下载   |   个性化服务

IBM : developerWorks 中国网站 : XML & Web services : 所有的文章
developerWorks 中国网站
Web 服务的业务流程执行语言英文原文

版本 1.0

2002 年 8 月 9 日

作者(按字母顺序):

Francisco Curbera,IBM
Yaron Goland,BEA Systems
Johannes Klein,Microsoft
Frank Leymann,IBM
Dieter Roller,IBM
Satish Thatte,Microsoft(编辑)
Sanjiva Weerawarana,IBM

Copyright 2001-2002 BEA Systems, International Business Machines Corporation, Microsoft Corporation, Inc. All rights reserved.

提供、分发或以其它方式传播本规范所包含的信息并未授予您使用 IBM 或 Microsoft 或 BEA 和/或任何其它第三方所拥有或控制的知识产权的许可证(无论是明示的还是默示的)。IBM、Microsoft、BEA 和/或任何其它第三方可能拥有与本文档内容有关的各项专利权、专利应用权、商标权、版权或其它知识产权。提供本文档并未授予用户使用 IBM 或 Microsoft 或 BEA 或任何其它第三方的专利、商标、版权或其它知识产权的任何许可证。此处举例所用的公司、组织、产品、域名、电子邮件地址、徽标、人员、地点和事件均属虚构。无意也不应推测为与任何真实的公司、组织、产品、域名、电子邮件地址、徽标、人员、地点或事件有任何关联。

此处包含的规范和信息以“按现状”的基础提供,并在适用法律许可的最大范围内被允许,IBM 和 Microsoft 和 BEA 以“按现状并可能存在各种错误”的基础提供本文档,特此声明免除所有(无论是明示的、默示的,还是法定的)其它保证和条件,包括(但不限于)对与本文档有关的适销性、适用于某特定用途、响应的准确性或完整性、结果、技艺精湛的成果、无病毒和无疏忽的默示保证、责任或条件(如果有的话)。此外,对所有权、平静享用、平静占有、与描述相符或与本文档有关的知识产权的非侵权性不提供任何保证或条件。

在任何情况下,IBM 或 MICROSOFT 或 BEA 都不对任何其它方获取替代产品或服务的费用、利润损失、使用损失、数据丢失、或者任何意外的、有连带关系的、直接的、间接的或特殊的损害负责,不管这类损害是由合同、侵权或保证引起的,还是由此外的方式或与本文档有关的任何其它协定引起的,也不管该方是否已被预先告知可能发生这类损害。

摘要

本文为指定基于 Web 服务的业务流程行为定义了一种表示方法。这种表示方法被称为 Web 服务的业务流程执行语言(Business Process Execution Language for Web Services)(以下简称为 BPEL4WS)。用 BPEL4WS 表示的流程只能通过使用 Web 服务接口来导出和导入功能。

描述业务流程的方式有两种。可执行业务流程可以模拟业务交互中的参与者的实际行为。相对而言,业务协议使用流程描述来指定涉及协议的每一方能相互看到的消息交换行为,但并不公开他们的内部行为。业务协议的流程描述被称为抽象流程。BPEL4WS 应被用来模拟可执行流程和抽象流程的行为。

BPEL4WS 提供了正式指定业务流程和业务交互协议的语言。这样做是为了扩展 Web 服务交互模型并使它支持业务事务。BPEL4WS 所定义的可互操作的集成模型应该能够促进在企业内和企业间的自动流程集成的扩展。

状态

这里发布的是 BPEL4WS 规范的最初的公开草案。我们期待着不少扩展被增加到 BPEL4WS 的功能集(在本文的末尾将简要地讨论这些扩展)。BPEL4WS 代表了 XLANGWSFL 规范中的概念的融合。BPEL4WS 规范取代了 XLANG 和 WSFL。

BPEL4WS 和相关规范以“按现状”的基础提供,仅供审阅和评估。IBM、Microsoft 和 BEA 希望在不久的将来向您征稿,并得到您的建议。IBM、Microsoft 和 BEA 都不以任何方式作出关于这些规范的保证或表示。

内容


1. 引言

2. 词语约定

3. 与 WSDL 的关系

4. 定义业务流程
    4.1. 第一个示例
    4.2. 业务流程的结构
    4.3. 语言的可扩展性
    4.4. 业务流程的生命周期

5. 服务链接、伙伴和服务引用
    5.1. 服务链接
    5.2. 伙伴
    5.3. 服务引用

6. 消息属性
    6.1. 动机
    6.2. 定义属性

7. 相关性
    7.1. 消息相关性
    7.2. 定义和使用相关集

8. 数据处理
    8.1. 表达式
        8.1.1. 布尔表达式
        8.1.2. 值为截止期限的表达式
        8.1.3. 值为持续时间的表达式
        8.1.4. 一般表达式
    8.2. 容器
    8.3. 赋值
        8.3.1. 赋值的原子性
        8.3.2. 赋值示例
    8.4. 抽象流程与可执行流程间的差别总结

9. 基本活动
    9.1. 每个活动的标准属性
    9.2. 每个活动的标准元素
    9.3. 调用 Web 服务操作
    9.4. 提供 Web 服务操作
    9.5. 更新容器内容
    9.6. 发出故障信号
    9.7. 终止服务实例
    9.8. 等待
    9.9. 不做任何事

10. 结构化的活动
    10.1. Sequence
    10.2. Switch
    10.3. While
    10.4. Pick
    10.5. Flow
        10.5.1. 链接语义
        10.5.2. 死路删除(Dead-Path-Elimination,DPE)
        10.5.3. Flow 图的示例
        10.5.4. 链接和结构化的活动

11. 作用域
    11.1. 业务流程中的错误处理
    11.2. 补偿处理程序
        11.2.1. 定义补偿处理程序
        11.2.2. 调用补偿处理程序
    11.3. 故障处理程序
        11.3.1. 隐式故障处理程序和补偿处理程序
        11.3.2. 活动终止的语义
        11.3.3. 故障处理程序和补偿处理程序中的故障处理
    11.4. 可序列化的作用域

12. 示例
    12.1. 运输服务
        12.1.1. 服务描述
        12.1.2. 消息属性
        12.1.3. 流程
    12.2. 贷款审批
        12.2.1. 服务描述
        12.2.2. 流程
    12.3. 多个启动活动
        12.3.1. 服务描述
        12.3.2. 流程

13. 未来的发展方向
    13.1. 作用域
        13.1.1. 容器
        13.1.2. 事件处理程序
        13.1.3. 重叠作用域
        13.1.4. 原子作用域
        13.1.5. 补偿
    13.2. 生命周期和查询
        13.2.1. 暂挂/恢复
        13.2.2. 查询
    13.3. 服务组成
    13.4. 与 WS-Transaction 规范的关系

14. 安全性注意事项

15. 致谢

16. 参考资料

附录 A 标准故障

附录 B 属性和缺省值

附录 C 协调协议
    BPEL4WS 作用域的协调协议

附录 D - XSD 模式
    BPEL4WS 模式
    服务链接类型模式
    服务引用模式
    消息属性模式

1. 引言

Web 服务的努力目标是通过使用 Web 标准实现应用程序间的通用的互操作性。Web 服务使用松散耦合的集成模型以支持各种领域(包括企业到消费者、企业到企业和企业应用程序集成)中的各种系统的灵活集成。以下基本规范最初定义了 Web 服务空间:SOAP、Web 服务描述语言(Web Services Description Language,WSDL)和统一描述、发现和集成(Universal Description,Discovery,and Integration,UDDI)。SOAP 为基本服务的互操作性定义了 XML 消息传递协议。WSDL 采用了用于描述服务的公共语法。UDDI 为系统地发布和发现服务提供了所需的基础结构。这些规范共同使应用程序遵循一个松散耦合、与平台无关的模型来找到对方并进行交互。

系统集成需要的不仅仅是通过使用标准协议来进行简单交互的能力。仅当应用程序和业务流程能够通过使用标准流程集成模型来集成复杂的交互时才能发挥 Web 服务作为集成平台的全部潜力。WSDL 所直接支持的交互模型仅仅是同步或不相关的异步交互的无状态模型。业务交互的模型通常假设在涉及双方或多方的有状态的长期运行的交互中的同步和异步对等消息交换序列。为了定义这种业务交互,需要对业务流程在其交互中所用的消息交换协议的正式描述。这种业务协议的定义涉及准确地指定涉及协议的每一方都能相互看到的消息交换行为但并不公开它们的内部实现。出于两个原因,需要把业务流程行为分为公共部分和内部部分(或私有部分)。一个原因是企业显然不想让它的业务伙伴知道它的所有的内部决策和数据管理。另一个原因是(即便并不是这种情况)通过把公共流程和私有流程分开,您能够改变流程实现的私有部分而不会影响到公共业务协议。

必须用平台无关的方式来明确地描述业务协议,业务协议必须包括在跨企业业务中的所有重要行为部分。这样,每位参与者可以理解业务协议并为遵守它而作准备,每位参与者不必进行人工协商,目前,人工协商的过程大大增加了建立跨企业自动业务流程的难度。

描述业务协议需要哪些概念?这些概念与描述可执行流程所需的概念之间是什么关系?为了回答这些问题,请从以下几个方面来考虑:

  • 业务协议总是包括数据相关的行为。例如,供应链协议依赖于这样的数据:定单中的单项产品的数量、定单的总价或交付的截止期限。在这些情况下定义业务目的需要使用条件和超时构造。

  • 对于业务协议来说,指定异常条件及其后果(包括恢复序列)的能力至少与定义“一切正常运行”时的行为的能力一样重要。

  • 长期运行的交互包括多个工作单元,这些单元常常是嵌套的,每个单元有自己的数据要求。业务协议常常要求颗粒度不同的工作单元的结果(成功或失败)的跨伙伴协调。

如果我们想为跨企业业务协议提供准确的可预测的服务行为描述,那么我们需要一种丰富的流程描述表示方法,它的许多特点使我们想起了可执行语言。公共消息交换协议与可执行内部流程的主要区别是内部流程用丰富的私有方式来处理数据,但在公共协议中并不需要描述这些数据。

在考虑业务协议的数据处理部分时,对比网络通信协议和业务协议是有益的。网络协议定义了在通信线路上传输的协议信封的形式和内容,这些协议所描述的协议行为完全由这些信封中的数据来决定。换句话说,在有关协议的数据和“有效负载”数据之间存在清楚的物理分界线。在业务协议中,这种分界线是模糊的,这是因为有关协议的数据往往被嵌入在其它应用程序数据中。

BPEL4WS 使用消息属性这个概念来识别嵌入在消息中的有关协议的数据。属性可被看作有关公共部分的“透明的”数据,与之相对的是内部/私有函数使用的“不透明的”数据。透明的数据直接影响公共业务协议,不透明的数据主要是对后端系统有重要意义,它影响业务协议的唯一方式是产生不确定性,因为它影响决定的方式是不透明的。我们的原则是被用来影响业务协议的行为的任何数据必须是透明的,所以必须被视为属性。

不透明的数据的隐式影响表现为涉及业务协议的服务行为中的不确定性。请考虑购买协议的示例。卖方的服务接收购买定单并根据多个标准(包括商品是否有货和买方的信用)作出接受或拒绝的响应。显然,决定过程是不透明的,但是决定的事实必须在外部业务协议中作为行为的多种选择被反映出来。换句话说,协议要求在卖方的服务行为中有类似 switch 活动的东西但分支的选择是不确定的。通过把不确定的或不透明的值(通常是从可能的值的枚举集中选出)赋给消息属性,可以模拟这种不确定性。这样,属性可被用于定义表示各种行为选择的条件行为而不公开实际决策过程。为了表示公共行为的本质同时隐藏私有部分,BPEL4WS 显式地允许使用不确定的数值。

定义业务协议和定义可执行业务流程所需的概念非常相似。定义业务协议所需的概念和定义可执行业务流程的所需的概念组成了统一体,BPEL4WS 被设计成包括这个统一体。BPEL4WS 所定义的模型和语法可被用于描述基于流程和它的伙伴间的交互的业务流程的行为。与每个伙伴的交互是通过 Web 服务接口进行的,在接口级别上关系的结构被封装在我们称之为的服务链接中。BPEL4WS 流程定义了与这些伙伴交互的多个服务交互是怎样协调的以达到业务目的,还定义了这种协调所需的状态和逻辑。BPEL4WS 还引入了一些系统的机制来处理业务异常和流程处理故障。最后,BPEL4WS 引入了一种机制,以用于定义在发生异常时或伙伴请求撤销时流程中单个或合成活动是怎样被补偿的。

应用 BPEL4WS 的基本概念的方式有两种。通过使用抽象流程概念,BPEL4WS 流程可以定义业务协议角色。例如,在供应链协议中,买卖双方是两个不同的角色,双方都有自己的抽象流程。他们的关系通常被模拟成服务链接。抽象流程使用所有的 BPEL4WS 概念但它对待数据处理的方式反映了描述业务协议公共部分所需的抽象程度。具体地说,抽象流程仅处理有关协议的数据。BPEL4WS 提供了把有关协议的数据识别为消息属性的方式。另外,抽象流程使用不确定的数值来隐藏行为的私有部分。

BPEL4WS 也可被用来定义可执行业务流程。流程的逻辑和状态决定了在每个业务伙伴那里进行的 Web 服务交互的性质和顺序,从而决定了交互协议。虽然从私有实现的角度来看并不需要完整地定义 BPEL4WS 流程,但是 BPEL4WS 为仅依赖于 Web 服务资源和 XML 数据的业务流程有效地定义了可移植的执行格式。此外,这种流程的执行以及与它们的伙伴交互的方式是一致的,与托管环境的实现所用的支持平台或编程模型无关。

即便在私有实现部分使用平台相关的功能的情况下(这在许多情况下是很有可能的,在多数的实际情况下更有可能),BPEL4WS 中抽象流程和可执行流程间的基本概念的模型的连续性可能将包含在业务协议中的公共部分作为流程或角色模板进行输出和输入,同时保持协议的目的和结构。从充分利用 Web 服务的角度来看,可以论证这是使用 BPEL4WS 的最有吸引力的前景,因为它支持大大提高自动化程度的工具和其它技术的开发从而降低了建立跨企业自动的业务流程的成本。

BPEL4WS 位于几个 XML 规范之上:WSDL 1.1、XML Schema 1.0 和 XPath1.0。WSDL 消息和 XML Schema 类型定义提供了 BPEL4WS 流程所用的数据模型。XPath 为数据处理提供支持。所有的外部资源和伙伴被表示成 WSDL 服务。BPEL4WS 所提供的可扩展性能支持这些标准的未来版本,即用于 XML 计算的 XPath 和相关标准。

2. 词语约定

本文中的关键字“必须(MUST)”、“绝不可以(MUST NOT)”、“需要的(REQUIRED)”、“应该(SHALL)”、“将不(SHALL NOT)”、“应该(SHOULD)”、“不应该(SHOULD NOT)”、“推荐的(RECOMMENDED)”、“可以(MAY)”和“可选的(OPTIONAL)”将按 RFC2119 [13] 中的描述来解释。

名称空间 URI(常规形式是“some-URI”)表示 RFC2396 [14] 中定义的与应用程序相关或与内容相关的某个 URI。

本规范使用非正式的语法来描述 XML 片段的 XML 语法,如下:

  • 本语法以 XML 实例的形式出现,但其中的值代表数据类型而不是值。

  • 粗体显示的语法是还未在本文中介绍过的语法,或在示例中有特别的意义。

  • <-- description --> 是某些“其它”名称空间的元素的占位符(象 XSD 中的 ##other)。

  • 字符按以下方式被附加到元素、属性和 <!-- descriptions -->:“?”(0 个或 1 个)、“*”(0 个或更多个)、“+”(1 个或更多个)。字符“[”和“]”用以表示所包含的项应作为一个与“?”、“*”或“+”字符有关的组被处理。

  • 被“|”分隔且被“(”和“)”并排在一起的元素和属性应被理解为语法的候选式。

  • XML 名称空间前缀(在下文中定义)被用来指出被定义的元素的名称空间。

  • 以 <?xml 开头的示例包含足够的信息,这些示例符合本规范;其它示例是片断,需指定更多信息才能符合本规范。

所提供的 XSD schema 和 WSDL 定义是语法的正式定义 [xml-schema1][WSDL]

3. 与 WSDL 的关系

BPEL4WS 依赖于以下基于 XML 的规范:WSDL 1.1、XML Schema 1.0 和 XPath 1.0。在这些规范中,WSDL 对 BPEL4WS 语言的影响最大。BPEL4WS 流程模型位于由 WSDL 1.1 所定义的服务模型之上。位于 BPEL4WS 流程模型核心的是由 WSDL 描述的服务间的对等交互概念;流程及其伙伴都被建模成 WSDL 服务。业务流程定义了怎样协调流程实例与它的伙伴间的交互。在这个意义上,一个 BPEL4WS 流程定义提供和/或使用一个或多个 WSDL 服务,还通过 Web 服务接口提供流程实例相对于它的伙伴和资源的行为和交互的描述。也就是说,BPEL4WS 定义了交互中某个角色的业务流程遵守的消息交换协议。

BPEL4WS 业务流程的定义也遵循 WSDL 的分离模型,即把业务流程使用的抽象消息内容与部署信息(消息和 portType 与绑定和地址信息)分开。具体地说,BPEL4WS 流程用抽象 WSDL 接口(portType 和操作)来表示所有的伙伴以及与这些伙伴的交互;它并不引用流程实例使用的实际服务。BPEL4WS 流程是可重用的定义,可以不同的方式在不同的情况下被部署同时在它们之间保持一致的应用程序级别的行为。请注意,BPEL4WS 流程的部署的描述超出了本规范的范围。

4. 定义业务流程

描述业务流程的方式有两种。可执行业务流程模拟业务交互中的参与者的实际行为。在可执行流程中,并不把业务流程分成从外部可看见的(或者说“公共”)部分和内部部分。相对而言,业务协议使用的流程描述指定了涉及协议的每一方的相互可以看见的消息交换行为并隐藏它们的内部行为。涉及业务协议的流程被称为抽象流程。一般来说,抽象流程是不可执行的。它们应被用来耦合 Web 服务接口定义与行为规范,这些行为规范既被用于约束业务角色的实现,也被用于以准确的词汇来定义业务协议中的每一方可以期望的对方行为。BPEL4WS 应被用来定义这两种流程。两者之间的差异限于这两种流程中用于数据处理的不同功能集。在数据处理这一节中,这些差异被准确地定义。

4.1. 第一个示例

在详细描述业务流程的结构之前,这一节先讲述一个用于处理购买定单的 BPEL4WS 流程的简单的示例。这样做的目的是为了介绍 BPEL4WS 的最基本的结构和一些基本概念。

这个很简单的流程操作被表示在下图中。带点的线表示序列。任意地把序列组成一组表示并发的序列。实心箭头表示用于并发活动间的同步的控制链接。请注意这张图并不是 BPEL4WS 流程的正式图解表示法。这张非正式的图被用来帮助读者理解。

当收到客户的购买定单后,流程初始化三个并行的任务:计算定单的最终价格、选择承运人以及为定单安排生产和运输。虽然有些处理可以并行地进行,但是三个任务之间存在相互依赖的控制和数据。具体地说,在计算最终价格时需要运输价格,在全面安排实现计划时需要运输日期。在完成这三个任务后就可以开始处理发票并把发票交给客户。

在下面的 WSDL 文档中显示了由服务提供给它的客户的 WSDL portType(purchaseOrderPT)。为了简单起见,业务流程所需的其它 WSDL 定义被包含在同一个 WSDL 文档中;具体地说,该文档还定义了一些 Web 服务的 portTypes,这些 Web 服务提供价格计算、运输选择和调度以及生产调度功能。请注意在 WSDL 文档中没有 bindings 或 service 元素。通过仅仅引用涉及流程的服务的 portType 并且不引用它们可能的部署,BPEL4WS 流程被“抽象地”定义。以这种方式定义的业务流程允许在兼容的服务的多次部署中再次使用业务流程定义。

在 WSDL 文档末尾的服务链接类型表示购买定单服务和与之交互的每一方的交互(请参阅服务链接、伙伴和服务引用)。无论 BPEL4WS 业务流程是为一种还是多种服务而定义的,服务链接类型可被用来表示这些服务间的依赖关系。每个服务链接类型最多可定义两个“角色”名称并列出每个角色必须支持的 portType 以便交互被成功执行。在这个示例中,“purchaseLT”链接类型和“schedulingLT”链接类型仅列出一个角色,这是因为在相应的服务交互中,其中的一方提供了所有的被调用操作:“purchaseLT”服务链接表示流程与请求客户之间的连接,其中只有购买定单服务需要提供服务操作(“sendPurchaseOrder”);“schedulingLT”服务链接表示购买定单服务与时间安排服务间的交互,其中只有后者的操作才被调用。其它两个服务链接类型“invoiceLT”和“shippingLT”定义了两个角色,这是因为发票计算的用户和运输服务(发票或运输安排)的用户都必须提供回调操作以使异步通知被异步地发送(“invoiceCallbackPT”portType 和“shippingCallbackPT”portType)。

<definitions targetNamespace="http://manufacturing.org/wsdl/purchase"
      xmlns:sns="http://manufacturing.org/xsd/purchase"
      xmlns:pos="http://manufacturing.org/wsdl/purchase"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns="http://schemas.xmlsoap.org/wsdl/"
       xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/"> 

<import namespace="http://manufacturing.org/xsd/purchase"
        location="http://manufacturing.org/xsd/purchase.xsd"/>

<message name="POMessage">
   <part name="customerInfo" type="sns:customerInfo"/>
   <part name="purchaseOrder" type="sns:purchaseOrder"/>
</message>
<message name="InvMessage">
   <part name="IVC" type="sns:Invoice"/>
</message>
<message name="orderFaultType">
   <part name="problemInfo" type="xsd:string"/>
</message>
<message name="shippingRequestMessage">
   <part name="customerInfo" type="sns:customerInfo"/>
</message>
<message name="shippingInfoMessage">
   <part name="shippingInfo" type="sns:shippingInfo"/>
</message>
<message name="scheduleMessage">
   <part name="schedule" type="sns:scheduleInfo"/>
</message>

<!-- portTypes supported by the purchase order process -->

<portType name="purchaseOrderPT">
   <operation name="sendPurchaseOrder">
      <input message="pos:POMessage"/>
      <output message="pos:InvMessage"/>
      <fault name="cannotCompleteOrder" 
             message="pos:orderFaultType"/>
   </operation>
</portType>
<portType name="invoiceCallbackPT">
   <operation name="sendInvoice">
      <input message="pos:InvMessage"/>
   </operation>
</portType>
<portType name="shippingCallbackPT">
   <operation name="sendSchedule">
      <input message="pos:scheduleMessage"/>
   </operation>
</portType>

<!-- portType supported by the invoice services -->

<portType name="computePricePT">
   <operation name="initiatePriceCalculation">
      <input message="pos:POMessage"/>
   </operation>
   <operation name="sendShippingPrice">
      <input message="pos:shippingInfoMessage"/>
   </operation>
</portType>

<!-- portType supported by the shipping service -->

<portType name="shippingPT">
   <operation name="requestShipping">
      <input message="pos:shippingRequestMessage"/>
      <output message="pos:shippingInfoMessage"/>
      <fault name="cannotCompleteOrder" 
             message="pos:orderFaultType"/>
   </operation>
</portType>

<!-- portType supported by the production scheduling process -->

<portType name="schedulingPT">
   <operation name="requestProductionScheduling">
      <input message="pos:POMessage"/>
   </operation>
   <operation name="sendShipingSchedule">
      <input message="pos:scheduleMessage"/>
   </operation>
</portType>

<slnk:serviceLinkType name="purchaseLT">
   <slnk:role name="purchaseService">
       <slnk:portType name="pos:purchaseOrderPT"/>
   </slnk:role>
</slnk:serviceLinkType>

<slnk:serviceLinkType name="invoiceLT">
   <slnk:role name="invoiceService">
       <slnk:portType name="pos:computePricePT"/>
   </slnk:role>
   <slnk:role name="invoiceRequester">
       <portType name="pos:invoiceCallbackPT"/>
   </slnk:role>
</slnk:serviceLinkType>

<slnk:serviceLinkType name="shippingLT">
   <slnk:role name="shippingService">
       <slnk:portType name="pos:shippingPT"/>
   </slnk:role>
   <slnk:role name="shippingRequester">
       <portType name="pos:shippingCallbackPT"/>
   </slnk:role>
</slnk:serviceLinkType>

<slnk:serviceLinkType name="schedulingLT">
   <slnk:role name="schedulingService">
       <slnk:portType name="pos:schedulingPT"/>
   </slnk:role>
</slnk:serviceLinkType>

</definitions>

接下来定义定单服务的业务流程。在这个流程定义中有四个主要部分:

  • <containers> 部分定义了流程使用的数据容器,用 WSDL 消息类型来提供它们的定义。容器使流程可以根据被交换的消息保存状态数据和流程历史。

  • <partners> 部分定义了在处理定单期间与业务流程交互的各方。其中的四个伙伴对应于定单的发送方(customer)、价格的提供者(invoiceProvider)、承运人(shippingProvider)和生产调度服务(schedulingProvider)。服务链接类型和角色名称描述了每个伙伴。这些信息标识了业务流程和伙伴必须提供以使该关系成功的功能,也就是购买定单流程和伙伴需要实现的 portType。

  • <faultHandlers> 部分所包含的故障处理程序定义了在对调用评估和批准服务所产生的故障作出响应时必须执行的活动。在 BPEL4WS 中,无论是内部故障还是服务调用所产生的故障都由限定名称来标识。具体地说,在 BPEL4WS 中,用来标识每个 WSDL 故障的限定名称由包含相关 portType 定义和故障定义的 WSDL 文档的目标名称空间和故障的 ncname 组成。然而,值得注意的是,由于 WSDL 1.1 并不要求故障名称在操作被定义的名称空间中是唯一的,所以无法区分所有共享相同名称且被定义在相同名称空间中的故障。尽管 WSDL 有这样严重的限制,BPEL4WS 还是为故障提供了统一的命名模型,希望 WSDL 的未来版本会提供更好的故障命名模型。

  • 余下的流程定义包含购买请求的正常执行的描述。在流程定义之后的部分中将解释这些描述的主要元素。
<process name="purchaseOrderProcess" 
         targetNamespace="http://acme.com/ws-bp/purchase"
         xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
         xmlns:lns="http://manufacturing.org/wsdl/purchase">
   
   <partners>
      <partner name="customer" 
               serviceLinkType="lns:purchaseLT"
               myRole="purchaseService"/>
      <partner name="invoiceProvider" 
               serviceLinkType="lns:invoiceLT"
               myRole="invoiceRequester"
               partnerRole="invoiceService"/>
      <partner name="shippingProvider" 
               serviceLinkType="lns:shippingLT"
               myRole="shippingRequester"
               partnerRole="shippingService"/>
      <partner name="schedulingProvider" 
               serviceLinkType="lns:schedulingLT"
               partnerRole="schedulingService"/>
   </partners>

   <containers>
      <container name="PO" messageType="lns:POMessage"/>
      <container name="Invoice" 
                 messageType="lns:InvMessage"/>
      <container name="POFault" 
                 messageType="lns:orderFaultType"/>
      <container name="shippingRequest" 
                 messageType="lns:shippingRequestMessage"/>
      <container name="shippingInfo" 
                 messageType="lns:shippingInfoMessage"/>
      <container name="shippingSchedule" 
                 messageType="lns:scheduleMessage"/>
   </containers>

   <faultHandlers>
      <catch faultName="lns:cannotCompleteOrder" 
             faultContainer="POFault">
         <reply   partner="customer"
                  portType="lns:purchaseOrderPT" 
                  operation="sendPurchaseOrder"
                  container="POFault" 
                  faultName="cannotCompleteOrder"/>
      </catch>
   </faultHandlers>
 
   <sequence>

      <receive partner="customer" 
               portType="lns:purchaseOrderPT" 
               operation="sendPurchaseOrder" 
               container="PO">
      </receive>

      <flow>

         <links>
            <link name="ship-to-invoice"/>
            <link name="ship-to-scheduling"/>
         </links>

         <sequence>
            <assign>
               <copy>
                  <from container="PO" part="customerInfo"/>
                  <to container="shippingRequest" 
                      part="customerInfo"/>
               </copy>
            </assign>
   
            <invoke  partner="shippingProvider" 
                     portType="lns:shippingPT" 
                     operation="requestShipping"
                     inputContainer="shippingRequest" 
                     outputContainer="shippingInfo">
               <source linkName="ship-to-invoice"/>
            </invoke>

            <receive partner="shippingProvider" 
                     portType="lns:shippingCallbackPT" 
                     operation="sendSchedule"
                     container="shippingSchedule">
               <source linkName="ship-to-scheduling"/>
            </receive>

         </sequence>

         <sequence>

            <invoke  partner="invoiceProvider" 
                     portType="lns:computePricePT" 
                     operation="initiatePriceCalculation"
                     inputContainer="PO">
            </invoke>
            <invoke  partner="invoiceProvider" 
                     portType="lns:computePricePT" 
                     operation="sendShippingPrice"
                     inputContainer="shippingInfo">
               <target linkName="ship-to-invoice"/>
            </invoke>

            <receive partner="invoiceProvider" 
                     portType="lns:invoiceCallbackPT" 
                     operation="sendInvoice"
                     container="Invoice"/>

         </sequence>

         <sequence>
            <invoke  partner="schedulingProvider" 
                     portType="lns:schedulingPT" 
                     operation="requestProductionScheduling"
                     inputContainer="PO">
            </invoke>
            <invoke  partner="schedulingProvider" 
                     portType="lns:schedulingPT" 
                     operation="sendShippingSchedule"
                     inputContainer="shippingSchedule">
               <target linkName="ship-to-scheduling"/>
            </invoke>
         </sequence>
      </flow>

      <reply partner="customer" 
             portType="lns:purchaseOrderPT" 
             operation="sendPurchaseOrder" 
             container="Invoice"/>
   </sequence>

</process>

外层 <sequence> 元素定义了主要处理部分的结构,声明了包含在其中的三个元素将按顺序被执行。先接收到客户请求(<receive> 元素),然后处理它(在启用并发执行的 <flow> 部分中),接着包含请求的最终批准状态的回复消息被返回给客户(<reply>)。请注意 <receive> 和 <reply> 元素分别和客户调用的“sendPurchaseOrder”操作的 <input> 和 <output> 消息相匹配,而在这些元素之间由流程执行的活动表示响应客户请求所执行的操作(从接收到请求之时到返回响应之时(reply))。

这个示例隐式地假设客户的请求可在较短的时间里被处理,所以要求调用者等待同步响应是合理的(因为这个服务的提供方式是请求-响应操作)。当这个假设不成立的时候,与客户的交互的更好模型是一对异步消息交换。在这种情况下,“sendPurchaseOrder”是单向操作而异步响应的发送方式是在客户“回调”接口上调用另一个单向操作。为了支持对客户的异步响应,不仅要更改“sendPurchaseOrder”的签名并定义表示客户回调接口的新的 portType,还要在前面的示例中作出两个修改。第一,表示流程-客户连接的服务链接类型“purchaseLT”需要包含第二个角色(“customer”),这个角色应列出客户回调 portType。第二,流程中的 <reply> 活动应该在客户回调操作中被 <invoke> 所取代。

在 <flow> 元素中进行的处理由三个并发运行的 <sequence> 块组成。三个并行 sequence 中的活动间的同步依赖关系由连接它们的“链接(links)”来表示。定义在 flow 中的 links 被用来把源活动连接到目标活动。(请注意每个活动通过使用嵌套的 <source> 和 <target> 元素来声明自己是链接的源还是目标。)在没有链接的情况下,直接嵌套在 flow 中的活动将被并行地执行。然而,在这个示例中的两个链接在每个 sequence 中执行的活动间引入了控制依赖关系。例如,虽然在接收到请求后可以立即开始价格计算,但是只有取得承运人信息后才能把运输价格添加到发票上;这种依赖关系由“ship-to-invoice”链接来表示,该链接把第一次调用承运人(“requestShipping”)与把运输信息发送给计价服务(“sendShippingPrice”)连接起来。类似地,只有接收到来自承运人服务的运输安排信息后才能把运输安排信息发送给生产调度服务;所以需要第二个链接(“ship-to-scheduling”)。

请注意,通过共享全局可见的数据容器,信息在不同的活动间被隐式地传递。在这个示例中,由链接表示的控制依赖关系与相应的数据依赖关系有关,承运人的价格的可用性就是一个例子,另一个例子是运输安排的可用性。通过两个全局数据容器(“shippingInfo”和“shippingSchedule”),信息从产生它的活动被传递给使用它的活动。目前的 BPEL4WS 版本仅支持全局数据容器,但未来的版本还将支持局部数据。此外,在未来的版本中,除了使用链接来表示同步依赖关系外,还将允许通过链接的数据流动。

根据操作的 WSDL 定义,某些操作可以返回故障。为了简单起见,这里假定两个操作返回相同的故障(“cannotCompleteOrder”)。当出现故障时,正常的处理被终止并把控制转移给相应的故障处理程序,故障处理程序被定义在 <faultHandlers> 部分。在这个示例中,处理程序使用 <reply> 元素来把故障返回给客户(请注意 <reply> 元素中的“faultName”属性)。

最后,请注意赋值活动是怎样被用来在数据容器间传输信息的,这一点很重要。这个示例中的简单赋值把来自源容器的消息部分传输给目标容器中的消息部分,但是也可以进行形式更为复杂的赋值。

4.2. 业务流程的结构

这一节简要地总结 BPEL4WS 的语法。这一节仅提供简短的概述;本文的其它章节将详细描述每个语言构造。

该语言的基本结构是:

<process name="ncname" targetNamespace="uri" 
         queryLanguage="anyURI"?
         expressionLanguage="anyURI"?
         suppressJoinFailure="yes|no"?
         enableInstanceCompensation="yes|no"?
         abstractProcess="yes|no"?
         xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/">

  <partners>?
    <!-- Note: At least one role must be specified. -->
    <partner name="ncname" serviceLinkType="qname" 
             myRole="ncname"? partnerRole="ncname"?>+
    </partner>
  </partners>

  <containers>?
    <!-- Note: The message type may be indicated with the messageType 
         attribute or with an inlined <wsdl:message> element within. -->
    <container name="ncname" messageType="qname"?>
      <wsdl:message name="ncname">? 
        ... 
      </wsdl:message>
    </container> 
  </containers>

  <correlationSets>?
    <correlationSet name="ncname" properties="qname-list"/>+
  </correlationSets>

  <faultHandlers>?
    <!-- Note: There must be at least one fault handler or default. -->
    <catch faultName="qname"? faultContainer="ncname"?>*
      activity
    </catch>
    <catchAll>?
      activity
    </catchAll>
  </faultHandlers>

  <compensationHandler>?
    activity
  </compensationHandler>

  activity
</process>

最高级别的属性如下:

  • queryLanguage 这个属性指定了在赋值、属性定义和其它使用中用于选择节点的 XML 查询语言。这个属性的缺省值是 XPath 1.0,其代表是 XPath 1.0 规范的 URI:http://www.w3.org/TR/1999/REC-xpath-19991116

  • expressionLanguage 这个属性指定了在流程中使用的表达式语言。这个属性的缺省值是 XPath 1.0,其代表是 XPath 1.0 规范的 URI:http://www.w3.org/TR/1999/REC-xpath-19991116

  • suppressJoinFailure 这个属性决定是否抑制流程中的所有活动的 joinFailure 故障。这个属性的缺省值是 "no"。

  • enableInstanceCompensation 这个属性决定流程实例是否可被作为整体由特定于平台的方式来补偿。这个属性的缺省值是 "no"。

  • abstractProcess 这个属性指定所定义的流程是抽象的(还是可执行的)。这个属性的缺省值是 "no"。

activity”标记可以是以下任一个:

  • <receive>

  • <reply>

  • <invoke>

  • <assign>

  • <throw>

  • <terminate>

  • <wait>

  • <empty>

  • <sequence>

  • <switch>

  • <while>

  • <pick>

  • <flow>

  • <scope>

  • <compensate>

在下面的段落中将介绍以上每个元素的语法。

<receive> 构造允许业务流程阻塞等待匹配消息的到达。

  <receive partner="ncname" portType="qname" operation="ncname"
           container="ncname" createInstance="yes|no"?
           standard-attributes>
    standard-elements
    <correlations>?
      <correlation set="ncname" initiation="yes|no"?>+
    </correlations>
  </receive>

<reply> 构造允许业务流程发送消息以回复通过 <receive> 接收到的消息。一个 <receive> 和一个 <reply> 组合为流程构成了在 WSDL portType 上的请求-响应操作。

  <reply partner="ncname" portType="qname" operation="ncname"
         container="ncname" faultName="qname"?
         standard-attributes>
    standard-elements
    <correlations>?
       <correlation set="ncname" initiation="yes|no"?>+
    </correlations>
  </reply>

<invoke> 构造允许业务流程调用由伙伴在 portType 上提供的单向或请求-响应操作。

  <invoke partner="ncname" portType="qname" operation="ncname"
          inputContainer="ncname" outputContainer="ncname"?
          standard-attributes>
    standard-elements
    <correlations>?
       <correlation set="ncname" initiation="yes|no"? 
                    pattern="in|out|out-in"/>+
    </correlations>
    <catch faultName="qname" faultContainer="ncname"?>*
      activity
    </catch>
    <catchAll>?
      activity
    </catchAll>
    <compensationHandler>?
      activity
    </compensationHandler>
  </invoke>

<assign> 构造的用途是用新的数据来更新容器的值。一个 <assign> 构造可以包括任意数量的基本赋值。赋值活动的语法是:

  <assign standard-attributes>
    standard-elements
    <copy>+
      from-spec
      to-spec
    </copy>
  </assign>

其中 from-spec 标记和 to-spec 标记的各种选项是:

  <from container="ncname" part="ncname"? query="queryString"?/>
  <from partner="ncname" serviceReference="myRole|partnerRole"/>
  <from container="ncname" property="qname"/>
  <from expression="general-expr"/>
  <from> ... literal value ... </from>
  <from opaque="yes"/>

  <to container="ncname" part="ncname"? query="queryString"?/>
  <to partner="ncname"/>
  <to container="ncname" property="qname"/>

<throw> 构造从业务流程中生成故障。

  <throw faultName="qname" faultContainer="ncname"? standard-attributes>
    standard-elements
  </throw>

<terminate> 构造使您能够立即终止业务流程。

  <terminate standard-attributes>
    standard-elements
  </terminate>

<wait> 构造允许您等待一段给定的时间或等到某一时刻。您 必须指定其中一个到期条件。

  <wait (for="duration-expr" | until="deadline-expr") standard-attributes>
    standard-elements
  </wait>

<empty> 构造允许您在业务流程中插入“no-op”指令。例如,这个构造可被用于并行活动的同步。

  <empty standard-attributes>
    standard-elements
  </empty>

<sequence> 构造允许您定义一组按词法顺序先后被执行的活动。

  <sequence standard-attributes>
    standard-elements
    activity+
  </sequence>

<switch> 构造允许您从一组分支中只选择一个执行分支。

  <switch standard-attributes>
    standard-elements
    <case condition="bool-expr">+
      activity
    </case>
    <otherwise>?
      activity
    </otherwise>
  </switch>

<while> 构造允许您指定反复执行一个活动,直到某个成功条件被满足。

  <while condition="bool-expr" standard-attributes>
     standard-elements
     activity
  </while>

<pick> 构造允许您阻塞等待某一个合适的消息的到达或超时警报响起。当其中一个触发器触发后,相关的活动被执行,pick 也就完成了。

  <pick createInstance="yes|no"? standard-attributes>
    standard-elements
    <onMessage partner="ncname" portType="qname"
               operation="ncname" container="ncname">+
      <correlations>?
         <correlation set="ncname" initiation="yes|no"?>+
      </correlations>
      activity
    </onMessage>
    <onAlarm (for="duration-expr" | until="deadline-expr")>*
      activity
    </onAlarm>
  </pick>

<flow> 构造允许您指定一个或多个被并行地执行的活动。为了定义任意 的控制结构,可以在并行的活动中使用链接。

  <flow standard-attributes>
    standard-elements
    <links>?
      <link name="ncname">+
    </links>

    activity+
  </flow>

<scope> 构造允许您定义嵌套活动,这个嵌套活动有和自己关联的故障处理程序和补偿处理程序。

<scope containerAccessSerializable="yes|no" standard-attributes>
    standard-elements
    <faultHandlers>?
      ... see above under <process> for syntax ...
    </faultHandlers>
    <compensationHandler>?
      ... see above under <process> for syntax ...
    </compensationHandler>
    activity
  </scope>

<compensate> 构造被用来在已正常完成执行的内层作用域上调用补偿。您只能从故障处理程序或另一个补偿处理程序中调用这个构造。

  <compensate scope="ncname"? standard-attributes>
    standard-elements
  </compensate>

请注意上面所指的“standard-attributes”是:

  name="ncname"?
  joinCondition="bool-expr"?
  suppressJoinFailure="yes|no"?

它们的缺省值如下:

  • name 没有缺省值(也就是说,未命名)

  • joinCondition 目标为这个活动的所有链接的活跃状态的逻辑或。

  • suppressJoinFailure No

上面所指的“standard-elements”是:

  <target linkName="ncname"/>*
  <source linkName="ncname" transitionCondition="bool-expr"?/>*

其中“transitionCondition”属性的缺省值是“true()”,这是缺省表达式语言 XPath 1.0 中的真值函数。

4.3. 语言的扩展性

一般来说,BPEL4WS 所包括的构造已足够表达抽象业务流程和可执行业务流程。然而在有些情况下还是有必要通过添加 来自其它 XML 名称空间的构造来“扩展”BPEL4WS 语言。

为了支持扩展性,BPEL4WS 允许名称空间 限定的属性出现在任何 BPEL4WS 元素上,也允许其它名称空间的元素出现在 BPEL4WS 定义的元素中。这在 BPEL4WS 的 XML Schema 规范中是允许的。

所有用于 BPEL4WS 文档的扩展名称空间必须被声明。请使用以下语法来声明扩展名称空间:

  <extension namespace="anyURI"/>*

扩展绝不可以改变 BPEL4WS 名称空间的任何元素或属性的语义。

4.4. 业务流程的生命周期

正如在引言中所述,WSDL 所直接支持的交互模型仅仅是同步或无关的异步交互的无 状态客户机/服务器模型。构建在 WSDL 之上的 BPEL4WS 假设业务流程的所有对外交互都是通过 Web 服务 操作来进行的。但是,BPEL4WS 业务流程表示有状态的长期运行的交互,其中每个交互有起点、在生命期中已定义的行为和终点。例如,在供应链中,卖方的业务流程可以提供一个服务,这个服务通过接收输入消 息来接受购买定单从而开始交互,如果该定单可以被满足,那么这个服务就把确认消息返回给买方。后来, 这个服务可以向买方发送更多消息(例如运输通知和发票)。卖方的业务流程能记住分别来自其他类似交互的每个这样的购买定单的状态。这样做是必要的,因为买方可能与同一个卖方同时进行多个购买流程。简而言之,BPEL4WS 业务流程定义可被看作创建业务流程实例的模板。

在 BPEL4WS 中流程实例的创建总是隐 式的;接收消息的活动(也就是 receive 活动和 pick 活动)可被加上注解,以表示活动的进行将导致业务流程的新实例被创建。注解的方式是把这种活动的 createInstance 属性设置为 "yes"。当消息被这样的活动接收到后,如果该业务流程的实例还不存在,那么该业务流程的实例将被创建(请参阅提供 Web 服务操作Pick)。

为了被实例化,每个业务流程必须包括至少一个这样的“启动活动”。它必须是初始活动,其含义是在执行流程中,从逻辑上说,它的前面没有基本活动。

如果要使不止一个启动活动被并发地执行,那么所有这些活动必须使用至少一个相关集且必须使用相同的相关集(请参阅相关性多个启动活动示例)。

如果仅有一个启动活动需被执行,那么相关集的使用是没有限制的。这包括有多个 onMessage 分支的 pick;每个这样的分支可以使用不同的相关集,也可以不使用相关集。

业务流程实例以下面方式中的一种方式被终止:

  • 当定义流程行为的活动全部完成时。在这种情况下,终止是正常的。

  • 当故障到达流程的作用域而且被处理或未被处理。在这种情况下,即使故障被处理而且故障处理程序没有再次扔出任何故障,终止也被认为是异常的。对于所有异常终止的作用域都没有安装补偿处理程序。

  • 当流程实例被 terminate 活动显式地终止(请参阅终止服务实例)。在这种情况下,终止是异常的。

  • 如果为整个业务流程指定了补偿处理程序(请参阅补偿处理程序),那么在业务流程实例正常完成后,特定于平台的方式可以补偿业务流程实例。启用这个功能的方法是把 processenableInstanceCompensation 属性设置为 "yes"

5. 服务链接、伙伴和服务引用

BPEL4WS 的很重要(可能也是最重要)的用例是描述跨企业业务交互 ,在这种交互中,每个企业的业务流程通过 Web 服务接口与其它企业的流程交互。为了在这种环境中真实地 模拟业务流程的进行,一个重要的要求是模拟伙伴流程的能力。WSDL 已能在抽象级别和具体级别上描述由伙 伴提供的服务的功能。业务流程与伙伴的关系通常是对等的,这需要在服务级别上有双向的相关性。换句话说 ,伙伴表示由业务流程提供的服务的消费者,同时,对于业务流程来说,伙伴又表示服务的提供者。这种 情况的典型例子是基于异步消息传递(而不是基于远程过程调用)的交互。服务链接这个概念被用来直接 模拟对等伙伴关系。为了定义与伙伴的关系的形式,服务链接定义了在交互的两个方向上用到的消息和端口类型。然而,实际的伙伴服务可以在流程中被动态的确定。为了表示描述伙伴服务所需的动态数据,BPEL4WS 定义了服务引用这个概念。

在这里有必要强调所用的服务链接概念和服务引用概念是 初步的。目前,这些与 Web 服务相关的概念还没有广泛接受的规范,我们期待着在将来出现这些概念的标 准定义。为了符合预期的未来标准,BPEL4WS 规范将被相应地更新。

5.1. 服务链接

为了描述两个服务间的关系,服务链接类型 定义了关系中每个服务所扮演的“角色”并指定每个角色所提供的 portType。下面举例说明服务链接类型 声明的基本语法:

<serviceLinkType name="BuyerSellerLink"
        xmlns="http://schemas.xmlsoap.org/ws/2002/07/service-link/">
  <role name="Buyer">
    <portType name="buy:BuyerPortType"/>
  </role>
  <role name="Seller">
    <portType name="sell:SellerPortType"/>
  </role>
</serviceLinkType>

每个角色可以包括任意个 WSDL portType。

在常见的情况中,每个角色的 portType 产生于不同的名称空间。然而在有些情况下可以用来自相同名称空间的 portType 来定义 服务链接类型的两个角色。在服务间定义“回调”关系的服务链接类型就会出现后一种情况。

服务 链接类型定义可以是独立于任一个服务的 WSDL 文档的单独的构件。服务链接类型定义也可以被放在 定义 portType 的 WSDL 文档中,这些 portType 也被用来定义不同的角色。

WSDL 1.1 的扩展机制被用来把 serviceLinkType 定义为新的定义类型并且在所有的情况下都是作为 <wsdl:definitions> 元素的直接子元素被放置。这是为了支持 WSDL 目标名称空间规范 的再利用,更为重要的是,也是为了支持导入 portType 的导入机制。如果服务链接类型声明链接了两个不同服 务的 portType,那么服务链接类型声明可以被放在单独的(有自己的 targetNamespace 的)WSDL 文档中。

定义服务链接类型的语法如下:

<definitions name="ncname" targetNamespace="uri"
     xmlns="http://schemas.xmlsoap.org/wsdl/"
     xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/">
  ...
  <slnk:serviceLinkType name="ncname">
    <slnk:role name="ncname">
      <slnk:portType name="qname"/>+
    </slnk:role>
    <slnk:role name="ncname">?
      <slnk:portType name="qname"/>+
    </slnk:role>
  </slnk:serviceLinkType>
  ...
</definitions>

以上定义了服务链接类型,其名称空间是 WSDL 文档元素的“targetNamespace”属性值。正如引用所有顶层 WSDL 定义那样,通过使用 QNames 来引用标识在 <slnk:role> 中的 portType。

请注意,在有些情况下定义包含一个角色(而不是两个角色)的服务链接类型是有意义的。在这种服务链接情 形中,一个服务愿意链接任何其它服务而不对其它服务提出任何要求。

您可以在本规范中的各种业务流程示例中找到 serviceLinkType 声明的示例。

5.2. 伙伴

在 BPEL4WS 中,与业务流程交互的服务被模拟成伙伴。每个伙伴由 serviceLinkType 来描述。同一个 serviceLinkType 可以描述多个伙伴。例如,某个采购流程可以在它的事务中使用多个供应商,但是对于所 有的供应商都使用相同的 serviceLinkType。

  <partners>
    <partner name="ncname" serviceLinkType="qname" 
             myRole="ncname"? partnerRole="ncname"?>+
    </partner>
  </partners>

每个伙伴被命名,这个名称被用于与这个伙伴的所有服务交互。这一点很重要,例如, 为了多个同时产生的同一种请求而使响应与不同的伙伴相关(请参阅调用 Web 服务操作提供 Web 服务操作)。

属性 myRole 指出了业务流程的角色而属性 partnerRole 指出了伙伴的角色。如果 serviceLinkType 退化为仅有一个角色,那么将根据需要省略其中一个属性。

请注意,伙伴声明指定了 BPEL4WS 流程将在它的行为中使用的关系的静态形式。当这样的流程被部署和执行时,在调用伙伴的服务上的操作前必须把所有出现 partnerRole 属性的伙伴解析到实际的服务。关于伙伴服务的有关信息可被设置为业务流程部署的一部分。这超出了 BPEL4WS 的范围。然而,动态地选择和分配实际的伙伴服务也是可能的,BPEL4WS 提供了这样做的机制。从概念上说,关于伙伴服务的信息的动态内容被封装在服务引用中。事实上,因为伙伴很可能是有状态的,所以服务端点信息需用特定于实例的信息来扩展。BPEL4WS 允许隐式地出现在伙伴定义中的服务引用被动态地抽取和赋值,也允许不止一次地被设置。在下面几个段落中将描述服务引用的形式。请参阅赋值,了解用于把服务引用动态地赋给伙伴的机制。

5.3. 服务引用

WSDL 严格地区分 portType 和端口。PortType 使用抽象消息来定义抽象功能。端口提供实际访问 信息,包括通信端点和(通过使用扩展元素)其它有关部署的信息(例如用于加密的公钥)。绑定使 两者连结在一起。虽然服务的用户必须静态地依赖于由 portType 定义的抽象接口,但是在通常情况下可以 动态地发现和使用包括在端口定义中的信息。

服务引用的基本用途是作为一种机制,用于服务的特 定于端口的数据的动态通信。服务引用使您能在 BPEL4WS 中动态地为某个服务类型选择提供者和调用它们 的操作。BPEL4WS 提供使消息与服务的有状态实例相关的一般机制,所以携带中立于实例间的端口信息的服务引用在多数情况下是足够的。然而,一般来说,有必要在服务引用本身中携带额外的实例标识标记。

服务引用被定义为有类型的引用,它包括服务的特定于端口的数据,还可能包括关于实例标识的标记和其它有关属性的更多数据。为了避免冗余,有关的 WSDL 模式应当被尽可能地使用。

服务引用的语法结构是:

<sref:serviceReference  
     xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/">
  <wsdl:definitions> ... </wsdl:definitions>?
  <sref:service name="qname"/>
  <sref:referenceProperties>?
    <sref:property name="qname">+
      <!-- any element content -->
    </sref:property>
  </sref:referenceProperties>
</sref:serviceReference>

至少来说,服务引用是 <wsdl:service> 元素的限定名,其中的元素要么被直接插入在服务引用中,要么可以假定服务引用的接收方已经知晓该元素。以下是服务引用的最简单的示例:

<sref:serviceReference  
       xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
       xmlns:ns="http://example.com/services/">
  <sref:service name="ns:myService"/>
</sref:serviceReference>

如果不能假设服务可通过引用已被知晓,那么它的定义可被直接插入在服务引用中,以动态地表示 WSDL 文档的服务定义部分。

<sref:serviceReference  
    xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
    xmlns:ns="http://example.com/services/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:definitions 
            targetNamespace="http://example.com/services/" ...>
       <wsdl:service name="myService">
          ...
       </wsdl:service>
  </wsdl:definitions>
  <sref:service name="ns:myService"/>
</sref:serviceReference>

被直接插入的 <wsdl:definitions> 元素绝不可以包括不止一个直接插入的服务定义。<wsdl:definitions> 的标准使用仅包括单个服务定义。任何其它定义的存在可能影响服务引用的可移植性。

最后,出于某些目的(例如识别一个或多个感兴趣的服务实例),服务引用可能需要更多标记。服务引用模式并不把任何某种重要性赋给这些标记。这些标记是允许出现的,以作为携带用于各种目的的元数据的简便方法。要点是该数据总是与被全局命名的属性关联(请参阅消息属性)。同样地,可以假定服务引用的接收方知道它们的语义。

<sref:serviceReference  
   xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
   xmlns:ns="http://example.com/services/"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <sref:service name="ns:myService"/>
  <sref:referenceProperties>
    <sref:property name="ns:instanceID">
       74b9f5d0-33fb-4a81-b02b-5b760641c1d6
    </sref:property>
  </sref:referenceProperties>
</sref:serviceReference>

在流程的部署或执行中,BPEL4WS 流程实例中的每个伙伴被分配给独特的服务引用。

6. 消息属性

6.1. 动机

从概念上说,消息中的数据由两部分组成:应用程序数据和有关协议的数据,其中的协议可以是业务协议,也可以是提供更高质量的服务的基础结构协议。业务协议数据的一个示例是用于相关集的相关标记(请参阅相关性)。基础结构协议的示例有安全性、事务和可靠的消息传递协议。业务协议数据通常被嵌入在应用程序可见的消息部分中,而基础结构协议几乎总是把隐式的额外部分添加到消息类型,以表示与应用程序数据分开的协议头。这种隐式部分常常被称为消息上下文,因为它们和这些上下文有关:交互的安全上下文、事务上下文和其它类似中间件上下文。业务流程可能需要访问和处理两种有关协议的数据。消息属性这个概念被定义为命名和表示消息中不同数据元素的一般方法,无论是在应用程序可见数据中还是在消息上下文中。如果要全面考虑基础结构协议的服务描述部分,那么就有必要定义服务策略、端点属性和消息上下文等概念。这些工作超出了 BPEL4WS 的范围。在这里,为了包括由隐式部分组成的消息上下文,我们以足够通用地方式来定义消息属性,但是在本规范中,消息属性主要被用于嵌入在应用程序可见的数据中的属性,而这些应用程序可见的数据被用于业务协议和抽象业务流程的定义。

6.2. 定义属性

属性定义创建了全局唯一的名称并使它与 XML Schema 类型关联。目的并不在于创建新类型。目的在于创建比该类型本身更重要的名称。例如,序列号可以是整数,但是整数类型并没有表达这种重要性,而全局命名的 sequence-number 属性能表达这种重要性。属性可以出现在消息中的任何地方(包括消息上下文)。

在 BPEL4WS 中,属性的典型用途是命名标记,以用于服务实例与消息的相关。例如,在长期运行的多方业务流程的税收部分,社会保障号可被用于标识某一个交税人。社会保障号可以出现在许多不同的消息类型中,但是在有关税收的流程的上下文中,社会保障号有某种重要性,即交税人标识。于是,通过定义一个属性,一个全局名被用于该类型的这种用法,如下面的例子所示:

<definitions name="properties"
      targetNamespace="http://example.com/properties.wsdl"
      xmlns:tns="http://example.com/properties.wsdl"
      xmlns:txtyp="http://example.com/taxTypes.xsd"
      xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
      xmlns="http://schemas.xmlsoap.org/wsdl/">

    <!-- define a correlation property -->
    <bpws:property name="taxpayerNumber"
                               type="txtyp:SSN"/>
    ...
</wsdl:definitions>

在相关性中,有效的属性名必须有全局重要性。诸如价格、风险、响应等待时间等用于业务流程中的条件行为的属性有类似的全局和公共重要性。这些属性很有可能被映射到多个消息,所以它们应该被全局地命名,就象在相关属性的情况中那样。这种属性是必需的,尤其是在抽象流程中。

WSDL 可扩展机制被用来定义属性以使 WSDL 的目标名称空间和其它有用部分成为可用的。"http://schemas.xmlsoap.org/ws/2002/07/business-process/" 是 BPEL4WS 标准名称空间,被用来定义属性。属性定义的语法是一种新的 WSDL 定义,如下所示:

<wsdl:definitions name="ncname"
   xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/">
    <bpws:property name="ncname" type="qname"/>
    ...
</wsdl:definitions>

用于业务协议的属性通常被嵌入在应用程序可见的消息数据中。别名这个概念被用来把全局属性映射到某个消息部分中的字段。属性名成为消息部分和位置的别名,例如,别名可被用于抽象业务流程中的表达式赋值

<definitions name="properties"
         targetNamespace="http://example.com/properties.wsdl"
         xmlns:tns="http://example.com/properties.wsdl"
         xmlns:txtyp="http://example.com/taxTypes.xsd"
         xmlns:txmsg="http://example.com/taxMessages.wsdl"
         xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
         xmlns="http://schemas.xmlsoap.org/wsdl/">

    <!-- define a correlation property -->
    <bpws:property name="taxpayerNumber" type="txtype:SSN"/>
     ...
    <bpws:propertyAlias propertyName="tns:taxpayerNumber"   
       messageType="txmsg:taxpayerInfo" part="identification" 
       query="/socialsecnumber"/>
    </bpws:propertyAlias>
</definitions>

bpws:propertyAlias 把全局命名的属性 tns:taxpayerNumber 定义为消息类型 txmsg:taxpayerInfoidentification 部分中位置的别名。

定义 propertyAlias 的语法是:

<definitions name="ncname"
          ...
      xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/">
 
    <bpws:propertyAlias propertyName="qname" 
            messageType="qname" part="ncname" query="queryString"/>
    ...
</wsdl:definitions>

messagepartquery 等属性的解释与复制赋值中的相应的 from-spec 中的属性的解释相同(请参阅赋值)。

7. 相关性

以上提供的信息暗示被传递到业务流程服务的消息的目标是接收方的服务的 WSDL 端口。这是误解,由于有状态业务流 程的本质,业务流程被实例化以符合扩展的交互历史。所以,被发送到这样的流程的消息不仅需要被传递到正确的目标端口,还需要被传递到提供端口的业务流程的正确实例。托管流程的基础结构必须以一般的方式做到这两点,以使每个流程实现不必实现实例路由的定制机制。创建新的业务流程实例的消息是特殊情况,请参阅业务流程的生命周期

在面向对象的世界中,这种有状态的交互需要通过对象引用,对象引用本身提供了访问具有合适的交互状态和历史的某个对象(实例)的能力。这种方式适用于紧密耦合的实现,在这种实现中,对实现的结构的依赖性是正常的。在松散耦合的 Web 服务的世界中,这种引用的使用将造成实现之间脆弱的依赖关系,这种依赖关系无法适应在每个业务伙伴那里进行的业务流程实现细节的独立演化。在这个世界中,解决的方法是依靠定义伙伴间线路层的合同的业务数据和通信协议头并尽可能地避免在实例路由中使用特定于实现的标记。

请考虑买方向卖方发送购买定单的情况,这在供应链中经常出现。假定买方与卖方有稳定的业务关系并通过静态的配置把有关购买交互的文档发送到与相关 WSDL 服务端口关联的 URL。卖方需要异步地返回对定单的确认并且这种确认必须被路由到位于买方的正确的业务流程实例。为此,显而易见的标准机制是在定单消息中携带业务标记(例如购买定单编号)并把它复制到确认中以用于相关性。该标记可以在头中的消息信封中,也可以在业务文档(购买定单)中。在这两种情况中,相关消息中的标记的确切位置和类型是固定的且与实例无关。只有标记的值才与实例相关。所以,每个消息中的相关性标记的结构和位置可以在业务流程描述中以声明的方式被表达。在下一节中描述的相关集这个 BPEL4WS 概念提供了这个功能。这种声明性的信息使遵守 BPEL4WS 的基础结构能够使用相关性标记来自动地提供实例路由。

声明性的相关性指定依赖于声明性的消息属性。属性仅仅是消息中由查询来识别的“字段”— 缺省的查询语言是 XPath 1.0。为此,必须使用 XML Schema 来描述消息部分的类型或 binding 元素。相关标记和服务引用的使用限于这样描述的消息部分。更清楚地说,这种类型的实际线路格式仍可以是非 XML 的,例如,基于不同端口类型的绑定的 EDI 平面文件。

7.1. 消息相关性

在业务流程实例的生命期中,它通常与涉及它工作的伙伴进行一次或多次对话。对话可基于成熟的传输基础结构,这种基础结构通过使用某种形式的对话标识使对话中的消息相关并把它们自动地路由到正确的服务实例而无需在业务流程中注释。但是,在许多情况下,相关的对话涉及的参与者不止两个或使用轻量级传输基础结构(即把相关标记直接嵌套在被交换的应用程序数据中)。在这种情况下,常常有必要提供另外的应用程序级的机制,以使消息和对话被匹配到预定的业务流程实例。

相关形式可能相当复杂。一般来说,某一个相关标记集的使用并不跨越服务实例与伙伴(实例)间的交互,而是跨越交互的一部分。被相关的交换可以嵌套和重叠,消息可以携带几个相关标记集。例如,买方可启动与卖方的被相关的交换,启动的方法是发送购买定单(purchase order,PO)并使用嵌入在 PO 文档中的 PO 编号作为相关标记。卖方在 PO 确认中使用该 PO 编号。后来,卖方可能发送携带 PO 编号(以使它与 PO 相关)的发票,也可能携带发票编号以使今后有关支付的消息只需携带发票编号作为相关标记。这样,该发票消息携带两个不同的相关标记并参与两个重叠的被相关的交换。

为了处理相关性情况,BPEL4WS 提供了声明性机制,以指定服务实例中操作的被相关组。一组相关标记可被定义为被相关的组中被所有消息共享的一组属性。这样的一组属性被称为相关集

相关集在业务流程的实例的作用域中被实例化。正如业务流程的实例化,相关集的实例化由特别标记的操作来触发。在当前的 BPEL4WS 版本中,在一个业务流程实例的生命期中,一个相关集只能被实例化一次。在初始化后,它的值可被认为是业务流程实例的标识的别名。

在多方业务协议中,被相关的消息交换中的每个参与流程要么是该交换的初始者,要么是该交换的跟随者。初始者流程发送启动对话的第一个消息(作为操作调用的一部分)从而定义了在标记该对话的相关集中的属性值。所有其它参与者是跟随者,它们接收到提供相关集中的属性值的进站的消息从而实例化对话中的相关集。初始者和跟随者都必须把它们相应的组中的第一个活动标记为初始化相关集的活动。

7.2. 定义和使用相关集

这一节的示例几乎演示了用于每个消息传递活动(receive、reply 和 invoke)的相关性。这是因为 BPEL4WS 并不假定在消息传递中使用任何成熟的对话的传输协议。在使用这种协议的情况下,BPEL4WS 中的相关性的显式使用可被限于建立对话的连接的活动。

BPEL4WS 中的每个相关集是命名的属性组,它们一起定义了在业务协议实例中识别应用程序级别的对话的方法。一个给定的消息可以携带多个相关集。在初始化后,对于携带相关集的所有操作中的所有消息来说,业务流程实例中的相关集的属性值必须相同。如果在执行时,这个约束被违反,那么遵守本规范的实现必须抛出标准故障 bpws:correlationViolation。如果一个活动企图使用还未实例化的相关集,那么相同的故障必须被抛出。正如下面的示例所演示的那样,当使用相关集的活动把属性 initiation="yes" 应用到相关集时,相关集被实例化。

  <correlationSets>?
    <correlationSet name="ncname" properties="qname-list"/>+
  </correlationSets>

下面是扩展的相关性示例。它先定义了四个消息属性:customerIDorderNumbervendorIDinvoiceNumber。所有这些属性被定义为由该文档定义的 "http://example.com/supplyCorrelation.wsdl" 名称空间的一部分。

<definitions name="properties"
    targetNamespace="http://example.com/supplyCorrelation.wsdl"
    xmlns:tns="http://example.com/supplyCorrelation.wsdl"
    xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
    xmlns="http://schemas.xmlsoap.org/wsdl/">

    <!-- define correlation properties -->
    <bpws:property name="customerID" type="xsd:string"/>
    <bpws:property name="orderNumber" type="xsd:int"/>
    <bpws:property name="vendorID" type="xsd:string"/>
    <bpws:property name="invoiceNumber" type="xsd:int"/>
</definitions>

请注意这些属性是有已知的(简单的)XML Schema 类型的全局名。它们是抽象的,其含义是它们在消息中的出现需被分开指定(请参阅消息属性)。接着,该示例定义了购买定单和发票消息并使用别名概念把抽象属性映射到由选择标识的消息数据中的字段。

<definitions name="correlatedMessages"
    targetNamespace="http://example.com/supplyMessages.wsdl"
    xmlns:tns="http://example.com/supplyMessages.wsdl"
    xmlns:cor="http://example.com/supplyCorrelation.wsdl"
    xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
    xmlns="http://schemas.xmlsoap.org/wsdl/">
    <!梔efine schema types for PO and invoice information -->
    <types>
       <xsd:complexType name="PurchaseOrder">
           <xsd:element name="CID" type="xsd:string"/>
           <xsd:element name="order" type="xsd:int"/>
           ...
       </xsd:complexType>
    </types>
    <types>
       <xsd:complexType name="PurchaseOrderResponse">
           <xsd:element name="CID" type="xsd:string"/>
           <xsd:element name="order" type="xsd:int"/>
           ...
       </xsd:complexType>
    </types>
    <types>
       <xsd:complexType name="PurchaseOrderReject">
           <xsd:element name="CID" type="xsd:string"/>
           <xsd:element name="order" type="xsd:int"/>
           <xsd:element name="reason" type="xsd:string"/>
           ...
       </xsd:complexType>
    </types>
    <types>
       <xsd:complexType name="Invoice">
           <xsd:element name="VID" type="xsd:string"/>
           <xsd:element name="invNum" type="xsd:int"/>
       </xsd:complexType>
    </types>
    <message name="POMessage">
        <part name="PO" type="tns:PurchaseOrder"/>
    </message>
    <message name="POResponse">
        <part name="RSP" type="tns:PurchaseOrderResponse"/>
    </message>
    <message name="POReject">
        <part name="RJCT" type="tns:PurchaseOrderReject"/>
    </message>
    <message name="InvMessage">
        <part name="IVC" type="tns:Invoice"/>
    </message>
    <bpws:propertyAlias propertyName="cor:customerID"
                  messageType="tns:POMessage" part="PO"
                  query="/CID"/>
    <bpws:propertyAlias propertyName="cor:orderNumber"
                  messageType="tns:POMessage" part="PO"
                  query="/Order"/>
    <bpws:propertyAlias propertyName="cor:vendorID"
                  messageType="tns:InvMessage" part="IVC"
                  query="/VID"/>
    <bpws:propertyAlias propertyName="cor:invoiceNumber"
                  messageType="tns:InvMessage" part="IVC"
                  query="/InvNum"/>
  ...
</definitions>

最后,所用到的 portType 被定义在不同的 WSDL 文档中。

<definitions name="purchasingPortType"
    targetNamespace="http://example.com/puchasing.wsdl"
    xmlns:smsg="http://example.com/supplyMessages.wsdl"
    xmlns="http://schemas.xmlsoap.org/wsdl/">

<portType name="PurchasingPT">
   <operation name="SyncPurchase">
      <input message="smsg:POMessage"/>
      <output message="smsg:POResponse"/>
      <fault name="tns:RejectPO" message="smsg:POReject"/>
   </operation>
   <operation name="AsyncPurchase">
      <input message="smsg:POMessage"/>
   </operation>
</portType>
<portType name="BuyerPT">
   <operation name="AsyncPurchaseResponse">
      <input message="smsg:POResponse"/>
      <fault name="tns:RejectPO" message="smsg:POReject"/>
   </operation>
   <operation name="AsyncPurchaseReject">
      <input message="smsg:POReject"/>
   </operation>
</portType>
</definitions>

这些属性以及它们到购买定单和发票消息的映射将都被用于下面的相关性示例。

  <correlationSets 
         xmlns:cor="http://example.com/supplyCorrelation.wsdl">
      <!-- Order numbers are particular to a customer, 
           this set is carried in application data -->
    <correlationSet name="PurchaseOrder"
              properties="cor:customerID cor:orderNumber"/>
 
      <!-- Invoice numbers are particular to a vendor, 
           this set is carried in application data -->
    <correlationSet name="Invoice"
              properties="cor:vendorID cor:invoiceNumber"/>
  </correlationSets>

相关集被用于 invokereceivereply 等活动(请参阅调用 Web 服务操作提供 Web 服务操作)以指出哪个相关集出现在被发送和接收的消息中。initiation 属性被用来指出该集是否被初始化。当该属性被设置为 "yes" 时,使用在被发送和被接收的消息中出现的属性值来初始化该集。最后,在 invoke 中,当调用的操作是同步的请求/响应时,一个 pattern 属性被用来指出相关性是应用到出站的(请求)消息还是应用到入站的(响应)消息还是应用到两者。在这个示例的其余部分中,这些概念在相关性的使用的上下文中被更详细地说明。

一个消息可以携带一个或多个相关集的标记。在第一个示例所演示的交互中,购买定单在单向入站的请求中被接收,包括发票的确认在异步的响应中被发送。PurchaseOrder correlationSet 被用于两个活动以使异步响应可在买方处与请求相关。receive 活动初始化了 PurchaseOrder correlationSet。所以买方是初始者而接收的业务流程是这个 correlationSet 的跟随者。发送异步响应的 invoke 活动也初始化了一个新的 correlationSet Invoice。该业务流程是这个被相关的交换的初始者,买方是跟随者。所以,响应消息是两个不同对话的一部分并构成它们间的桥梁。

在下面,前缀 SP: 表示名称空间 "http://example.com/puchasing.wsdl"

  <receive partner="Buyer" portType="SP:PurchasingPT" 
           operation="AsyncPurchase" 
           container="PO">
     <correlations>
       <correlation set="PurchaseOrder" initiation="yes">
     </correlations>
  </receive>
 
  <invoke partner="Buyer" portType="SP:BuyerPT" 
         operation="AsyncPurchaseResponse" inputContainer="POResponse">
     <correlations>
       <correlation set="PurchaseOrder" initiation="no" pattern="out">
       <correlation set="Invoice" initiation="yes" pattern="out">
     </correlations>
  </invoke>

另外,响应可能是拒绝(例如“缺货”消息),在这种情况下终止了被 correlationSet PurchaseOrder 相关的对话而不启动与 Invoice 相关的新对话。请注意 initiation 属性没有出现。所以它的缺省值是 "no"

  <invoke partner="Buyer" portType="SP:BuyerPT" 
         operation="AsyncPurchaseReject" inputContainer="POReject">
     <correlations>
       <correlation set="PurchaseOrder" pattern="out">
     </correlations>
  </invoke>

invoke 活动使用的另外的同步购买操作被用于买方的业务流程中,这演示了同步 Web 服务调用中的相关性的使用。

  <invoke partner="Seller" portType="SP:PurchasingPT" operation="SyncPurchase" 
           inputContainer="sendPO" 
           outputContainer="getResponse">
     <correlations>
       <correlation set="PurchaseOrder" initiation="yes" 
                    pattern="out">
       <correlation set="Invoice" initiation="yes" 
                    pattern="in">
     </correlations>
    <catch faultName="SP:RejectPO" faultContainer="POReject">
      <!-- handle the fault -->
    </catch>
  </invoke>

请注意一个 invoke 由两个消息组成:一个出站的请求消息和一个进站的应答消息。可应用到每个消息的相关集必须被分开考虑,因为它们可能不同。在这种情况下,PurchaseOrder 相关性应用到初始化它的出站请求,而 Invoice 相关性应用到进站的应答并被该应答初始化。因为出站的消息初始化了 PurchaseOrder 相关性,所以买方是该相关性的初始者但不是 Invoice 相关性的跟随者,因为 Invoice 的相关属性值在买方接收的应答中被卖方设置。

8. 数据处理

业务流程模拟有状态的交互。涉及的状态由接收和发送的消息以及其它有关数据(例如超时值)组成。业 务流程的状态的维护需要使用状态变量,这种状态变量被称为容器。另外,状态中的数据需被抽取并 被有意义地组合,以控制流程行为,这就需要数据表达式。最后,状态更新需要赋值概念。BPEL4WS 为 XML 数据 类型和 WSDL 消息类型提供这些功能。这方面的 XML 标准系列仍在发展中,为查询和表达式语言而使用流程级别的属性为融入未来的标准做好准备。

抽象流程与可执行流程之间的区别仅限于这两种 流程可用的数据处理功能集。可执行流程可以使用全部的数据处理和赋值功能但是不能使用不确定的值。抽 象流程只能对包含在消息属性中的值进行有限地处理但是能够使用不确定的值,以反映隐藏的私有行为的后果。在下面的几节中将指定两者的具体区别并在数据处理部分的末尾作出总结。

8.1. 表达式

BPEL4WS 使用几种类型的表达式。用到的表达式种类如下(在括号中列出的是有关的使用上下文):

  • 值为布尔(Boolean)的表达式(过渡条件、连结条件、while 条件和 switch case)

  • 值为截止期限(Deadline)的表达式(onAlarm 和 wait 的“until”属性)

  • 值为持续时间(Duration)的表达式(onAlarm 和 wait 的“for”属性)

  • 一般表达式(赋值)

BPEL4WS 为用于这些表达式的语言提供可扩展的机制。该语言由 process 元素的 expressionLanguage 属性来指定。遵守当前版本的 BPEL4WS 的实现必须支持使用 XPath 1.0 作为表达式语言。XPath 1.0 由 expressionLanguage 属性的缺省值来指定,即:

http://www.w3.org/TR/1999/REC-xpath-19991116

对于给出的表达式语言,必须能够从容器中查询数据、抽取属性值以及在表达式中查询链接的状态。本 规范仅为 XPath 1.0 定义这些函数,我们期望其它表达式语言绑定将提供等同的功能。这一节的其余部分特定于 XPath 1.0。

为了使 XPath 1.0 表达式能访问流程中的信息,BPEL4WS 把几个扩展函数增加到 XPath 的内置函数。这些 扩展被定义在标准的 BPEL4WS 名称空间 "http://schemas.xmlsoap.org/ws/2002/07/business-process/"。前缀 "bpws:" 与这个名称空间关联。

用于 XPath 表达式的所有限定名的解析需使用表达式 所处的 BPEL4WS 文档中的当前作用域中的名称空间声明。

本规范定义了以下这些函数:

bpws:getContainerData ('containerName', 'partName', 'locationPath'?)
 

这个函数从容器中抽取值。第一个参数命名了数据的源容器,第二个参数命名了从该容器中选择的部分,第三个可选参数(如果给出的话)提供了绝对位置路径(‘/’表示代表整个部分的文档片断的根)以标识表示该部分的文档片段中的子树的根。这个函数的返回值是包括单个节点的节点集,其中的节点表示整个部分(如果没有给出第三个参数)或基于 locationPath 的选择结果。如果给出的 locationPath 选择的节点集的节点数不是一个,那么遵守本规范的实现必须抛出标准故障 bpws:selectionFailure

在抽象流程中用到的表达式中禁止使用 bpws:getContainerData

bpws:getContainerProperty ('containerName', 'propertyName') 

这个函数从容器中抽取全局属性值。第一个参数命名数据的源容器,第二个参数是从该容器中选择的全局属性的限定名(QName)(请参阅消息属性)。如果给出的属性不出现在该容器的消息类型的任何部分中,那么遵守本规范的实现必须抛出标准故障 bpws:selectionFailure。这个函数的返回值是包括表示该属性的单个节点的节点集。如果给出的属性定义选择的节点集的节点数不是一个,那么遵守本规范的实现必须抛出标准故障 bpws:selectionFailure

bpws:getLinkStatus ('linkName') 

这个函数返回表示链接状态的布尔值(请参阅链接语义)。如果链接状态是正的,那么返回值是 true;如果链接状态是负的,那么返回值是 false。这个函数绝不可以被用于连接条件以外的地方。linkName 参数必须是与连结条件关联的活动的进站的链接名。这些限制必须被静态地强制。

这些 BPEL4WS 定义的扩展函数可在所有的 XPath 1.0 表达式中使用。

以下几个段落将讲述用于 BPEL4WS 的 XPath 1.0 表达式的语法。

8.1.1. 布尔表达式

这些表达式符合 XPath 1.0 Expr 的产生式,其运算结果是布尔值。

8.1.2. 值为截止期限的表达式

这些表达式符合 XPath 1.0 Expr 的产生式,其运算结果是 XML Schema 类型 dateTimedate 的值。请注意,XPath 1.0 并不知晓 XML Schema。也就是说,XPath 1.0 的内置函数都不能产生或处理 dateTime 或 date 值。但是可以写一个符合 XML Schema 定义的常数(文字)并把它用作截止期限值,也可以从 dateTime 类或 date 类的容器(部分)中抽取字段并把它用作截止期限值。XPath 1.0 将把该文字看作 string 文字,但其结果可被解释为 dateTime 或 date 值的词法表示

8.1.3. 值为持续时间的表达式

这些表达式符合 XPath 1.0 Expr 的产生式,其运算结果是 XML Schema 类型 duration 的值。如上所述,XPath 1.0 并不知晓 XML Schema。

8.1.4. 一般表达式

这些表达式符合 XPath 1.0 Expr 的产生式,其结果是任何 XPath 值类型(string、number 或 Boolean)。

有运算符的表达式受以下限制:

  • 所有数值(包括任意常数)可与相等或关系运算符(<、<=、=、!=、>= 和 >)一起用。

  • 整数值(short、int、long、unsignedShort 等等)类型(包括常数)可被用于数值表达式,条件是仅进行整数算术运算。在实际中,这意味着除法是被禁止的。在 XPath 1.0 中进行这种限制是困难的,因为 XPath 1.0 缺乏对类型的完整支持。该限制应被看作意向的声明,当今后的表达式语言有了更完善的类型系统时,该限制将被强制执行。

  • 在与 string 类的值(包括常数)一起使用时,只允许相等运算符(= 和 !=)。

这些限制反映了 XPath 1.0 的语法和语义。在这方面,未来的其它标准将提供更强的类型系统从而支持有更细微的差别的约束。规定这些限制的原因是 XPath 的一般表达式应被用来进行有关业务协议的计算(例如重试循环、单项产品计数等),这些计算在流程定义中必须是透明的。它们的主要用途并不是提供任意计算的。由于这个原因,数值表达式只处理整数计算,而通过表达式进行任意的 string 处理是禁止的。

8.2. 容器

业务流程指定了涉及伙伴间消息交换的有状态的交互。业务流程的状态不仅包括被交换的消息,还包括用于业务逻辑和构造发送给伙伴的消息的中间数据。BPEL4WS 把所有的状态统一地看作一组消息。

容器所提供的方式可用于保存组成业务流程状态的消息。所保存的消息往往是已从伙伴那里接收到的消息或将被发送给伙伴的消息。但是 BPEL4WS 并没有提出这样的要求。容器所保存的消息可以作为用于计算的“临时变量”,这样的消息从不在伙伴间被交换。

每个容器的类型必须是 WSDL 消息类型。但是,BPEL4WS 允许消息类型被直接插入在容器声明中而不是被定义在 WSDL 文档中。提供这种方式的目的是为了避免在 WSDL 文档中乱塞用于“临时变量”的类型。containers 声明的语法如下:

  <containers>
    <container name="ncname" messageType="qname"?>+
      <wsdl:message name="ncname">? 
        ... 
      </wsdl:message>
    </container> 
  </containers> 

以下是有直接插入的消息类型的 container 声明的示例:

  <container name="orderDetails">
     <wsdl:message name="orderDetails">
        <part name="processDuration" type="xsd:duration"/>
     </wsdl:message>
  </container>

如果在 targetNamespace 为 "http://example.com/orders" 的 WSDL 文档中声明相同的消息类型,那么可以这样声明:

  <container   xmlns:ORD="http://example.com/orders"
               name="orderDetails" messageType="ORD:orderDetails"/>

容器可被指定为 invoke、receive 和 reply 等活动的输入容器或输出容器(请参阅调用 Web 服务操作提供 Web 服务操作)。当 invoke 操作返回故障消息时将在当前作用域中产生故障。所接收到的故障消息被用来初始化相应的故障处理程序中的故障容器(请参阅作用域故障处理程序)。

在流程开始的时候,所有的容器都未被初始化。各种各样的方式(包括赋值和接收消息)可被用来初始化容器。属性赋值可使容器被部分初始化,或者,当容器的消息类型中的一些(但不是所有的)部分被赋值时,容器被部分初始化。使用容器中任何未被初始化的部分的企图必须导致标准故障 bpws:uninitializedContainer。遵守本规范的实现可以选择执行静态(执行前)分析,以检测和防止这种故障的出现。这样的分析是悲观的,这是有必要的,所以在有些情况下可能制止不产生故障的流程的使用(在某次使用中或在任何使用中)。

虽然抽象流程是不可执行的,但是 BPEL4WS 要求在使用消息前(例如在 Web 服务操作调用中,在把消息的容器用作 inputContainer 前),消息中的所有消息属性必须被初始化。

8.3. 赋值

在业务流程中,把数据从一个容器复制到另一个容器是常见的任务。assign 活动可以把数据从一个容器复制到另一个容器,也可使用表达式来构造和插入新数据。使用表达式的主要动机是为了进行简单的计算(例如递增序列号)以用于描述业务协议行为。表达式对消息选择、属性和文字常数进行运算以产生容器属性或选择的新值。最后,这个活动还可把服务引用复制到伙伴链接,或把伙伴链接复制到服务引用。

assign 活动可包括一个或多个基本的赋值。

  <assign standard-attributes>
    standard-elements
    <copy>+
      from-spec
      to-spec
    </copy>
  </assign>

assign 活动把类型兼容的值从源(“from-spec”)复制到目的地(“to-spec”)。from-spec 必须是以下形式中的一种(除非是在抽象流程中可用的不透明的形式):

<from container="ncname" part="ncname"? query="queryString"?/>
<from partner="ncname" serviceReference="myRole|partnerRole"/>
<from container="ncname" property="qname"/>
<from expression="general-expr"/>
<from> ... literal value ... </from>
<from opaque="yes">

to-spec 必须是以下形式中的一种:

<to container="ncname" part="ncname"? query="queryString"?/>
<to partner="ncname"/>
<to container="ncname" property="qname"/>

在各种形式中,container 属性提供容器名,part 属性提供该容器中一部分的名称。

from-specto-spec 的第一种形式中,query 的属性值是用于识别源容器部分或目标容器部分中的单个值的查询字符串。BPEL4WS 为用于这些查询的语言提供了可扩展的机制。该语言由 <process> 元素的“queryLanguage”属性来指定。遵守目前版本的 BPEL4WS 的实现必须支持把 XPath 1.0 用作查询语言。XPath 1.0 由 queryLanguage 属性的缺省值来指明,即:

http://www.w3.org/TR/1999/REC-xpath-19991116

对于 XPath 1.0,查询的属性值必须是绝对的 locationPath(‘/’表示代表整个部分的文档片段的根)。它被用来标识代表该部分的文档片段中的子树的根。位置路径必须只选择一个节点。如果位置路径选择了零个节点或不止一个节点,那么遵守本规范的实现必须抛出标准故障 bpws:selectionFailure。请注意,partquery 属性是可选的。而且,在抽象流程中禁止使用 query 属性。

from-specto-spec 的第二种形式允许动态处理与伙伴关联的服务引用。partner 的属性值是在流程中声明的伙伴名称。在 from-spec 的第二种形式中,角色也必须被指定,这是因为流程可能需要表达对应于它自己的角色或伙伴的角色的服务引用。“myRole”的值表示关于伙伴的流程的服务引用是源而“partnerRole”的值表示伙伴的服务引用是源。在 to-spec 的第二种形式中,赋值只能对 partnerRole 进行,所以没有必要指定角色。用于伙伴式的 from/to-spec 的值类型总是服务引用(请参阅服务链接、伙伴和服务引用)。

from-specto-spec 的第三种形式允许显式地处理消息属性(请参阅消息属性)。这种属性形式对抽象流程特别有用,因为它们能够明确地定义消息中的特异数据元素是怎样被使用的。

第四种(“expression”)from-spec 形式允许流程对属性和容器进行简单的计算(例如递增序列号)。

第五种 from-spec 形式允许把给出的文字值作为源值赋给目的地。文字值的类型必须是目的地(to-spec)的类型。通过使用 XML Schema 的实例类型机制(xsi:type),文字值的类型可以可选地与值一起直接插入。

第六种 from-spec 形式(<from opaque="yes">)允许根据不确定的选择赋给不透明的值。这种形式只能出现在抽象流程(即不可执行的流程)中。在赋值的解释中,这种形式的值是从目标的 XSD 值空间中不确定地选出。它只能被用于“to-spec”指的是容器属性的赋值。目标属性的 XSD 类型必须是以下几种类型中的一种:

  • 该类型是从 xsd:string 派生的且受枚举的限制

  • 该类型是从任何 XSD 整数类型派生的且受枚举限制或受 minExclusive 或 minInclusive 和 maxExclusive 或 maxInclusive 的组合的限制

在正常的意义下,使用不透明的值的赋值的流程显然是不可执行的。然而,使用正确类型的随机值的赋值来模拟可能的执行轨迹是可行的。

要使赋值是有效的,由 from 和 to 指定所引用的数据必须属于兼容的类型。以下几点准确地表达了这层含义:

  • from-spec 是容器且 to-spec 是容器。在这种情况下,两个容器必须属于相同的消息类型(如果两个消息类型的限定名是相同的,那么可以说它们是相等的)。

  • 在 from 和 to 指定中,一个是容器,另一个不是。这是非法的,因为在容器的部分、容器部分的选择或服务引用与容器间无法直接地相互赋值。

  • 在所有的其它情况下,源和目的地的类型是 XML Schema 类型,源值必须有与目的地关联的类型。请注意,这并不是要求与源关联的类型和与目的地关联的类型是相同的。具体地说,源类型可以是目的地类型的子类型。

如果违反了以上任何匹配的约束,遵守本规范的实现必须抛出标准故障 bpws:mismatchedAssignmentFailure

8.3.1. 赋值的原子性

在 BPEL4WS 中,赋值的一个重要特征是赋值活动是原子的。如果在赋值活动的执行过程中出现任何故障,那么目的地容器不被改变(目的地容器还是赋值活动开始时的样子)。无论在整个赋值活动中有几个赋值元素,原子性总是成立的。

8.3.2. 赋值示例

该示例假定以下在名称空间“http://tempuri.org/bpws/example”中的复杂类型定义:

<complexType name="address">
  <sequence>
    <element name="number" type="xsd:int"/>
    <element name="street" type="xsd:string"/>
    <element name="city" type="xsd:string"/>
    <element name="phone">
      <complexType>
        <sequence>
          <element name="areacode" type="xsd:int"/>
          <element name="exchange" type="xsd:int"/>
          <element name="number" type="xsd:int"/>
        </sequence>
      </complexType>
    </element>
  </sequence>
</complexType>

假定在相同的目标名称空间中存在以下 WSDL 消息定义:

<message name="person" xmlns:x="http://tempuri.org/bpws/example">
  <part name="full-name" type="xsd:string"/>
  <part name="address" type="x:address"/>
</message>

另外假定以下 BPEL4WS 容器声明:

<container name="c1" messageType="x:person"/>
<container name="c2" messageType="x:person"/>

第一个示例演示了从一个容器复制到另一个容器:

<assign>
  <copy>
    <from container="c1"/>
    <to container="c2"/>
  </copy>
</assign>

8.4. 抽象流程与可执行流程间的差别总结

在用于抽象流程的 XPath 表达式中禁止使用 bpws:getContainerData

在用于抽象流程的赋值中,from-specto-spec 不可以在容器部分中使用查询,也就是说,禁止使用 query 属性。

在抽象流程中,把 from-spec 的特殊不确定形式用于基于属性 opaque 的复制赋值是允许的,但在可执行流程中是禁止的。

9. 基本活动

9.1. 每个活动的标准属性

每个活动的可选的标准属性如下:一个名称、一个连结条件以及一个在连结故障发生时指示是否压制它的指示符。连结条件被用来指定到达一个活动的并行路径的要求。请参阅 Flow,了解有关后两个属性的详细信息。suppressJoinFailure 的缺省值是 no

name="ncname"?
joinCondition="bool-expr"?
suppressJoinFailure="yes|no"?> 

joinCondition 的属性值是本文档所指的表达式语言中的取值为布尔的表达式(请参阅表达式)。缺省表达式语言 XPath 的连结条件的缺省值是该活动的所有进站的链接的链接状态的逻辑“或”。

9.2. 每个活动的标准元素

每个 BPEL4WS 活动有可选的嵌套的标准元素 <source> 和 <target>。在通过链接 建立同步关系时需要使用这些元素(请参阅 Flow)。每个链接被独立地 定义并被命名。链接名被用作 <source> 元素的 linkName 属性值。通过包括一个或多个 <source> 元素,一个活动可以把自己声明为一个或多个链接的。每个 <source> 元素必须使用不同的链接名。类似地,通过包括一个或多个 <target> 元素,一个活动可以把自己声明为一个或多个链接的目标。与某个活动关联的每个 <source> 元素必须使用与那个活动中所有其它 <source> 元素不同的链接名。与某个活动关联的每个 <target> 元素必须使用与那个活动中所有其它 <target> 元素不同的链接名。每个 <source> 元素可以可选地指定过渡条件,用于为跟随这个指定链接而守护(请参阅 Flow)。如果过渡条件被省略,那么可以认为它存在且它的值是常数值 true

<source linkName="ncname" transitionCondition="bool-expr"?/>*
<target linkName="ncname"/>*

9.3. 调用 Web 服务操作

由伙伴提供的 Web 服务(请参阅服务链接、伙伴和服务引用)可被用来执行 BPEL4WS 业务流程中的工作。在这种服务上调用操作是基本活动。如前所述,这种操作可以是同步的请求/响应,也可以是异步的单向操作。对于这两种情况,BPEL4WS 使用相同的基本语法,但对于同步的情况还有一些更多选项。

异步调用仅指定操作的输入容器,这是因为它并不期待作为操作一部分的响应(请参阅提供 Web 服务操作)。同步调用既指定输入容器,也指定输出容器。一个或多个相关集可被指定,以使业务流程实例与位于伙伴处的有状态的服务相关(请参阅相关性)。

在同步调用的情况下,该操作可能返回 WSDL 故障消息。这将导致 BPEL4WS 故障。该活动可以在本地捕获这样的故障,在这种情况下将执行被指定的活动。如果该活动没有在本地捕获故障,那么该故障将被抛到包括该活动的作用域(请参阅作用域故障处理程序)。

请注意,在 BPEL4WS 中,WSDL 故障由限定名来标识,该限定名由相应的 portType 的目标名称空间和故障名所组成。即使这种统一的命名机制与 WSDL 的故障命名模型不能准确地匹配,也必须遵守这种命名机制。因为 WSDL 并不要求故障名在服务操作被定义的名称空间中是唯一的,所以所有共享相同名称且被定义在相同名称空间的故障在 BPEL4WS 中是无法被区分的。在 WSDL 1.1 中,为了唯一地识别故障,有必要指定 portType 名称、操作名和故障名。这就限制了使用故障处理机制来处理调用故障的能力。这是 WSDL 故障模型的重要缺陷,未来的 WSDL 版本将改掉这种缺陷。

最后,一个活动可与用作它的补偿操作的另一个活动关联。这个补偿处理程序可被显式地调用,也可被外层作用域的缺省补偿处理程序来调用(请参阅作用域补偿处理程序)。

从语义上说,本地的故障和/或补偿处理程序的指定等同于紧紧包括该活动并提供那些处理程序的隐式作用域的存在。这种隐式作用域的名称总是与它包括的活动的名称相同。

  <invoke partner="ncname" portType="qname" operation="ncname"
          inputContainer="ncname" outputContainer="ncname"?
          standard-attributes>
    standard-elements
    <correlations>?
       <correlation set="ncname" initiation="yes|no"? 
                    pattern="in|out|out-in"/>+
    </correlations>
    <catch faultName="qname" faultContainer="ncname"?>*
      activity
    </catch>
    <catchAll>?
      activity
    </catchAll>
    <compensationHandler>?
      activity
    </compensationHandler>
  </invoke>

请参阅相关性,了解相关性语义的解释。下面的示例显示了有嵌套的补偿处理程序的调用。在本规范的许多地方还有其它示例。

  <invoke partner="Seller" portType="SP:Purchasing" 
          operation="SyncPurchase" 
          inputContainer="sendPO" 
          outputContainer="getResponse">
    <compensationHandler>
        <invoke partner="Seller" portType="SP:Purchasing" 
                operation="CancelPurchase" 
                inputContainer="getResponse"
                outputContainer="getConfirmation">
    </compensationHandler>
  </invoke>

9.4. 提供 Web 服务操作

业务流程通过 receive 活动和相应的 reply 活动把服务提供给它的伙伴。receive 活动指定了它期望从哪个伙伴那里接收,还指定了它期望伙伴调用的端口类型和操作。

另外,receive 活动还在业务流程的生命周期中扮演角色。在 BPEL4WS 中,实例化业务流程的唯一方法是注解 receive 活动,把 createInstance 属性设置为 "yes"(请参阅 Pick,了解另一种方法)。该属性的缺省值是 "no"。被这样注解的 receive 活动必须是流程中的初始活动,也就是说,可能先于或同时与这种 receive 活动被执行的唯一其它基本活动必须是被类似地注解的 receive 活动。

把一并发的初始活动的 createInstance 属性设置为 "yes" 是允许的。在这种情况下,目的是表达某种可能性,即一组所需的进站消息中的任一个都可以创建流程实例,这是因为这些消息的到达顺序是不可预测的。所有这些 receive 活动都必须使用相同的相关集(请参阅相关性)。遵守本规范的实现必须确保在携带相同相关集标记的进站消息中只有一个消息实际实例化了该业务流程(通常是第一个到达的,但这是实现相关的)。并发初始组中的其它进站消息必须被传递到已经被创建的实例中的相应的 receive 活动。

一个业务流程实例绝不可以为相同的伙伴、portType 和操作同时启用两个 receive 操作。请注意,receive 是阻塞操作,其含义是它的执行在流程实例接收到匹配的消息后才完成。因此,这里表达的约束禁止了逻辑故障,但不是竞赛条件。然而,本规范并不提供检测这种故障的静态分析算法,但是规定如果在业务流程实例的执行中相同伙伴、portType 和操作的两个 receive 操作实际上被同时启用,那么遵守本规范的实现必须抛出标准故障 bpws:conflictingReceive。为了这种约束的目的,pick 中的 onMessage 子句等同于 receive(请参阅 Pick)。

<receive partner="ncname" portType="qname" operation="ncname"
           container="ncname" createInstance="yes|no"?
           standard-attributes>
    standard-elements
    <correlations>?
       <correlation set="ncname" initiation="yes|no"?>+
    </correlations>
</receive>

reply 活动被用来发送对先前通过 receive 活动被接受的请求的响应。这种响应仅对同步交互有意义。异步响应的发送方式总是调用伙伴的服务上的相应的单向操作。请求与相应的回复间的相关性所基于的约束是来自某个 portType 和操作的某个伙伴的多个未完成的同步请求绝不可以在执行中的任何时候是未完成的。如果在业务流程实例中这个约束被违反,那么遵守本规范的实现必须抛出标准故障 bpws:conflictingRequest。请注意,在语义上它不同于 bpws:conflictingReceive,这是因为有可能通过连续接收来自某个 portType 和操作的某个伙伴的相同请求来创建 conflictingRequest。为了这种约束的目的,pick 中的 onMessage 子句等同于 receive(请参阅 Pick)。

  <reply partner="ncname" portType="qname" operation="ncname"
         container="ncname" faultName="qname"?
         standard-attributes>
    standard-elements
    <correlations>?
       <correlation set="ncname" initiation="yes|no"?>+
    </correlations>
  </reply>

9.5. 更新容器内容

容器通过赋值活动来更新,请参阅赋值

9.6. 发出故障信号

当业务流程需要显式地发出内部故障信号时可以使用 throw 活动。每个故障需要有一个全局唯一的 QName。throw 活动须为故障提供这样的名称,还可以可选地提供数据的容器,该容器提供有关故障的更多信息。故障处理程序可以使用这种数据,以分析和处理该故障并植入需被发送到其它服务的所有故障消息。

BPEL4WS 并不要求在 throw 元素中使用故障名前就定义它们。应用程序或特定于流程的故障名可被直接使用,方式是使用合适的 QName 作为 faultName 属性的值并提供有故障数据的容器(如果需要的话)。这提供了很轻量级的机制,可用于引入特定于应用程序的故障。

  <throw faultName="qname" faultContainer="ncname"? standard-attributes>
    standard-elements
  </throw>

下面是不提供有故障数据的容器的 throw 活动的一个简单示例:

<throw xmlns:FLT="http://example.com/faults" faultName="FLT:OutOfStock"/>

9.7. 终止服务实例

terminate 活动可被用来立即放弃执行 terminate 活动的业务流程实例中的所有执行。所有的当前运行的活动必须尽可能快地被终止而不出现任何故障处理和补偿行为。

  <terminate standard-attributes>
    standard-elements
  </terminate>

9.8. 等待

wait 活动允许业务流程指定延迟时间的长短或等到某个截止期限(请参阅表达式,了解持续时间表达式和截止期限表达式的语法)。

  <wait (for="duration-expr" | until="deadline-expr") standard-attributes>
    standard-elements
  </wait>

该活动的典型用途是在某一时刻调用操作(这里是一个常数,但更典型的用法是依赖于流程状态的表达式):

<sequence>
  <wait until="2002-12-24T18:00+01:00"/>
 
  <invoke partner="CallServer" portType="AutomaticPhoneCall" 
          operation="TextToSpeech" 
          inputContainer="seasonalGreeting">
  </invoke>
</sequence>

9.9. 不做任何事

我们经常会需要使用不做任何事的活动,例如在故障需被不捕获和压制时。empty 活动被用于这个目的。它的语法是显然的,也是最简单的。

  <empty standard-attributes>
    standard-elements
  </empty>

10. 结构化的活动

结构化的活动规定了一组活动发生的顺序。它们描述了业务流程是怎样通过把它执行的基本活动组成结构而被创建的,这些结构表达了涉及业务协议的流程实例间的控制形式、数据流程、故障和外部事件的处理以及消息交换的协调。

BPEL4WS 的结构化的活动包括:

  • 活动间一般的顺序控制由 sequenceswitchwhile 来提供。

  • 活动间的并发和同步由 flow 来提供。

  • 基于外部事件的不确定的选择由 pick 来提供。

您可以用一般的方法来递归地使用结构化的活动。需理解的要点是结构化的活动可被任意地嵌套和组合。这样,类似图的控制方式和类似程序的控制方式被自由地组合在一起,这有点特别但很有吸引力,在传统上,这被认为是另一种方法而不是正规的组成功能。第一个示例中就有这种组合使用的简单示例.

需要强调的是在下文中,活动一词既指基本活动也指结构化的活动。

10.1. Sequence

一个 sequence 活动包含一个或多个按顺序执行的活动,执行顺序是这些活动在 <sequence> 元素中被列出的先后顺序,即词法顺序。当 sequence 中的最后一个活动完成后,该 sequence 活动也就完成了。

  <sequence standard-attributes>
    standard-elements
    activity+
  </sequence>

例如:

<sequence>
  <flow>
    ...
  </flow>
  <scope>
    ...
  </scope>
  <pick>
    ...
  </pick>
</sequence>

10.2. Switch

switch 结构化的活动以经常出现的形式支持条件行为。该活动由 case 元素定义的一个或多个条件分支的有序列表组成,后面可跟也可以不跟一个 otherwise 分支。以 switchcase 分支的出现顺序来考虑它们。条件是 true 的第一个分支被选择并被作为该 switch 的被执行的活动。如果有条件的分支都未被选择,那么 otherwise 分支将被选择。如果 otherwise 分支未被显式地指定,那么有 empty 活动的 otherwise 分支将被认为存在。当被选的分支中的活动完成后,switch 活动也就完成了。

  <switch standard-attributes>
    standard-elements
    <case condition="bool-expr">+
      activity
    </case>
    <otherwise>?
      activity
    </otherwise>
  </switch>

例如:

<switch xmlns:inventory="http://supply-chain.org/inventory" 
        xmlns:FLT="http://example.com/faults">
  <case condition= "bpws:getContainerProperty(stockResult,level) > 100">
    <flow>
       <!-- perform fulfillment work -->
    </flow>
  </case>
  <case condition="bpws:getContainerProperty(stockResult,level) >= 0">
    <throw faultName="FLT:OutOfStock" 
           container="RestockEstimate"/>
  </case>
  <otherwise>
    <throw faultName="FLT:ItemDiscontinued"/>
  </otherwise>
</switch>

10.3. While

while 活动支持指定的重复活动的反复执行。执行重复活动直到给出的布尔 while 条件不再被满足。

  <while condition="bool-expr" standard-attributes>
     standard-elements
     activity
  </while>

例如:

    ...
<container name="orderDetails">
   <wsdl:message name="orderDetails">
      <part name="numberOfItems" type="xsd:integer"/>
   </wsdl:message>
</container>
    ...
<while condition=
           "bpws:getContainerProperty(orderDetails,numberOfItems) > 100">
  <scope>
    ...
  </scope>
</while>

10.4. Pick

pick 活动等待一组事件中的一个事件的发生,然后执行与发生的事件关联的活动。事件的发生往往是相互排斥的(某流程接收到接受消息,或者接收到拒绝消息,但不可能同时接收到这两个消息)。如果不止一个事件发生,那么被执行的活动的选择取决于哪个事件先发生。如果多个事件几乎同时发生,那么将出现竞赛且被执行的活动的选择取决于定时和实现。

pick 的形式是一组形式为事件/活动的分支且仅有一个分支被选择,所选的分支与最先发生的事件关联。请注意,当 pick 活动接受处理一个事件后,pick 将不再接受其它事件。可能的事件是某些消息的到达,其形式是进站的单向的或请求/响应操作的调用,或者是基于定时器的“警报”(从闹钟意义上说)。

当业务流程的实例的创建是由于接收到一组可能的消息中的一个消息而发生的时,可以使用 pick 的特殊形式。在这种情况下,pick 本身的 createInstance 属性的值是 yes(这个属性的缺省值是 no)。在这种情况下,pick 中的事件必须都是进站的消息,每一个等同于属性为 "createInstance=yes"receive。对于这种特殊情况,不允许出现警报。

每个 pick 活动必须至少包括一个 onMessage 事件。对于 bpws:conflictingReceive 故障和 bpws:conflictingRequest 故障的出现的语义来说,启用每个 onMessage 处理程序等同于启用相应的 receive 活动(请参阅提供 Web 服务操作)。

  <pick createInstance="yes|no"? standard-attributes>
    standard-elements
    <onMessage partner="ncname" portType="qname"
               operation="ncname" container="ncname">+
      <correlations>?
         <correlation set="ncname" initiation="yes|no"?>+
      </correlations>
      activity
    </onMessage>
    <onAlarm (for="duration-expr" | until="deadline-expr")>*
      activity
    </onAlarm>
  </pick>

当 pick 的一个分支被其关联的事件的发生触发且相应的活动完成后,pick 活动就完成了。下面的示例展示了 pick 的典型用法。这个 pick 活动可以出现在接受大定单的单项产品的循环中,但是启用了完成操作,以作为另一个事件。

<pick>
  <onMessage partner="buyer"
                portType="orderEntry"
                operation="inputLineItem"
                container="lineItem">
        <!-- activity to add line item to order -->
  </onMessage>
  <onMessage partner="buyer"
                portType="orderEntry"
                operation="orderComplete"
                container="completionDetail">
        <!-- activity to perform order completion -->
   </onMessage>

   <!-- set an alarm to go after 3 days and 10 hours -->
   <onAlarm for="P3DT10H">
        <!-- handle timeout for order completion  -->
   </onAlarm>
</pick>

10.5. Flow

flow 构造能提供并发和同步。flow 的语法是:

  <flow standard-attributes>
    standard-elements
    <links>?
      <link name="ncname">+
    </links>
    activity+
  </flow>

对于嵌套在 flow 中的活动来说,standard attributesstandard elements 尤为重要,因为 standard attributes 和 standard elements 主要是为活动提供与 flow 有关的语义而存在的。

把一组活动集中在 flow 中的最基本的语义效果是使它们能并发地执行。当 flow 中的所有活动完成时,flow 也就完成了。flow 中的一个活动的完成可能表明如果该活动的启用条件变为 false,那么该活动不再被执行(请参阅删除死路)。所以,flow 的最简单用法等同于嵌套的并发构造。在下面的示例中,在 flow 被启动后,两个 invoke 活动立刻被并发地启动。当 seller(卖方)和 shipper(承运人)都响应后,flow 就完成了(假定 invoke 操作是同步请求/响应)。仅当 flow 完成后才调用 bank(银行)。

  <sequence>
    <flow>
       <invoke partner="Seller" .../>
       <invoke partner="Shipper" .../>
    </flow>
    <invoke partner="Bank" .../>
  </sequence>

更为广义地说,flow 活动创建了一组直接嵌套在其中的并发活动。它能进一步表达直接或间接嵌套在其中的活动间的同步相关性。link 构造被用来表达这种同步相关性。一个 link 有一个名称,flow 活动的所有链接必须在 flow 活动中被分开定义(请参阅发展方向,了解链接被分开定义的原因)。活动的标准的 sourcetarget 元素被用来链接两个活动。链接的源必须指定指定链接名的 source 元素,链接的目标必须指定指定链接名的 target 元素。源活动也可以通过 source 元素的 transitionCondition 属性来指定过渡条件。如果 transitionCondition 属性被省略,那么该属性被认为存在且它的值是 "true"。在 flow 活动中声明的每个 link 必须在该 flow 中恰好有一个活动作为它的源而且在该 flow 中恰好有一个活动作为它的目标。link 的源和目标可以被任意深地嵌套在直接被嵌套在该 flow 中的(结构化的)活动中,但是这要受边界跨越的限制。

下面的示例显示了可以跨越结构化的活动的边界的 link。名为“CtoD”的 link 从 sequence Y 中的活动 C 开始,到直接被嵌套在外层 flow 中的活动 D 结束。另外,该示例还演示了 sequence X 的执行必须先于 sequence Y 的执行,因为 X 是名为“XtoY”的 link 的源而该 link 的目标是 sequence Y。

  <flow>
    <links>
      <link name="XtoY"/>
      <link name="CtoD"/>
    </links>
    <sequence name="X">
       <source linkName="XtoY"/>
       <invoke name="A" .../>
       <invoke name="B" .../>
    </sequence>
    <sequence name"Y">
       <target linkName="XtoY"/>
       <receive name="C"/>
         <source linkName="CtoD"/>
       </receive>
       <invoke name="E" .../>
    </sequence>
    <invoke partner="D">
      <target linkName="CtoD"/>
    </invoke>
  </flow>

一般来说,在以下情况下,link 跨越了语法构造的边界:link 的源活动被嵌套在该构造中但目标活动并不在其中,或相反,link 的目标活动被嵌套在该构造中但源活动并不在其中。

link 绝不可以跨越 while 活动、可序列化作用域或补偿处理程序的边界(请参阅作用域)。另外,跨越故障处理程序边界的 link 必须是出站的,也就是说,它的源活动必须在故障处理程序中,它的目标活动必须在包括与该故障处理程序关联的作用域的作用域中。最后,link 绝不可以创建控制循环,也就是说,源活动的逻辑优先活动绝不可以是目标活动,目标活动绝不可以逻辑优先于源活动(如果从语义上说活动 B 的初始化需要活动 A 的完成,那么 A 逻辑优先于 B)。所以,由 link 创建的有向图总是无环的。

10.5.1. 链接语义

在剩余的这部分中,源活动是 A 的链接将被称为 A 的出站链接,目标活动是 A 的链接将被称为 A 的进站链接。如果链接的源活动和目标活动分别是 Y 和 X,那么 X 同步依赖于 Y。

每个作为链接目标的活动有一个与它关联的显式或隐式 joinCondition 属性。甚至只有一个进站链接的活动也是这样。如果没有显式 joinCondition,那么隐式条件要求至少一个进站链接状态是正的(下面有链接状态的说明)。连结条件是布尔表达式(请参阅表达式 )。活动的连结条件的表达式必须使用布尔运算符和应用到该活动的进站链接的 bpws:getLinkStatus 函数来构造(请参阅表达式)。

在不考虑链接的情况下,业务流程、作用域和结构化的活动的语义决定了给定的活动在什么时候开始执行。例如,sequence 中的第一个活动完成后,第二个活动就可以开始执行了。定义 switch 中的分支的行为的活动的执行条件是该分支被选择并将被执行。类似地,当 flow 开始执行后,直接嵌套在该 flow 中的活动就可以开始执行了,因为从本质上说 flow 是并发的构造。

如果在这种意义上就绪的活动有进站链接,那么,要等到该活动的所有进站链接状态被确定且与该活动关联的(显式或隐式)连结条件的值被求出,该活动才开始执行。链接状态的求值的准确语义如下:

当活动 A 完成时将执行以下步骤以确定同步链接对今后执行的影响:

  • 确定 A 的所有出站的链接状态。该状态要么是正的要么是负的。为了确定每个链接的状态,该链接的 transitionCondition 值将被求出。如果求出的值是 true,那么该状态是正的,反之则为负的。

  • 对于每个同步依赖于 A 的活动 B,检查:
    • 在以上描述的意义下 B 已可以开始执行(除了对进站的链接的依赖性)。

    • B 的所有进站的链接状态被确定。
  • 如果这两个条件都是 true,那么求出 B 的连结条件的值。如果连结条件的值是 false,那么标准故障 bpws:joinFailure 被抛出,否则,开始执行活动 B。

如果在结构化的活动 S 的执行过程中,S 的语义要求嵌套在 S 中的活动 X 不作为执行 S 的一部分被执行,那么来自 X 的所有出站链接的状态被设置为负的。在 switch 活动中不被选择的分支中的活动就是一例,还有,在因故障(包括 bpws:joinFailure)而终止处理的作用域中未完成的活动也是一个例子(请参阅作用域补偿处理程序)。

请注意,一般来说,根据有多个出站链接的活动的完成情况,多个目标活动将被并行地启用;因此,这种活动常被称为 fork 活动。

10.5.2. 死路删除(Dead-Path-Elimination,DPE)

在控制流主要由链接网络定义的情况下,对活动 A 的 false 连结条件的正常解释是不应该执行 A 而不是发生故障。此外,有必要通过把负状态赋给 A 的出站链接来传播该决定的后果。通过使用活动上的属性 suppressJoinFailure,BPEL4WS 简化了这种语义的表达。该属性的值为 "yes" 的后果是在该活动及其嵌套的活动中压制 bpws:joinFailure 故障,但是把嵌套的活动中的 suppressJoinFailure 属性设为 "no" 后将覆盖这个后果。压制 bpws:joinFailure 等同于由特别的缺省处理程序逻辑地捕获该故障,该处理程序被附加到紧紧包括有那个连结条件的活动的隐式作用域。缺省处理程序的行为是 empty 活动,也就是说,处理程序压制了故障但不作任何处理。但是,由于有连结条件的活动未被执行,根据链接语义的规则,它的出站链接被自动地赋给负状态。所以,在 suppressJoinFailure 属性被设为 "yes" 的活动中,连结条件的值为 false 的语义是跳过关联活动的执行并把该活动的所有出站链接状态设为负。这被称为死路删除,因为在有过渡条件的链接网络的类似图的解释中,这些语义的后果是在由连续的链接组成的整个路径上传播负链接状态直到遇到值为 true 的连结条件。

请注意,紧紧包括有连结条件的活动的隐式作用域(创建的目的是压制 bpws:joinFailure)的名称与该活动本身的名称完全相同。如果这是有直接插入的故障处理程序或补偿处理程序的调用活动(请参阅调用 Web 服务操作),那么故障处理程序和补偿处理程序的隐式作用域将与这里描述的隐式作用域合并,这样,bpws:joinFailure 又多了一个故障处理程序。

suppressJoinFailure 属性的缺省值是 "no"。这是为了在简单的用例中避免意外的行为,这里的“简单的用例”指的是不涉及复杂图且没有过渡条件的链接被用于同步的用例。这种用例的设计者很可能没有完全理解链接的语义,他们会对压制明确定义的故障的缺省解释的后果感到奇怪。例如,请考虑把第一个示例suppressJoinFailure 属性设为 "yes" 并考虑这个示例的解释。请进一步考虑 shippingProvider 的调用被包括在提供故障处理程序的作用域中(请参阅作用域故障处理程序)。如果这些调用中的一个调用发生故障,那么来自该调用的出站链接状态将是负的,位于链接目标的(隐式)连结条件将是 false,但是产生的 bpws:joinFailure 将被隐式地压制且目标活动将在序列中被无声地跳过而不是引起预期的故障。

如果需要全面压制 bpws:joinFailure,那么容易的实现方式是在业务流程定义的根部即整个 process 元素中使用值为 "yes"suppressJoinFailure 属性。

10.5.3. Flow 图的示例

在下面的示例中,flow 活动定义了一个图,它的节点是一些活动,这些活动的名称是:getBuyerInformationgetSellerInformationsettleTradeconfirmBuyerconfirmSeller。定义的链接有:

  • 名为 buyToSettle 的链接从 getBuyerInformation(在嵌套在 getBuyerInformation 中的相应的 source 元素中指定)开始到 settleTrade (在嵌套在 settleTrade 中的相应的 target 元素中指定)结束。

  • 名为 sellToSettle 的链接从 getSellerInformation 开始到 settleTrade 结束。

  • 名为 toBuyConfirm 的链接从 settleTrade 开始到 confirmBuyer 结束。

  • 名为 toSellConfirm 的链接从 settleTrade 开始到 confirmSeller 结束。

根据 flow 所定义的图结构,getBuyerInformation 活动和 getSellerInformation 活动可并行地运行。在这两个活动都被完成前不执行 settleTrade 活动。在 settleTrade 完成这两个活动后,confirmBuyerconfirmSeller 被再次并行地执行。

<flow suppressJoinFailure="yes">
  <links>
    <link name="buyToSettle"/>
    <link name="sellToSettle"/>
    <link name="toBuyConfirm"/>
    <link name="toSellConfirm"/>
  </links>
  <receive name="getBuyerInformation">
    <source linkName="buyToSettle"/>
   </receive>
   <receive name="getSellerInformation">
    <source linkName="sellToSettle"/>
   </receive>
   <invoke name="settleTrade" 
           joinCondition="buyToSettle AND sellToSettle">
    <target linkName="getBuyerInformation"/>
    <target linkName="getSellerInformation"/>
    <source linkName="toBuyConfirm"/>
     <source linkName="toSellConfirm"/>
   </invoke>
  <reply name="confirmBuyer">
    <target linkName="toBuyConfirm"/>
  </reply>
  <reply name="confirmSeller">
    <target linkName="toSellConfirm"/>
  </reply> 
</flow>

10.5.4. 链接和结构化的活动

链接可以跨越结构化的活动的边界。在跨越时必须确保业务流程的正确行为。下面的示例将说明当链接的目标活动在结构化的构造中时业务流程的行为。

下面的 flow 被用来执行活动 A、B 和 C 的 sequence。活动 B 同步依赖于该序列外的活动 X 和活动 Y,也就是说,B 是来自 X 和 Y 的链接的目标。没有位于 B 的连结条件,所以隐式地假定为缺省值,即目标为 B 的链接状态的析取。所以,如果至少一个进站链接是正状态,那么该条件是 true。在这种情况下,根据链接上的过渡条件,该条件可被表示为布尔条件 P(X,B) OR P(Y,B)

在该 flow 中,当该 flow 开始执行时,名为 S 的 sequence 和两个 receive 活动 X 和 Y 被并发地启动。在 S 中,在活动 A 被完成后,在确定来自 X 和 Y 的进站链接的状态和求出隐式连结条件的值之前,B 无法开始执行。当活动 X 和 Y 完成执行后,B 的连结条件的值被求出。

假设表达式 P(X,B) OR P(Y,B) 的值是 false。在这种情况下将抛出标准故障 bpws:joinFailure,因为环境属性 suppressJoinFailure 被设置为 "no"。所以,该 flow 的执行被中断,B 和 C 都不被执行。

在另一方面,如果环境属性 suppressJoinFailure 被设置为 "yes",那么 B 的执行将被跳过但将执行 C,因为与 B 关联的隐式作用域将压制 bpws:joinFailure

<flow suppressJoinFailure="no">
  <links>
    <link name="XtoB"/>
    <link name="YtoB"/>
  </links>

  <sequence name="S">
    <receive name="A" ...>
      ...
    </receive>
    <receive name="B" ...>
      <target linkName="XtoB"/>
      <target linkName="YtoB"/>
      ...
    </receive>
    <receive name="C" ...>
      ...
    </receive>
  </sequence>

  <receive name="X" ...>
    <source linkName="XtoB" transitionCondition="P(X,B)"/>
    ...
  </receive>
  <receive name="Y" ...>
    <source linkName="YtoB" transitionCondition="P(Y,B)"/>
    ...
   </receive>
</flow>

最后,假定略微改写上面的 flow:把 A、B 和 C 从 sequence 中拿出,通过链接把它们链接起来(过渡条件为常数真值 "true")。现在,B 和 C 将总被执行。因为连结条件是析取且链接 AtoB 的过渡条件是常数 "true",所以连结条件的值总是 "true",且与 P(X,B) 和 P(Y,B) 的值无关。

<flow suppressJoinFailure="no">
  <links>
    <link name="AtoB"/>
    <link name="BtoC"/>
    <link name="XtoB"/>
    <link name="YtoB"/>
  </links>
  <receive name="A">
    <source linkName="AtoB"/>
   </receive>
   <receive name="B">
    <target linkName="AtoB"/>
    <target linkName="XtoB"/>
    <target linkName="YtoB"/>
    <source linkName="BtoC"/>
   </receive>
  <receive name="C">
    <target linkName="BtoC"/>
  </receive>
  <receive name="X">
    <source linkName="XtoB" transitionCondition="P(X,B)"/>
   </receive>
  <receive name="Y">
    <source linkName="YtoB" transitionCondition="P(Y,B)"/>
   </receive>
</flow>

11. 作用域

每个活动的执行上下文由 scope(作用域)来提供。scope 可以提供故障处理程序、补偿处理程序、数据容器和相关集。

在当前的 BPEL4WS 版本中,在由整个 process 定义的全局的 scope 中只允许出现容器和相关集。请参阅未来的发展方向,了解在未来的版本中预期的更改。

从语法上说,所有的 scope 元素是可选的,当有些 scope 元素被省略的时候,它们有缺省的语义。下面将详细地说明 scope 的语法和语义。

<scope containerAccessSerializable="yes|no" standard-attributes>
    standard-elements
    <faultHandlers>?
      ...
    </faultHandlers>
    <compensationHandler>?
      ...
    </compensationHandler>
    activity
  </scope>

每个 scope 有一个定义它的正常行为的主要活动。该主要活动可以是一个复杂的结构化的活动,其中有任意深度的许多嵌套的活动。所有的嵌套的活动都共享该 scope。在下面的示例中,该 scope 有一个主要的 flow 活动,其中包括两个并发的 invoke 活动。任一个 invoke 活动可以接收一种类型或多种类型的故障响应。该 scope 的故障处理程序被两个 invoke 活动所共享,该故障处理还可被用来捕获由可能的故障响应所引起的故障。

  <scope>
    <faultHandlers>?
      ...
    </faultHandlers>
    <flow>
       <invoke partner="Seller" portType="Sell:Purchasing" 
               operation="SyncPurchase" 
               inputContainer="sendPO" 
               outputContainer="getResponse"/>
       <invoke partner="Shipper" 
               portType="Ship:TransportOrders" 
               operation="OrderShipment" 
               inputContainer="sendShipOrder" 
               outputContainer="shipAck"/>
    </flow>
  </scope>

11.1. 业务流程中的错误处理

业务流程的持续时间往往较长,它在通信中使用异步消息。它还处理后端数据库和行业应用程序中的敏感的商业数据。在这样的环境中的错误处理是困难的但对业务的正常运行却是至关重要的。ACID 事务的使用往往限于本地的更新,这是因为在可以产生技术错误、商业错误和故障条件的业务流程实例的长期运行中,锁和隔离无法被保持,另外还有信任问题。结果,在许多 ACID 事务被提交期间,整个业务事务可能失败或被取消,已完成的部分工作必须被尽可能妥善地撤销。所以,业务流程中的错误处理在很大程度上依赖于众所周知的补偿概念,也就是说,企图撤消先前的活动的效果的特定于应用程序的活动,其中,“先前的活动”是正在被放弃的较大的工作单元的一部分。在有关 Sagas 的使用 [10] 和开放的嵌套事务 [11] 等领域,已经有人进行了长期的研究。BPEL4WS 提供这种补偿协议的变种,即提供撤消的灵活控制的能力。为此,BPEL4WS 提供了以特定于应用程序的方式定义故障处理和补偿的能力,由此产生的特征被称为长期运行的(业务)事务(Long-Running(Business)Transactions,LRT)。

这里描述的 LRT 概念应被完全地用于特定于平台的实现中,理解这一点很重要。并没有规定业务流程须跨越(或被分布于)多个供应商和平台。在这种环境中,WS-Transaction 规范 [12] 应被用来注册对 LRT 实现提供的撤消通知感兴趣的参与者。请参阅附录 C,了解有关基于 WS-Transaction 概念的 BPEL4WS LRT 模型的详细信息。

另外,这里描述的 LRT 概念完全是本地的且发生在单个业务流程实例中,理解这一点很重要。在多方参与的服务中,没有关于一致赞成的结果的分布式协调。分布式协定的实现是超出 BPEL4WS 范围的传统问题,使用 WS-Transaction 规范中描述的协议可解决这个问题。我们已认识到用 BPEL4WS 来撰写 WS-transaction 的需求。未来的发展方向将进一步阐述这个问题。

作为 LRT 的一个示例,请考虑旅行路线的安排和实现。这可被看作个别服务预约可使用整个 LRT 的作用域中嵌套的事务的 LRT。如果路线被取消,那么预约事务必须由取消事务来补偿,对应的支付事务也必须被相应地补偿。对于在数据库中的 ACID 事务,事务协调器和它们控制的资源知道所有未提交的更新和这些更新必须被撤销的顺序,它们能完全控制这样的撤销。在业务事务中,补偿行为本身是业务逻辑和协议的一部分且必须被显式地指定。例如,根据机票的舱位和时间,对于取消机票的预约可能有处罚或收费。如果旅行的预付款已被支付,那么在预付前必须成功地取消预定,因为可通过减少预付款来撤销。这意味着补偿操作需按原先事务的顺序来运行,这在多数事务系统中并不是标准的或缺省的作法。通过使用活动作用域作为逻辑工作单元的定义,BPEL4WS 的 LRT 功能能够满足这些要求。

11.2. 补偿处理程序

通过补偿处理程序,作用域可以描述一部分通过应用程序定义的方式的可撤销的行为。有补偿处理程序和故障处理程序的作用域可不受约束地任意深地被嵌套。

11.2.1. 定义补偿处理程序

在目前的 BPEL4WS 版本中,补偿处理程序仅仅是补偿活动的包装,如下所示。在许多情况下,补偿处理程序需要接收有关世界的当前状态的数据并返回关于补偿结果的数据。在下面的部分及未来的发展方向中将进一步讨论这个问题。

  <compensationHandler>?
    activity
  </compensationHandler>

调用 Web 服务操作中已讲过,invoke 活动有一个特别的快捷方式,即直接插入补偿处理程序而不是显式地使用紧贴的外层作用域。例如:

  <invoke partner="Seller" portType="SP:Purchasing" 
          operation="SyncPurchase" 
          inputContainer="sendPO" 
          outputContainer="getResponse">
    <correlations>
       <correlation set="PurchaseOrder" initiation="yes" 
                    pattern="out"/>
    </correlations>

    <compensationHandler>
        <invoke partner="Seller" portType="SP:Purchasing" 
                operation="CancelPurchase" 
                inputContainer="getResponse"
                outputContainer="getConfirmation">
          <correlations>
              <correlation set="PurchaseOrder" pattern="out"/>
          </correlations>
        </invoke>
    </compensationHandler>
  </invoke>

在这个示例中,原先的 invoke 活动买件东西,在购买需要被补偿的情况下,compensationHandler 调用在同一伙伴的同一端口上的取消操作(使用对购买请求的响应作为输入)。

在标准的语法中(不用 invoke 的快捷方式)可以等同地表示这个示例,如下所示:

  <scope>
    <compensationHandler>
        <invoke partner="Seller" portType="SP:Purchasing" 
                operation="CancelPurchase" 
                inputContainer="getResponse"
                outputContainer="getConfirmation">
           <correlations>
              <correlation set="PurchaseOrder" pattern="out"/>
           </correlations>
        </invoke>
    </compensationHandler>
    <invoke partner="Seller" portType="SP:Purchasing" 
            operation="SyncPurchase" 
            inputContainer="sendPO" 
            outputContainer="getResponse">
       <correlations>
          <correlation set="PurchaseOrder" initiation="yes" 
                       pattern="out"/>
       </correlations>
    </invoke>
  </scope>

请注意,在补偿被调用前,为了其它目的,容器 getResponse 可在后来被再次使用。但是补偿处理程序需要对正被撤销的 invoke 操作的具体响应。BPEL4WS 语义规定补偿处理程序(如果被调用的话)将看到所有容器的冻结的快照,这些容器就如同正被补偿的作用域的执行已被完成时那样。换句话说,如果这里显示的补偿处理程序被使用,那么补偿处理程序所看到并使用的 getResponse 的内容正是它所补偿的 invoke 活动在完成时的内容。这也意味着补偿处理程序无法更新业务流程正在使用的容器中的活跃数据。它们完全生存在快照世界中。补偿处理程序(一旦被安装后)可被看作完全独立的操作,业务流程实例的全局状态既不影响它也不受它的影响。它只影响外部的实体。

期待补偿活动总是不注意世界的当前状态是不现实的。事实上,补偿既影响当前状态也受当前状态的影响。但是,补偿在运行时所处的世界的形式是难以预测的。所以,有必要使补偿活动与活跃世界间的双向交互能够在严格控制的方式下进行。为此,未来的 BPEL4WS 将向补偿处理程序添加输入和输出参数(请参阅未来的发展方向)。

流程的生命周期已讲过,如果补偿处理程序是为整个业务流程而指定的,那么特定于平台的方式可以在业务流程实例正常完成后补偿它。这个功能的启用方法是把 processenableInstanceCompensation 属性设置为 "yes"

11.2.2. 调用补偿处理程序

补偿处理程序的调用方法是使用 compensate 活动,该活动命名了执行补偿所在的作用域,也就是说,将被执行的补偿处理程序的作用域。仅当作用域正常完成执行后该作用域的补偿处理程序才可被调用。调用未被安装的补偿处理程序等同于 empty 活动(它是 no-op)— 这就确保了故障处理程序不必依赖于状态来决定哪个嵌套的作用域已成功地完成。如果一个已被安装的补偿处理程序被调用的次数大于一,那么遵守本规范的实现必须抛出标准故障 bpws:repeatedCompensation

请注意,在 invoke 活动定义了直接插入的补偿处理程序的情况下,该活动的名称是将被用于 compensate 活动的 scope 名。

  <compensate scope="ncname"? standard-attributes>
    standard-elements
  </compensate>

显式地执行 compensate 活动的能力是 BPEL4WS 的应用程序控制的错误处理框架的基础所在。该活动只能被用于业务流程的以下部分中:

  • 在作用域的 fault 处理程序中,该作用域紧紧包括补偿将被执行的作用域。

  • 在作用域的补偿处理程序中,该作用域紧紧包括补偿将被执行的作用域。

例如:

<compensate scope="RecordPayment"/>

如果按名称被显式地补偿的作用域在循环中被执行,那么在后续的迭代中补偿处理程序的实例将按相反的顺序被执行。

如果作用域没有补偿处理程序,那么缺省补偿处理程序调用紧贴的外层作用域的补偿处理程序,调用的顺序与这些作用域的完成顺序相反。

如果 compensate 活动中的作用域名被省略,那么这种 <compensate/> 形式将导致缺省行为被显式地调用。当外层的故障处理程序或补偿处理程序在为内层作用域执行缺省的补偿的同时还需要进行另外的工作(例如更新容器或发送对外的通知)的时候,这种形式是有用的。请注意,附加到作用域 S 的故障处理程序或补偿处理程序中的 <compensate/> 活动导致按缺省顺序调用直接嵌套在 S 中的已完成的作用域的补偿处理程序。除了显式地调用 <compensate scope="Sx"/>(作用域 Sx 被直接嵌套在 S 中),这种活动的使用可与任何其它用户指定的行为组合。正如期待的那样,嵌套于 S 的作用域的显式补偿调用禁用了缺省顺序的补偿。

11.3. 故障处理程序

业务流程中的故障处理可被看作从作用域中的正常处理切换出来的模式。附加到作用域的可选的故障处理程序提供了定义一组定制的故障处理活动的方法,其语法定义为 catch 活动。被定义的每个 catch 活动能拦截某种故障(由全局唯一的故障 QName 和有与该故障关联的数据的容器来定义)。如果没有故障名,那么 catch 将拦截全部有适合类型的故障数据的故障。故障容器是可选的,这是因为故障可能没有与之关联的额外数据。对 invoke 活动的故障响应是故障的来源之一,根据 WSDL 操作中的故障定义,该故障有明显的名称和数据部分。通过编程,throw 活动也是故障的来源,它也有显式给出的名称和数据。BPEL4WS 定义了若干个有名称和数据的标准故障,还可以有其它特定于平台的故障,例如在业务流程的执行中发生的通信故障。catchAll 子句可被用来捕获所有未被更特定的 catch 处理程序捕获的故障。

  <faultHandlers>?
    <!-- there must be at least one fault handler or default -->
    <catch faultName="qname"? faultContainer="ncname"?>*
      activity
    </catch>
    <catchAll>?
      activity
    </catchAll>
  </faultHandlers>

虽然补偿的使用是故障处理程序的行为的重要部分,但是每个处理程序执行任意的活动,甚至可以是 <empty/>。在有故障处理程序的时候,故障处理程序将负责处理该故障。它可能再次抛出相同的故障或不同的故障,或者它可能这样处理故障:进行清除并使正常的处理在外层作用域中继续。

发生故障的作用域可被认为是异常结束的作用域,无论该故障是否被捕获并处理而没有被故障处理程序再次抛出。从不为发生故障的作用域安装补偿处理程序。

如果作用域 S 的故障处理程序处理了发生在 S 的故障而没有再次抛出,那么在故障被处理后,以 S 作为源的链接的状态值可被正常地求出,因为在外层作用域中的处理应被继续。

调用 Web 服务操作中已讲过,invoke 活动有一个特别的快捷方式,即直接插入补偿处理程序而不是显式地使用紧贴的外层作用域。例如:

  <invoke partner="Seller" 
          portType="SP:Purchasing" 
          operation="SyncPurchase" 
          inputContainer="sendPO" 
          outputContainer="getResponse">
    <catch faultName="SP:POFault" faultContainer="POFault">
      <!-- handle the fault -->
    </catch>
  </invoke>

在这个示例中,原先的 invoke 活动买件东西,一个故障处理程序被直接插入,以处理购买请求导致了故障响应这种情况。在标准的语法中(不用 invoke 的快捷方式)以等同地表示这个示例,如下所示:

<scope>
  <faultHandlers>
    <catch faultName="SP:POFault" faultContainer="POFault">
      <!-- handle the fault -->
    </catch>
  </faultHandlers>
  <invoke partner="Seller" 
          portType="SP:Purchasing" 
          operation="SyncPurchase" 
          inputContainer="sendPO" 
          outputContainer="getResponse">
  </invoke>
</scope>

当作用域 C 的执行正常完成后,紧贴的外层作用域的故障处理程序和补偿处理程序可以调用作用域 C 的补偿处理程序。当作用域 C 的执行已开始但没完成的时候,C 的故障处理程序可被调用。如果作用域在完成执行前发生故障,那么合适的故障处理程序取得控制,所有其它故障处理程序被卸载。在任何情况下,为同一个作用域运行多个故障处理程序是不可能的。

请注意,可用性也适用于隐式故障处理程序和补偿处理程序

执行作用域 C 的故障处理程序的第一步是隐式地终止直接包括在 C 中的所有正在执行的活动的执行(请参阅活动终止的语义)。在故障处理程序的具体行为开始前就终止了正在执行的活动。这同样适用于下面描述的隐式故障处理程序。

11.3.1. 隐式故障处理程序和补偿处理程序

因为作用域名和补偿处理程序的可见性限于下一个外层作用域,所以,如果外层作用域没有补偿处理程序或没有某个故障的故障处理程序,那么将无法补偿作用域。因为许多故障并不是程序引起的(或者说许多故障并不是操作调用的结果),所以期望在每个作用域中每个故障都有显式处理程序是不合理的。因此,在没有显式处理程序的情况下,BPEL4WS 提供缺省的补偿处理程序和故障处理程序。这些隐式处理程序的行为是按完成相应的作用域的相反的顺序运行可用的补偿处理程序。下面将用更准确的语言来定义这种行为。

如果任何给定的作用域没有(任何故障的)故障处理程序或补偿处理程序,那么它们将被隐式地创建,它们的行为如下:

故障处理程序

  • 按完成相应的作用域的相反的顺序运行所有紧贴的外层作用域的补偿处理程序。

  • 把故障抛向下一个外层作用域。

补偿处理程序

  • 按完成相应的作用域的相反的顺序运行所有紧贴的外层作用域的补偿处理程序。

11.3.2. 活动终止的语义

如前所述,执行作用域 C 的故障处理程序的第一步是隐式地终止直接包括在 C 中的所有正在执行的活动的执行。以下几段定义了这句话对所有 BPEL4WS 活动类型的含义。

invokereplyassign 等活动能较快结束,所以允许它们完成执行而不是在强制终止时中断它们。已开始的表达式的求值也被允许完成。和 wait 一样,receive 活动被过早地中断并终止。终止概念不适用于 emptyterminatethrow

所有的结构化的活动行为被中断。while 的迭代被中断且终止被应用到循环体活动。如果 switch 已选择了一个分支,那么终止被应用到被选分支的活动。对于 pick 来说也是这样。如果这些活动中的任一个尚未选择分支,那么 switchpick 将停止执行并被终止。sequenceflow 构造是这样被终止的:终止它们的执行并把终止应用到嵌套在其中的所有当前活动的活动。

作用域在某种程度上提供了控制强制终止的语义的能力。当被终止的活动实际上是作用域时,该作用域的执行被中断并运行标准故障 bpws:forcedTermination 的故障处理程序。请注意,这仅适用于在正常处理模式中的作用域。如果作用域已遇到内部故障且在执行故障处理程序,那么如上所述,所有其它故障处理程序(包括 bpws:forcedTermination 的处理程序)被卸载,强制的终止没有影响。已开始执行的故障处理程序被允许完成它的执行。

bpws:forcedTermination 故障的故障处理程序的编写与其它故障处理程序的编写相似,但这个故障处理程序不能再次抛出任何故障。即使在它的执行中发生未捕获的故障,该故障也不被再次抛向下一个外层作用域。这是因为外层作用域已发生故障,这也是引起嵌套的作用域的被强制终止的原因。

在其它方面,上述故障处理程序是正常的故障处理程序。执行该处理程序的第一步是隐式地(递归地)终止执行所有被直接包括在它的关联作用域中的正在执行的活动。它可以执行补偿活动。如果没有该处理程序,那么将使用用于所有其它隐式故障处理程序的同一隐式行为来提供。

请注意,嵌套的作用域的强制终止顺序是从最里面开始,这是因为(如上所述)执行所有故障处理程序的第一步是隐式地(递归地)终止执行所有被直接包括在它的关联作用域中的正在执行的活动。

11.3.3. 故障处理程序和补偿处理程序中的故障处理

补偿处理程序总是作为处理某个故障处理程序 E 的一部分被直接地或间接地调用。E 调用的补偿处理程序的执行可能引起故障被抛出。如果该故障未被 E 调用的补偿处理程序链中的作用域所捕获,那么该故障将被看作 E 中的故障。

如果在执行作用域 C 的故障处理程序 E 时有故障被抛出,那么可以使用 E 的作用域来捕获该故障。如果 E 中的作用域没有捕获该故障,那么该故障被立即抛向 C 的父作用域且 E 的执行被过早地终止。实际上,E 有意再次抛出的故障与 E 的执行中意外发生的故障之间没有区别。

11.4. 可序列化的作用域

当 containerAccessSerializable 属性被设置为 "yes" 时,该作用域在管理对共享容器的访问方面提供了并发控制。这样的作用域被称为可序列化的作用域(serializable scope)。可序列化的作用域绝不可以是嵌套的。被标记为 containerAccessSerializable="yes" 的作用域必须是叶作用域。

假设两个并发的可序列化的作用域 S1 和 S2 访问一组公共的容器(在它们之外)以进行读写操作。可序列化的语义确保它们的执行结果是相同的,条件是在概念上对任何共享容器的有冲突的所有操作(读/写和写/写操作)被重新排序,以使 S1 中的所有操作的执行先于 S2 中的操作的执行,或者相反。实际用来确保可序列化的机制依赖于具体的实现。

在可序列化的作用域中,错误处理功能的使用由以下规则来管理:

  • 可序列化的作用域的故障处理程序共享关联的作用域的可序列化域,也就是说,在可序列化的作用域中发生故障的情况下,故障处理程序的执行被认为是可序列化行为的一部分(常用的实现说法是:在向故障处理程序过渡时不释放锁)。这是因为故障的修复需要共享的隔离环境,以提供可预测的行为。

  • 可序列化的作用域的补偿处理程序并共享关联的作用域的可序列化域。

  • 对于有补偿处理程序的可序列化域,创建补偿的状态快照是可序列化行为的一部分。换句话说,总是可以对执行步骤重新排序,就好象作用域对共享的容器在完成前有足够的独有访问权(包括创建快照)。

可序列化的作用域的语义与数据库事务中所用的标准隔离级别“可序列化”很相似,注意到这一点是有用的。

12. 示例

12.1. 运输服务

这个示例在描述基本的运输服务的过程中演示了 BPEL4WS 抽象流程的使用。该服务处理定货的运输。从服务的角度看,定货由多件货物组成。该运输服务提供两种运输:一种是货物被保存然后一起被运输,另一种是货物被分成多次运输直到所有的定货被报帐。

12.1.1. 服务描述

运输服务的上下文是客户与服务间的双方交互。这被模拟成下面的 serviceLinkType 定义:

<slnk:serviceLinkType name="shippingLT"
   xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/">
   <slnk:role name="shippingService">
      <slnk:portType name="shippingServicePT"/>
   </slnk:role>
   <slnk:role name="shippingServiceCustomer">
      <slnk:portType name="shippingServiceCustomerPT"/>
   </slnk:role>
</slnk:serviceLinkType>

相应的消息和 portType 定义如下:

<wsdl:definitions  
              targetNameSpace="http://ship.org/wsdl/shipping"
              xmlns:ship= ...>

<message name="shippingRequestMsg">
   <part name="shipOrder" type="ship:shipOrder"/>
</message>

<message name="shippingNoticeMsg">
   <part name="shipNotice" type="ship:shipNotice"/>
</message>

<portType name="shippingServicePT">
   <operation name="shippingRequest">
      <input message="shippingRequestMsg"/>
   </operation>
</portType>

<portType name="shippingServiceCustomerPT">
   <operation name="shippingNotice">
      <input message="shippingNoticeMsg"/>
   </operation>
</portType>

</wsdl:definitions>

12.1.2. 消息属性

与服务行为有关的属性如下:

  • 运输定单标识,用于使运输通知与运输定单相关(shipOrderID

  • 定单是否被运完(shipComplete

  • 定单中的货物总数(itemsTotal

  • 在运输通知中被引用的货物数量,这样,在分批运输是可接受的情况下,我们可以使用货物数量和 itemsTotal 来跟踪运输的整体完成情况(itemsCount

以下是这些属性及其别名的定义:

<wsdl:definitions 
       targetNamespace="http://example.com/shipProps/"
       xmlns:sns="http://ship.org/wsdl/shipping"
       xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/">
 
   <!-- types used in abstract processes are required to be finite domains. 
        The itemCountType is restricted by range -->

   <wsdl:types>
      <xsd:schema> 
      _<xsd:simpleType name="itemCountType"> 
      _      _<xsd:restriction base="xsd:int"> 
      _      _      _<xsd:minInclusive value="1"/> 
      _      _      _<xsd:maxInclusive value="50"/> 
      _      _</xsd:restriction> 
      _</xsd:simpleType> 
      </xsd:schema>    
   </wsdl:types>

   <!-- a message type is defined here for internal use of the 
        abstract process; it is not inlined because we need to
        define a property relative to it -->

   <wsdl:message name="itemsShipped">
      <part name="value" type="ship:itemCountType"/>
   </wsdl:message>
   <bpws:property name="shipOrderID" type="xsd:int"/>
   <bpws:property name="shipComplete" type="xsd:boolean"/>
   <bpws:property name="itemsTotal" type="ship:itemCountType"/>
   <bpws:property name="itemsCount" type="ship:itemCountType"/>
   <bpws:property name="numItemsShipped" type="ship:itemCountType"/>

   <bpws:propertyAlias propertyName="tns:shipOrderID" 
              messageType="sns:shippingRequestMsg"
              part="shipOrder"
              query="/ShipOrderRequestHeader/shipOrderID"/>

   <bpws:propertyAlias propertyName="tns:shipOrderID" 
              messageType="sns:shippingNoticeMsg"
              part="shipNotice"
              query="/ShipNoticeHeader/shipOrderID"/>

   <bpws:propertyAlias propertyName="tns:shipComplete" 
              messageType="sns:shippingRequestMsg"
              part="shipOrder"
              query="/ShipOrderRequestHeader/shipComplete"/>

   <bpws:propertyAlias propertyName="tns:itemsTotal" 
               messageType="sns:shippingRequestMsg"
               part="shipOrder"
               query="/ShipOrderRequestHeader/itemsTotal"/>

   <bpws:propertyAlias propertyName="tns:itemsCount" 
                messageType="sns:shippingNoticeMsg"
                part="shipNotice"
                query="/ShipNoticeHeader/itemsCount"/>

   <bpws:propertyAlias propertyName="tns:numItemsShipped" 
                 messageType="tns:itemsShipped"
                 part="value"
                 query="/"/>

</wsdl:definitions>

12.1.3. 流程

下面是流程的定义。为了简短起见,抽象流程定义省略了不少东西,例如完整的描述应说明(商业的或其它的)错误情况的处理。该流程的大致轮廓如下:

receive shipOrder
switch
   case shipComplete
      send shipNotice
   otherwise
      itemsShipped := 0
      while itemsShipped < itemsTotal
      itemsCount := opaque // non-deterministic assignment
                     // corresponding e.g. to 
                     // internal interaction with
                     // back-end system
      send shipNotice
      itemsShipped = itemsShipped + itemsCount

以下是更完整的版本:

<process name="shippingService"
      targetNameSpace="http://acme.com/shipping"
         xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
      xmlns:sns="http://ship.org/wsdl/shipping"
         xmlns:props="http://example.com/shipProps/"
         abstractProcess="yes">

<partners>
  <partner name="customer"
           serviceLinkType="sns:shippingLT"
        partnerRole="shippingServiceCustomer"
        myRole="shippingService"/>
</partners>

<containers>
  <container name="shipRequest"
             messageType="sns:shippingRequestMsg"/>
  <container name="shipNotice"
        messageType="sns:shippingNoticeMsg"/>
  <container name="itemsShipped"
             messageType="props:itemsShipped"/>
</containers>

<correlationSets>
  <correlationSet name="shipOrder"
         properties="props:shipOrderID"/>
</correlationSets>

<sequence>

  <receive partner="customer"
           portType="sns:shippingServicePT"
        operation="shippingRequest"
        container="shipRequest">
    <correlations>
      <correlation set="shipOrder" initiation="yes"/>
    </correlations>
  </receive>

  <switch>
    <case condition=
       "bpws:getContainerProperty('shipRequest','props:shipComplete')" >
      <sequence>
     <assign>
          <copy>
            <from container="shipRequest" property="props:itemsCount"/>
            <to container="shipNotice" property="props:itemsCount"/>
          </copy>
        </assign>
        <invoke partner="customer"
        portType="sns:shippingServiceCustomerPT"
              operation="shippingNotice"
        inputContainer="shipNotice">
       <correlations>
         <correlation set="shipOrder" pattern="out"/>
       </correlations>
     </invoke>
      </sequence>
    </case>
    <otherwise>
      <sequence>
        <assign>
          <copy>
         <from expression="0"/>
         <to container="itemsShipped" part="value"/>
          </copy>
     </assign>
     <while condition=
         "bpws:getContainerProperty('itemsShipped','props:numItemsShipped') <
          bpws:getContainerProperty('shipRequest','props:itemsTotal')">
           <sequence>
           <assign>
                <copy>
               <from opaque="yes"/>
               <to container="shipNotice" property="props:itemsCount"/>
                </copy>
          </assign>
             <invoke partner="customer"
               portType="sns:shippingServiceCustomerPT"
                     operation="shippingNotice"
               inputContainer="shipNotice">
             <correlations>
                <correlation set="shipOrder" pattern="out"/>
             </correlations>
          </invoke>
          <assign>
                <copy>
               <from expression=
                        "bpws:getContainerProperty('itemsShipped',
                         'props:numItemsShipped')+ 
                         bpws:getContainerProperty('shipNotice',
                         'props:itemsCount')"/>
               <to container="itemsShipped" part="value"/>
                </copy>  
          </assign>
           </sequence>
      </while>
       </sequence>
    </otherwise>
  </switch>
</sequence>

</process>

12.2. 贷款审批

这个示例考虑的是一个简单的贷款审批 Web 服务,客户可以通过该服务提供的端口发送他们的贷款申请。该服务的客户发送的贷款申请中包括个人信息和申请的数额。根据这些信息,贷款服务运行一个简单的流程,运行的结果是“贷款被批准”消息或“贷款被拒绝”消息。作出审批决定的方式有两种,这取决于申请的数额以及与申请人有关的风险。对于小额贷款(小于 $10,000)和低风险个人,审批是自动的。对于大额贷款或中额贷款和高风险个人,每个信用申请需被更仔细地研究。所以,为了处理每个申请,贷款服务使用两个其它服务提供的功能。在小额贷款可用的简化的处理中,“风险评估”服务被用来获取与提出申请的个人有关的风险的快速评估。当简化的审批流程不适用的时候,完整的“贷款审批”服务(可能需要贷款专家的直接参与)被用来获取申请的全面评估。

12.2.1. 服务描述

下面显示的是该服务支持的 WSDL portType(“loanServicePT”portType)。这里假定一个独立的“loan.org”联盟已提供了贷款服务 portType 的定义以及风险评估和全面贷款审批服务的定义,所以所有所需的 WSDL 定义出现在相同的 WSDL 文档中。具体地说,这里定义了提供风险评估和审批功能的 Web 服务的 portType,还定义了与使用这些 portType 有关的所有所需的服务链接类型。

<definitions 
      targetNamespace="http://loans.org/wsdl/loan-approval"
      xmlns="http://schemas.xmlsoap.org/wsdl/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"    
       xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/"      
      xmlns:lns="http://loans.org/wsdl/loan-approval">


<message name="creditInformationMessage">
   <part name="firstName" type="xsd:string"/>
   <part name="name" type="xsd:string"/>
   <part name="amount" type="xsd:integer"/>
</message>

<message name="approvalMessage">
   <part name="accept" type="xsd:string"/>
</message>

<message name="riskAssessmentMessage">
   <part name="level" type="xsd:string"/>
</message>   

<message name="errorMessage">
   <part name="errorCode" type="xsd:integer"/>
</message>

<portType name="loanServicePT">
   <operation name="request">
      <input message="lns:creditInformationMessage"/>
      <output message="lns:approvalMessage"/>
      <fault name="unableToHandleRequest" 
             message="lns:errorMessage"/>
   </operation>
</portType>

<portType name="riskAssessmentPT">
   <operation name="check">
      <input message="lns:creditInformationMessage"/>
      <output message="lns:riskAssessmentMessage"/>
      <fault name="loanProcessFault" 
             message="lns:errorMessage"/>
   </operation>
</portType>

<portType name="loanApprovalPT">
   <operation name="approve">
      <input message="lns:creditInformationMessage"/>
      <output message="lns:approvalMessage"/>
      <fault name="loanProcessFault" 
             message="lns:errorMessage"/>
   </operation>
</portType>      

<slnk:serviceLinkType name="loanServiceLinkType">
   <slnk:role name="customer">
       <slnk:portType name="lns:loanServicePT"/>
   </slnk:role>
</slnk:serviceLinkType>

<slnk:serviceLinkType name="loanApprovalLinkType">
   <slnk:role name="approver">
       <slnk:portType name="lns:loanApprovalPT"/>
   </slnk:role>   
</slnk:serviceLinkType>

<slnk:serviceLinkType name="riskAssessmentLinkType">
   <slnk:role name="assessor">
       <slnk:portType name="lns:riskAssessmentPT"/>
   </slnk:role>   
</slnk:serviceLinkType>

</definitions>

12.2.2. 流程

在下面定义的业务流程中,初始的 <receive> 活动和匹配的 <reply> 活动表示与客户的交互。<invoke> 元素表示风险评估和贷款审批服务的使用。所有这些活动被包括在 <flow> 中,根据相应的 <link> 元素所表示的依赖关系来(可能是并行地)执行这些活动。请注意,附加在链接的 <source> 元素上的过渡条件决定激活哪个链接。由于 <process> 元素的“suppressJoinFailure”属性值为 "yes",所以死路删除已被启用。这暗示,由于某些链接被设置为 false,该决定的后果可被传播而某些活动的执行可被跳过。

因为被调用的操作可能返回类型是“loanProcessFault”的故障,所以需要提供故障处理程序。当故障发生时,控制被转移给故障处理程序,故障处理程序中的 <reply> 元素被用来把类型是“unableToHandleRequest”的故障响应返回给贷款申请人。

<process name="loanApprovalProcess" 
         targetNamespace="http://acme.com/loanprocessing" 
         xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
         xmlns:lns="http://loans.org/wsdl/loan-approval"
         suppressJoinFailure="yes">

   <partners>
      <partner name="customer" 
               serviceLinkType="lns:loanServiceLinkType"
               partnerRole="customer"/>
      <partner name="approver" 
               serviceLinkType="lns:loanApprovalLinkType"
               partnerRole="approver"/>
      <partner name="assessor" 
               serviceLinkType="lns:riskAssessmentLinkType"
               partnerRole="assessor"/>
   </partners>

   <containers>
     <container name="request" 
                messageType="lns:creditInformationMessage"/>
     <container name="risk" 
                messageType="lns:riskAssessmentMessage"/>
     <container name="approval" 
                messageType="lns:approvalMessage"/>
     <container name="error" 
                messageType="lns:errorMessage"/>
   </containers>

   <faultHandlers>
      <catch faultName="lns:loanProcessFault" 
             faultContainer="error">
         <reply   partner="customer"
                  portType="lns:loanServicePT" 
                  operation="request"
                  container="error" 
                  faultName="unableToHandleRequest"/>
      </catch>
   </faultHandlers>
                                                                  

   <flow>

      <links>
         <link name="receive-to-assess"/>
         <link name="receive-to-approval"/>
         <link name="approval-to-reply"/>
         <link name="assess-to-setMessage"/>
         <link name="setMessage-to-reply"/>
         <link name="assess-to-approval"/>
      </links>

      <receive partner="customer" 
               portType="lns:loanServicePT" 
               operation="request" 
               container="request" createInstance="yes">
         <source linkName="receive-to-assess"
            transitionCondition=
              "bpws:getContainerData('request','amount')< 10000"/>          
         <source linkName="receive-to-approval"
            transitionCondition=
              "bpws:getContainerData('request','amount')>=10000"/>
      </receive>

      <invoke  partner="assessor" 
               portType="lns:riskAssessmentPT" 
               operation="check"
               inputContainer="request"  
               outputContainer="risk">
         <target linkName="receive-to-assess"/>
         <source linkName="assess-to-setMessage" 
            transitionCondition=
              "bpws:getContainerData('risk','level')='low'"/>
         <source linkName="assess-to-approval" 
            transitionCondition=
              "bpws:getContainerData('risk','level')!='low'"/>
      </invoke>

      <assign>
         <target linkName="assess-to-setMessage"/>
         <source linkName="setMessage-to-reply"/>
         <copy>
            <from expression="'yes'"/>
            <to container="approval" part="accept"/>
         </copy>
      </assign>

      <invoke  partner="approver" 
               portType="lns:loanApprovalPT" 
               operation="approve" 
               inputContainer="request" 
               outputContainer="approval">
         <target linkName="receive-to-approval"/>
         <target linkName="assess-to-approval"/>
         <source linkName="approval-to-reply" />
      </invoke>

      <reply   partner="customer" 
               portType="lns:loanServicePT" 
               operation="request" 
               container="approval">
         <target linkName="setMessage-to-reply"/>
         <target linkName="approval-to-reply"/>
      </reply>
   </flow>

</process>

12.3. 多个启动活动

一个流程可以有多个创建流程实例的活动。这种情况的示例是拍卖行运行的(简化的)业务流程。该业务流程的目的如下:收集来自某次拍卖的买卖双方的信息;向某个拍卖注册服务报告适当的拍卖结果;然后把注册结果返回给买卖双方。所以,该业务流程有两个启动活动,一个用于接收卖方信息,另一个用于接收买方信息。因为拍卖标识能唯一地识别某次拍卖,所以买卖双方需在发送他们的数据时提供拍卖标识。卖方请求和买方请求到达拍卖行的顺序是随机的。所以,当这样的请求到达时,需检查业务流程实例是否已经存在。如果还不存在,那么就创建业务流程实例。当接收到这两个请求后,拍卖注册服务被调用。因为调用是异步的,拍卖行把拍卖标识传递给拍卖注册服务。拍卖注册服务在它的回答中返回这个拍卖标识以使拍卖行找到正确的业务流程实例。因为有许多买方和卖方,买卖双方都需要提供他们的服务引用以使拍卖服务可以正确地响应。另外,拍卖行需向拍卖注册服务提供它自己的服务引用以使拍卖注册服务可以把响应返回给拍卖行。

12.3.1. 服务描述

拍卖服务提供两个名为 sellerPT 和 buyerPT 的端口类型,这两个端口类型有可用于接受由卖方和买方提供的数据的适当的操作。因为业务流程的处理时间是漫长的,所以拍卖服务通过合适的端口类型(sellerAnswerPT 和 buyerAnswerPT)来响应卖方和买方。这些 portType 被正确地组合成两个服务链接类型,一个是用于卖方的 sellerAuctionHouseLT,另一个是用于买方的 buyerAuctionHouseLT。

拍卖服务需要两个为调用拍卖注册服务提供的端口类型(auctionRegistrationPT 和 auctionRegistrationAnswerPT)。这些端口类型是合适的服务链接类型 auctionHouseAuctionRegistrationServiceLT 的一部分。

<definitions 
    targetNamespace="http://www.auction.com/wsdl/auctionService" 
    xmlns:tns="http://www.auction.com/wsdl/auctionService" 
    xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/"
    xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
    xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://schemas.xmlsoap.org/wsdl/">
 
<!-- Messages for communication with the seller -->
 
  <message name="sellerData">
     <part name="creditCardNumber" type="xsd:string"/>
     <part name="shippingCosts" type="xsd:integer"/>
     <part name="auctionId" type="xsd:integer"/>
     <part name="serviceReference" type="sref:serviceReferenceType"/>
  </message>
  <message name="sellerAnswerData">
     <part name="thankYouText" type="xsd:string"/>
  </message>
 
<!-- Messages for communication with the buyer -->
 
  <message name="buyerData">
     <part name="creditCardNumber" type="xsd:string"/>
     <part name="phoneNumber" type="xsd:string"/>
     <part name="ID" type="xsd:integer"/>
     <part name="serviceReference" type="sref:serviceReferenceType"/>
  </message>
  <message name="buyerAnswerData">
     <part name="thankYouText" type="xsd:string"/>
  </message>
 
<!-- Messages for communication with the auction registration service -->
 
  <message name="auctionDetails">
     <part name="auctionId" type="xsd:integer"/>
     <part name="amount" type="xsd:integer"/>
  </message>
  <message name="auctionDetailsAnswer">
     <part name="registrationId" type="xsd:integer"/>
     <part name="auctionId" type="xsd:integer"/>
     <part name="auctionHouseServiceReference" 
                 type="sref:serviceReferenceType"/>
  </message>
  
<!-- Port types for interacting with the seller -->
 
  <portType name="sellerPT">
     <operation name="submit">
        <input message="tns:sellerData"/>
     </operation>
  </portType>
  <portType name="sellerAnswerPT">
     <operation name="answer">
        <input message="tns:sellerAnswerData"/>
     </operation>
  </portType>
 
<!-- Port types for interacting with the buyer -->
 
  <portType name="buyerPT">
     <operation name="submit">
        <input message="tns:buyerData"/>
     </operation>
  </portType>
  <portType name="buyerAnswerPT">
     <operation name="answer">
        <input message="tns:buyerAnswerData"/>
     </operation>
  </portType>
 
<!-- Port types for interacting with the auction registration service -->
 
  <portType name="auctionRegistrationPT">
     <operation name="process">
        <input message="tns:auctionDetails"/>
     </operation>
  </portType>
  <portType name="auctionRegistrationAnswerPT">
     <operation name="answer">
        <input message="tns:auctionRegAnswer"/>
     </operation>
  </portType>

<!-- Context type used for locating business process via auction Id -->
 
  <bpws:property name="auctionId"
                 type="xsd:string"/>

  <bpws:propertyAlias propertyName="tns:auctionId"
                     messageType="tns:sellerData" 
                     part="auctionId"/>

  <bpws:propertyAlias propertyName="tns:auctionId"
                           messageType="tns:buyerData"
                           part="ID"/>
  <bpws:propertyAlias propertyName="tns:auctionId"
                          messageType="tns:auctionDetailsAnswer"
                          part="auctionId"/>
   <bpws:propertyAlias propertyName="tns:auctionId"
                          messageType="tns:auctionDetails"
                          part="auctionId"/>
 
 
<!-- Service link type for seller/auctionHouse -->
 
  <slnk:serviceLinkType name="tns:sellerAuctionHouseLT">     
     <slnk:role name="auctionHouse">
        <slnk:portType name="tns:sellerPT"/>
     </slnk:role>
     <slnk:role name="seller">
        <slnk:portType name="tns:sellerAnswerPT"/>
     </slnk:role>
  </slnk:serviceLinkType>
 
<!-- Service link type for buyer/auctionHouse -->
 
  <slnk:serviceLinkType name="buyerAuctionHouseLT">      
     <slnk:role name="auctionHouse">
        <slnk:portType name="tns:buyerPT"/>
     </slnk:role>
     <slnk:role name="buyer">
        <slnk:portType name="tns:buyerAnswerPT"/>
     </slnk:role>
  </slnk:serviceLinkType>
 
<!-- Service link type for auction house/auction 
     registration service -->
 
  <slnk:serviceLinkType name="auctionHouseAuctionRegistrationServiceLT">
     <slnk:role name="auctionRegistrationService">
       <slnk:portType name="tns:auctionRegistrationPT"/>
     </slnk:role>
     <slnk:role name="auctionHouse">
       <slnk:portType name="tns:auctionRegistrationAnswerPT"/>
     </slnk:role>
  </slnk:serviceLinkType>
</definitions>

12.3.2. 流程

由拍卖行提供的业务流程的 BPEL4WS 定义如下:

<process name="auctionService"
       targetNamespace="http://www.auction.com"
       containerAccessSerializable="no"
       xmlns:as="http://www.auction.com/wsdl/auctionService" 
       xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
       xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/">
         
 
<!-- Partners -->
 
  <partners>
     <partner name="seller" 
              serviceLinkType="as:sellerAuctionHouseLT"
              myRole="auctionHouse" partnerRole="seller"/>
     <partner name="buyer" 
              serviceLinkType="as:buyerAuctionHouseLT"
              myRole="auctionHouse" partnerRole="buyer"/>
     <partner name="auctionRegistrationService"
              serviceLinkType=
              "as:auctionHouseAuctionRegistrationServiceLT"
              myRole="auctionHouse" 
              partnerRole="auctionRegistrationService"/>
  </partners>
 
<!-- Containers -->
 
  <containers>
     <container name="sellerData" messageType="as:sellerData"/>
     <container name="sellerAnswerData" messageType="as:sellerAnswerData"/>
     <container name="buyerData" messageType="as:buyerData"/>
     <container name="buyerAnswerData" messageType="as:buyerAnswerData"/>
     <container name="auctionRegistrationData"
                messageType="as:auctionDetails"/>
     <container name="auctionRegistrationResponse"
                messageType="as:auctionRegAnswer"/>
  </containers>
 
<!-- Correlation set for correlating buyer and seller request 
     as well as auction house and auction registration service
     exchange -->
 
  <correlationSets>
     <correlationSet name="auctionIdentification"
        properties="as:auctionId"/>
        
  </correlationSets>
 
<!-- Structure of the business process -->
 
  <sequence>
 
<!-- Process buyer and seller request in parallel
     Either one can create a process instance -->
 
     <flow>
 
<!-- Process seller request --> 
    
        <receive name="acceptSellerInformation"
                 partner="seller"
                 portType="as:sellerPT"
                 operation="provide"
                 container="sellerData"
                 createInstance="yes">
            <correlations>
                 <correlation set="auctionIdentification"
                              initiation="yes"/>
                 </correlations>              
                 
         </receive>
 
<!-- Process buyer request --> 
 
        <receive name="acceptBuyerInformation"
                 partner="buyer"
                 portType="as:buyerPT"
                 operation="provide"
                 container="buyerData"
                 createInstance="yes">
            <correlations>
                 <correlation set="auctionIdentification"
                              initiation="yes"/>
            </correlations>
        </receive> 
                 
 
      </flow>
 
<!-- Invoke auction registration service 
     by setting the target service reference
     and setting my own service reference for call back
     and receiving the answer 
     Correlation of request and answer is via auction Id -->
 
      <assign>
         <copy>
            <from>
               <sref:serviceReference>
                  <sref:service name="ars:RegistrationService"
                     xmlns:ars="http://auctionRegistration.com"/>
               </sref:serviceReference>
            </from>
            <to partner="auctionRegistrationService"/>
         </copy>
      </assign>
 
      <assign>
         <copy>
           
            <from partner="auctionRegistrationService"
                  serviceReference="myRole"/>
            <to container="auctionRegistrationData"
                part="auctionHouseServiceRef"/>
         </copy>
      </assign>
 
      <invoke name="registerAuctionResults"
              partner="auctionRegistrationService"
              portType="as:auctionRegistrationPT"
              operation="process"
              inputContainer="auctionRegistrationData">
         <correlations>
              <correlation set="auctionIdentification"/>                                
         </correlations>  
      </invoke> 
              
      <receive name="receiveAuctionRegistrationInformation"
               partner="auctionRegistrationService"
               portType="as:auctionRegistrationAnswerPT"
               operation="answer"
               container="auctionRegistrationAnswerData">
               
         <correlations>
              <correlation set="auctionIdentification"/>
         </correlations>
    </receive>

<!-- Send responses back to seller and buyer -->
 
      <flow>
 
<!-- Process seller response by
     setting the seller to the service reference provided by the seller
     and invoking the response -->
 
         <sequence>
 
             <assign>
                <copy>
                   <from container="sellerData"
                         part="serviceReference"/>
                   <to partner="seller"/>
                </copy>
             </assign>
  
             <invoke name="respondToSeller"
                     partner="seller"
                     portType="as:sellerAnswerPT"
                     operation="answer"
                     inputContainer="sellerAnswerData"/>
 
         </sequence>
 
<!-- Process buyer response by
     setting the buyer to the service reference provided by the buyer
     and invoking the response -->
 
         <sequence>
 
             <assign>
                <copy>
                   <from container="buyerData" 
                         part="serviceReference"/>
                   <to partner="buyer"/>
                </copy>
             </assign>
  
             <invoke name="respondToBuyer"
                     partner="buyer"
                     portType="as:buyerAnswerPT"
                     operation="answer"
                     inputContainer="buyerAnswerData"/>
 
         </sequence>
 
      </flow>
 
   </sequence>
 
</process>

13. 未来的发展方向

BPEL4WS 是正在被改进的语言。本规范中缺少许多必需的功能。未来的版本中将包括这些功能。以下几节将讨论一些显而易见的扩展,这些扩展将被添加到 BPEL4WS 中。

13.1. 作用域

作用域所提供的功能是把多个活动分为一组并把属性赋给这个作用域,例如故障处理程序和补偿处理程序。以下特征需被添加到作用域。

13.1.1. 容器

目前,只能在流程级别声明容器,使整个流程成为可以访问容器的作用域。换句话说,所有的容器都是全局的。未来的版本将允许在所有的作用域中声明容器,从而允许定义仅在本地作用域中可见的且可访问的容器。这可以简化不少东西,比如说补偿处理程序的编写。

除了用于同步信号外,本地容器还使通过链接来发送数据变得有吸引力。本地容器与基于链接的数据传输的组合将大大减少对使用可序列化的作用域的显式并发控制的需求。

对并发访问共享的容器所产生的问题进一步的分析将有可能简化功能并更好地避免死锁和干扰。在这些方面,BPEL4WS 将在未来的版本中得到改进。

13.1.2. 事件处理程序

业务流程常常需要准备应对在流程执行的指定部分中的任何时候可能出现的未作安排的业务事件。作用域被用来描述流程执行部分,事件处理程序概念将被添加到作用域中,以模拟异步事件的处理。整个流程可以有一组在流程执行中总是可用的事件处理程序。有两种事件将被支持。第一种是对应于 WSDL 中进站的请求/响应或单向操作的事件。例如,状态查询很可能是请求/响应操作而取消可能是单向操作。第二种事件可以是在用户设定的时刻发出的定时器警报。与故障处理程序和补偿处理程序不同,事件处理程序将被考虑为该作用域的正常行为的一部分。

13.1.3. 重叠作用域

目前,作用域是互相嵌套的。因为作用域与许多不同的属性关联,所以有不同属性的作用域很可能有一些共同的活动。这可能要求作用域可以重叠。这是需要进一步研究的领域。

13.1.4. 原子作用域

业务流程常常是“事务的”,其含义是它们所执行的活动要求可预测的一致的结果。虽然在某种意义上持续性属于私有的实现范围,但是经常要求以这样的方式执行一组活动:要么全部成功完成,要么不执行这些活动(或消除它们的影响)。例如,接收请求、执行某些数据处理和回答请求这三个活动常常可以这种方式被耦合在一起。这常常被称为原子性,在未来的版本中将添加定义有原子性属性的作用域的功能。

13.1.5. 补偿

补偿是撤销整个业务流程的影响的方法,也是撤销部分业务流程的影响的方法。目前的规范提供了基本的支持。还需进一步改进补偿行为。例如,目前的补偿是完全独立的。它并不受它运行所在的流程实例的当前活动状态的影响,它也不能影响该状态。一般来说,这显然不符合实际。输入和输出参数将被添加到补偿处理程序,以支持在受控的方式中活动状态与补偿的相互影响。其它可能的改进包括在失败的情况下再次补偿和新的缺省补偿模式(目前支持的是“相反的顺序”模式)。

13.2. 生命周期和查询

13.2.1. 暂挂/恢复

有时候,有必要使业务流程的执行暂挂(停机)一段时间或显式地被合适的操作恢复。这类暂挂/恢复活动主要被用于事件处理程序,以暂挂和恢复整个业务流程或仅仅是某个作用域的处理。另外,目前的 terminate 活动终止整个流程。在有些情况下,仅仅终止某个作用域中的处理就足够了。

13.2.2. 查询

目前的草案不支持查询业务流程的状态的功能,这在执行业务流程时是需要的。选项的指定(例如所提供的信息的详细信息)必须被支持。

13.3. 服务组成

因为一般来说,BPEL4WS 流程提供和消费多个 Web 服务接口,所以这样的流程可被看作由其它 Web 服务组成的一组 Web 服务。服务链接所提供的关系概念在建立组成形式方面也很重要。这显然是需要进一步开发的领域,未来版本的 BPEL4WS 将参与这方面的开发。

13.4. 与 WS-Transaction 规范的关系

BPEL4WS 在作用域的语法构造的基础上定义了长期运行的业务事务(long-running business transactions,LRT)概念。对于存在分布式的业务流程或跨越多个供应商的实现的环境,WS-Transaction 规范 [12] 将提供机制来:

  • 为分布式作用域定义在业务事务中可被理解的全局协定上下文。

  • 允许分布式流程中的参与者注册故障处理和补偿通知(根据列出的 LRT 行为)。

请参阅附录 C,了解基于 WS-Transaction 概念的 BPEL4WS LRT 的详细模型。

另外,多个业务流程实例常常需要一起运行,以使共享的工作单元被一致地完成。这些流程中个别 LRT 的协调也需要使用 WS-Transaction 规范中列出的协调协议(根据在 WS-Transaction 中列出的协定行为)。

14. 安全性注意事项

因为消息可以被篡改或伪造,所以强烈推荐业务流程实现使用 WS-Security 来确保消息在传输过程中或驻留在目的地时未被篡改或伪造。类似地,无效或过期的消息可被再次使用,或者未专门与特定的消息相关联的消息头可被引用。因此,在使用 WS-Security 时,签名必须包含语义重要的消息头和消息体(以及任何其它有关数据),这样它们就不会被独立地分开并再次被使用了。

在业务流程间用于通信的消息传递协议易遭受各种形式的重播攻击。除上面列出的机制外,消息还应该在签名中包含一个消息时间戳(如 WS-Security 中所述)。接收者可以使用时间戳信息为业务流程高速缓存最近的消息、检测重复的传输并阻止潜在的重播攻击。

还应该注意,业务流程的实现易遭受各种形式的拒绝服务攻击。遵守本规范的业务流程执行系统的实现者应该把这一点考虑进去。

15. 致谢

Achille Fokoue、Ashok Malhotra 和 Bob Schloss 为开发和验证 XML Schema 提供了帮助。

Tony Andrews 和 Marc Levy 为定义抽象流程提供了帮助。

Tony Hoare 和 Marc Shapiro 经深思熟虑后提出了他们对 BPEL4WS 语言的概念的看法。

Jonathan Marsh 建议推广对外部(查询和表达式)语言的依赖关系。

Tom Freund 和 Tony Storey 引导我们准确地定义与 WS-Transaction 中的协调框架的关系。

Martin Nally 为改善 BPEL4WS 语言的可用性提供了帮助。

16. 参考资料

[1] W3C 推荐“XML 规范
[2] W3C 纪要“Simple Object Access Protocol (SOAP) 1.1
[3] W3C 纪要“Web Services Description Language (WSDL) 1.1
[4] 业界倡议“Universal Description, Discovery and Integration
[5] XLANG:Web Services for Business Process Design
[6] WSFL:Web Services Flow Language 1.0
[7] W3C 提议的推荐“XML Schema Part 1: Structures
[8] W3C 提议的推荐“XML Schema Part 2: Datatypes
[9] W3C 推荐“XML Path Language (XPath) Version 1.0
[10]“Sagas”,H. Garcia-Molina 和 K. Salem,Proc. ACM SIGMOD(1987 年)。
[11]“Trends in systems aspects of database management”,I.L. Traiger,Proc. 2nd Intl. Conf. on Databases (ICOD-2),Wiley & Sons 1983 年。
[12]“Web Services Transaction”,BEA、IBM 和 Microsoft,2002 年。
[13]“Key words for use in RFCs to Indicate Requirement Levels”,RFC 2119,S. Bradner,Harvard University,1997 年 3 月。
[14]“Uniform Resource Identifiers (URI): Generic Syntax”,RFC 2396,T. Berners-Lee,R. Fielding,L. Masinter,MIT/LCS,U.C. Irvine,Xerox Corporation,1998 年 8 月。

附录 A 标准故障

下面的列表指定了 BPEL4WS 规范中定义的标准故障。所有这些故障被命名在对应于 URI“http://schemas.xmlsoap.org/ws/2002/07/business-process/”的 BPEL4WS 名称空间的标准前缀 bpws: 中。

故障名原因
selectionFailure抛出的条件是在函数(例如 bpws:getContainerData)中或在赋值中执行的选择操作遇到错误。
conflictingReceive抛出的条件是具有相同的 partner、portType 和 operation 的多个 receive 活动或等同的代码(目前是 pick 活动中的 onMessage 分支)被同时启用。
conflictingRequest抛出的条件是多个来自某个端口类型和操作的相同的伙伴的同步进站请求是活动的。
mismatchedAssignmentFailure抛出的条件是在 assign 活动中遇到不兼容的类型。
joinFailure抛出的条件是活动的连结条件的值是 false。
forcedTermination 抛出的条件是外层作用域中发生故障。
correlationViolation抛出的条件是 invoke、receive 或 reply 等活动中处理的消息内容与指定的相关性信息不匹配。
uninitializedContainer抛出的条件是企图访问消息容器中未初始化部分的值。
repeatedCompensation抛出的条件是安装的补偿处理程序被调用多次。

附录 B 属性和缺省值

下面的列表指定了流程级别和活动级别的所有标准属性的缺省值。这张列表并不包括特定于活动的属性(例如 invoke 活动中的 partner)。

参数缺省值
queryLanguagehttp://www.w3.org/TR/1999/REC-xpath-19991116
expressionLanguagehttp://www.w3.org/TR/1999/REC-xpath-19991116
suppressJoinFailureno
containerAccessSerializableno
abstractProcessno
initiationno
pattern没有缺省值
createInstanceno
enableInstanceCompensationno
joinCondition进站链接状态的析取

附录 C 协调协议

使用 WS-Transaction [12] 的协议框架来表达作用域间的故障和补偿处理关系是重要的。具体地说,这一节讲述怎样使用 WS-Transaction 规范中定义的 BusinessAgreement 协议来模拟外层作用域与它的每个嵌套的作用域间的关系。BusinessAgreement 协议被用来启用业务活动的分布式的协调。BPEL4WS 在使用该协议时假定在 BPEL4WS 中实际并不需要单个服务中的本地化行为,因此也不需要该协议的几个功能(包括确认信号 ForgetError 消息和 Replay 消息)。

BPEL4WS 作用域的协调协议

  1. 嵌套的作用域的执行可能成功地完成。在这种情况下,嵌套的作用域被安装了补偿处理程序。可用从嵌套的作用域到它的父作用域的 Completed 信号来模拟这种情况。

  2. 嵌套的作用域的执行可能遇到内部故障。在这种情况下,该作用域总是不成功地终止。
    1. 如果故障处理程序向它的外层作用域再次抛出故障,那么可用从嵌套的作用域到它的父作用域的 Faulted 信号来模拟这种情况。

    2. 如果故障被处理且未被再次抛出,那么该作用域从容地从它的父作用域的工作中退出。可用从嵌套的作用域到它的父作用域的 Exited 信号来模拟这种情况。
  3. 当嵌套的作用域完成后,父作用域(的故障处理程序或补偿处理程序)可以要求它通过调用它的补偿处理程序来补偿它自己。可用从父作用域到嵌套的作用域的 Compensate 信号来模拟补偿操作。

  4. 在成功完成补偿后,嵌套的作用域向它的父作用域发送 Compensated 信号。

  5. 补偿处理程序自身可能产生内部故障。在这种情况下
    1. 如果该故障未被补偿处理程序的作用域处理,那么该故障被再次抛到父作用域。可用从嵌套的作用域到它的父作用域的 Faulted 信号来模拟这种情况。

    2. 如果该故障被处理且未被再次抛出,那么我们假定该补偿能够成功完成。在这种情况下,嵌套的作用域向它的父作用域发送 Compensated 信号。
  6. 如果在父作用域中有一个与嵌套的作用域的工作无关的故障,那么父作用域将要求嵌套的作用域通过发送 Cancel 信号来提前放弃它的工作。

  7. 在接收到 cancel 信号后,嵌套的作用域将中断并终止它的执行(就好象出现了内部故障),然后把 Canceled 信号返回给父作用域。

  8. 最后,当父作用域决定不再需要已完成的嵌套的作用域的补偿时,该父作用域向嵌套的作用域发送 Close 信号。在丢弃补偿处理程序后,嵌套的作用域发出 Closed 信号作为响应。

  9. 当来自嵌套的作用域的 Completed 信号与来自父作用域的 Cancel 信号出现竞争时,Completed 信号胜出,也就是说,可以认为嵌套的作用域已完成而 Cancel 信号被忽略。

  10. 如果 Cancel 信号被发送到已产生内部故障的嵌套的作用域,那么 Cancel 信号被忽略,该作用域将最终向父作用域发送 Faulted 信号或 Exited 信号。

以上的 BusinessAgreement 协议状态图总结了前面的讨论。在这张图中,父(外层)作用域产生 CancelCompensateForgetClose 等信号,嵌套的作用域产生 CompletedFaultedExited、Compensated、CanceledClosed 等信号。需要强调的是,这些状态表示父作用域与某一个嵌套的作用域间的关系的状态。但是,除了发生信号竞争的情况,可以说这些状态几乎能表示嵌套的作用域自身的状态。请注意,这张图并没有反映在上面的 I 点和 J 点讨论的信号竞争,因为这张图仅反映了真实的协议状态。

附录 D - XSD 模式

BPEL4WS 模式

<?xml version='1.0' encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"    
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
        xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/" 
    targetNamespace="http://schemas.xmlsoap.org/ws/2002/07/business-process/" 
        elementFormDefault="qualified">

    <import namespace="http://schemas.xmlsoap.org/wsdl/"/>

    <complexType name="tExtensibleElements">
        <annotation>
            <documentation>This type is extended by other component types to allow elements and attributres from other namespaces to be added. .</documentation>
        </annotation>
        <sequence>
            <any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
        </sequence>
        <anyAttribute namespace="##other" processContents="lax"/>

    </complexType>

    <element name="process" type="bpws:tProcess"/>
    <complexType name="tProcess">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <sequence>
                    <element name="partners" type="bpws:tPartners" minOccurs="0"/>
                    <element name="containers" type="bpws:tContainers" minOccurs="0"/>
                    <element name="correlationSets" type="bpws:tCorrelationSets" minOccurs="0"/>
                    <element name="faultHandlers" type="bpws:tFaultHandlers" minOccurs="0"/>
                    <element name="compensationHandler" type="bpws:tCompensationHandler" minOccurs="0"/>
                    <group ref="bpws:activity"/>
                </sequence>
                <attribute name="name" type="NCName"/>
                <attribute name="targetNamespace" type="anyURI"/>
                <attribute name="suppressJoinFailure" type="bpws:tBoolean" default="no"/>
                <attribute name="containerAccessSerializable" type="bpws:tBoolean" default="no"/>
                <attribute name="enableInstanceCompensation" type="bpws:tBoolean" default="no"/>
                <attribute name="abstractProcess" type="bpws:tBoolean" default="no"/>
            </extension>
        </complexContent>
    </complexType>

    <group name="activity">
        <choice>
            <element name="empty" type="bpws:tEmpty"/>
            <element name="invoke" type="bpws:tInvoke"/>
            <element name="receive" type="bpws:tReceive"/>
            <element name="reply" type="bpws:tReply"/>
            <element name="assign" type="bpws:tAssign"/>
            <element name="wait" type="bpws:tWait"/>
            <element name="throw" type="bpws:tThrow"/>
            <element name="terminate" type="bpws:tTerminate"/>
            <element name="flow" type="bpws:tFlow"/>
            <element name="switch" type="bpws:tSwitch"/>
            <element name="while" type="bpws:tWhile"/>
            <element name="sequence" type="bpws:tSequence"/>
            <element name="pick" type="bpws:tPick"/>
            <element name="scope" type="bpws:tScope"/>
        </choice>
    </group>


    <complexType name="tPartners">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <sequence>
                    <element name="partner" type="bpws:tPartner" minOccurs="1" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tPartner">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <attribute name="name" type="NCName" use="required"/>
                <attribute name="serviceLinkType" type="QName" use="required"/>
                <attribute name="myRole" type="NCName"/>
                <attribute name="partnerRole" type="NCName"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tFaultHandlers">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <sequence>
                    <element name="catch" type="bpws:tCatch" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="catchAll" type="bpws:tActivityOrCompensateContainer" minOccurs="0"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tCatch">
        <complexContent>
            <extension base="bpws:tActivityOrCompensateContainer">
                <attribute name="faultName" type="QName" use="optional"/>
                <attribute name="faultContainer" type="NCName" use="optional"/>
            </extension>
        </complexContent>
    </complexType>


    <complexType name="tActivityContainer">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <sequence>
                    <group ref="bpws:activity"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>
    <complexType name="tActivityOrCompensateContainer">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <choice>
                    <group ref="bpws:activity"/>
                    <element name="compensate" type="bpws:tCompensate"/>
                </choice>
            </extension>
        </complexContent>
    </complexType>


    <complexType name="tOnMessage">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <sequence>
                    <element name="correlations" type="bpws:tCorrelations" minOccurs="0"/>
                    <group ref="bpws:activity"/>
                </sequence>
                <attribute name="partner" type="NCName" use="required"/>
                <attribute name="portType" type="QName" use="required"/>
                <attribute name="operation" type="NCName" use="required"/>
                <attribute name="container" type="NCName" use="required"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tOnAlarm">
        <complexContent>
            <extension base="bpws:tActivityContainer">
                <attribute name="for" type="bpws:tDuration-expr" use="optional"/>
                <attribute name="until" type="bpws:tDeadline-expr" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tCompensationHandler">
        <complexContent>
            <extension base="bpws:tActivityOrCompensateContainer"/>
        </complexContent>
    </complexType>

    <complexType name="tContainers">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <sequence>
                    <element name="container" type="bpws:tContainer" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tContainer">
             
                <!-- container does not allow extensibility elements           because otherwise its content model would be non-deterministic -->
                <sequence>
                    <element name="message" type="wsdl:tMessage"
                             minOccurs="0">
                        <unique name="part">
                             <selector xpath="wsdl:part"/>
                             <field xpath="@name"/>
                        </unique>
                    </element>
                </sequence>
                <attribute name="name" type="NCName" use="required"/>
                <attribute name="messageType" type="QName" use="optional"/>
                <anyAttribute namespace="##other" processContents="lax"/>
        

    </complexType>

    <complexType name="tCorrelationSets">
        <complexContent>
            <extension base="bpws:tExtensibleElements">


                <sequence>
                    <element name="correlationSet" type="bpws:tCorrelationSet" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tCorrelationSet">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <attribute name="properties" use="required">
                    <simpleType>
                        <list itemType="QName"/>
                    </simpleType>
                </attribute>
                <attribute name="name" type="NCName" use="required"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tActivity">
        <complexContent>
            <extension base="bpws:tExtensibleElements">


                <sequence>
                    <element name="target" type="bpws:tTarget" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="source" type="bpws:tSource" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
                <attribute name="name" type="NCName" use="optional"/>
                <attribute name="joinCondition" type="bpws:tBoolean-expr" use="optional"/>
                <attribute name="suppressJoinFailure" type="bpws:tBoolean" use="optional"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tSource">
        <complexContent>
            <extension base="bpws:tExtensibleElements">


                <attribute name="linkName" type="NCName" use="required"/>
                <attribute name="transitionCondition" type="bpws:tBoolean-expr" use="optional"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tTarget">
        <complexContent>
            <extension base="bpws:tExtensibleElements">


                <attribute name="linkName" type="NCName" use="required"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tEmpty">
        <complexContent>
            <extension base="bpws:tActivity"/>
        </complexContent>
    </complexType>

    <complexType name="tCorrelations">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
               <sequence>
            <element name="correlation" type="bpws:tCorrelation" minOccurs="1" maxOccurs="unbounded" />
         </sequence>
      </extension>
   </complexContent>
   </complexType>
   <complexType name="tCorrelation">
      <complexContent>
         <extension base="bpws:tExtensibleElements">
                <attribute name="set" type="NCName" use="required"/>
                <attribute name="initiation" type="bpws:tBoolean" use="optional" default="no"/>
          <attribute name="pattern" use="optional">
          <simpleType>
            <restriction base="string">
               <enumeration value="in" />
               <enumeration value="out" />
               <enumeration value="out-in" />
            </restriction>
          </simpleType>
           </attribute>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tInvoke">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="correlations" type="bpws:tCorrelations"  minOccurs="0" maxOccurs="1"/>
                    <element name="catch" type="bpws:tCatch" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="catchAll" type="bpws:tActivityOrCompensateContainer" minOccurs="0"/>
                    <element name="compensationHandler" type="bpws:tCompensationHandler" minOccurs="0"/>
                </sequence>
                <attribute name="partner" type="NCName" use="required"/>
                <attribute name="portType" type="QName" use="required"/>
                <attribute name="operation" type="NCName" use="required"/>
                <attribute name="inputContainer" type="NCName" use="required"/>
                <attribute name="outputContainer" type="NCName" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tReceive">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="correlations" type="bpws:tCorrelations" minOccurs="0"/>
                </sequence>
                <attribute name="partner" type="NCName" use="required"/>
                <attribute name="portType" type="QName" use="required"/>
                <attribute name="operation" type="NCName" use="required"/>
                <attribute name="container" type="NCName" use="required"/>
                <attribute name="createInstance" type="bpws:tBoolean" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tReply">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="correlations" type="bpws:tCorrelations" minOccurs="0"/>
                </sequence>
                <attribute name="partner" type="NCName" use="required"/>
                <attribute name="portType" type="QName" use="required"/>
                <attribute name="operation" type="NCName" use="required"/>
                <attribute name="container" type="NCName" use="required"/>
                <attribute name="faultName" type="QName" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tAssign">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="copy" type="bpws:tCopy" minOccurs="1" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tCopy">
        <complexContent>
            <extension base="bpws:tExtensibleElements">

                <sequence>
                    <element ref="bpws:from"/>
                    <element ref="bpws:to"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <element name="from" type="bpws:tFrom"/>
    <complexType name="tFrom">
        <complexContent>
            <extension base="bpws:tExtensibleElements">


                <attribute name="container" type="NCName"/>
                <attribute name="part" type="NCName"/>
                <attribute name="select" type="string"/>
                <attribute name="property" type="QName"/>
                <attribute name="partner" type="NCName"/>
                <attribute name="expression" type="string"/>
                <attribute name="opaque" type="bpws:tBoolean"/>
            </extension>
        </complexContent>

    </complexType>
    <element name="to">
        <complexType>
            <complexContent>
                <restriction base="bpws:tFrom">
                    <attribute name="expression" type="string" use="prohibited"/>
                    <attribute name="opaque" type="bpws:tBoolean" use="prohibited"/>
                </restriction>
            </complexContent>
        </complexType>
    </element>
    
    
    <complexType name="tWait">
        <complexContent>
            <extension base="bpws:tActivity">
                <attribute name="for" type="bpws:tDuration-expr" use="optional"/>
                <attribute name="until" type="bpws:tDeadline-expr" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tThrow">
        <complexContent>
            <extension base="bpws:tActivity">
                <attribute name="faultName" type="QName" use="required"/>
                <attribute name="faultContainer" type="NCName"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tCompensate">
        <complexContent>
            <extension base="bpws:tActivity">
                <attribute name="scope" type="NCName" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tTerminate">
        <complexContent>
            <extension base="bpws:tActivity"/>
        </complexContent>
    </complexType>

    <complexType name="tFlow">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="links" type="bpws:tLinks" minOccurs="0"/>
                    <group ref="bpws:activity" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tLinks">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <sequence>
                    <element name="link" type="bpws:tLink" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tLink">
        <complexContent>
            <extension base="bpws:tExtensibleElements">
                <attribute name="name" type="NCName" use="required"/>
            </extension>
        </complexContent>

    </complexType>

    <complexType name="tSwitch">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="case" maxOccurs="unbounded">
                        <complexType>
                            <complexContent>
                                <extension base="bpws:tActivityContainer">
                                    <attribute name="condition" type="bpws:tBoolean-expr" use="required"/>
                                </extension>
                            </complexContent>
                        </complexType>
                    </element>
                    <element name="otherwise" type="bpws:tActivityContainer" minOccurs="0"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tWhile">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <group ref="bpws:activity"/>
                </sequence>
                <attribute name="condition" type="bpws:tBoolean-expr" use="required"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tSequence">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <group ref="bpws:activity" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tPick">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="onMessage" type="bpws:tOnMessage" maxOccurs="unbounded"></element>
                    <element name="onAlarm" type="bpws:tOnAlarm" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
                <attribute name="createInstance" type="bpws:tBoolean" use="optional"/>
            </extension>
        </complexContent>
    </complexType>

    <complexType name="tScope">
        <complexContent>
            <extension base="bpws:tActivity">
                <sequence>
                    <element name="faultHandlers" type="bpws:tFaultHandlers" minOccurs="0"/>
                    <element name="compensationHandler" type="bpws:tCompensationHandler" minOccurs="0"/>
                    <group ref="bpws:activity"/>
                </sequence>
                <attribute name="containerAccessSerializable" type="bpws:tBoolean" use="required"/>
            </extension>
        </complexContent>
    </complexType>

    <simpleType name="tListOfNCNames">
        <list itemType="NCName"/>
    </simpleType>

    <simpleType name="tBoolean-expr">
        <restriction base="string"/>
    </simpleType>

    <simpleType name="tDuration-expr">
        <restriction base="string"/>
    </simpleType>

    <simpleType name="tDeadline-expr">
        <restriction base="string"/>
    </simpleType>

    <simpleType name="tBoolean">
        <restriction base="string">
            <enumeration value="yes"/>
            <enumeration value="no"/>
        </restriction>
    </simpleType>
</schema>

服务链接类型模式

<?xml version='1.0' encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
   xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/" 
   targetNamespace="http://schemas.xmlsoap.org/ws/2002/07/service-link/"
   elementFormDefault="qualified">
    
   <element name="serviceLinkType" type="slnk:tServiceLinkType"/>
      
   <complexType name="tServiceLinkType">
      <sequence>
         <element name="role" type="slnk:tRole" minOccurs="1" maxOccurs="2"/>
      </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </complexType>
   
   <complexType name="tRole">
      <sequence>
         <element name="portType" minOccurs="1" maxOccurs="unbounded">
            <complexType>
               <attribute name="name" type="QName" use="required"/>
            </complexType>
         </element>
      </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </complexType>
</schema>

服务引用模式

<?xml version='1.0' encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
        xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
   targetNamespace="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"
      elementFormDefault="qualified">

   <import namespace="http://schemas.xmlsoap.org/wsdl/"/> 

   <element name="serviceReference" type="sref:serviceReferenceType"/>

   <complexType name="serviceReferenceType">
      <sequence>
         <element ref="wsdl:definitions" minOccurs="0"/>
         <element name="service" type="QName"/>
         <element name="referenceProperties" minOccurs="0">
            <complexType>
               <sequence>
                  <element name="property" type="sref:referencePropertyType" 
                           maxOccurs="unbounded"/>
               </sequence>
            </complexType>
         </element>
      </sequence>
   </complexType>

   <complexType name="referencePropertyType">
      <sequence>
         <any namespace="##other" minOccurs="0"/>
      </sequence>
      <attribute name="name" type="QName"/>
   </complexType>
</schema>

消息属性模式

<?xml version='1.0' encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://schemas.xmlsoap.org/ws/2002/07/business-process/"           
        xmlns:wsbp="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
        elementFormDefault="qualified">
 
    <element name="property">
       <complexType>
          <attribute name="name" type="NCName"/>
          <attribute name="type" type="QName"/>
       </complexType>
    </element>

    <element name="propertyAlias">
       <complexType>
          <attribute name="propertyName" type="QName" />
          <attribute name="messageType" type="QName"/>
          <attribute name="part" type="NCName"/>
          <attribute name="select" type="string"/>
       </complexType>
     </element>
</schema>


到页首
您对这篇文章的看法如何?
真棒!(5)好材料 (4)一般;尚可 (3)需提高 (2)太差! (1)

建议?


(c) Copyright IBM Corp. 2001, (c) Copyright IBM China 2001, All Right Reserved
  关于 IBM  |  隐私条约  |  法律条款  |  联系 IBM