IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services  >

使用 BPEL4WS 的业务流程: 学习 BPEL4WS,第 8 部分

使用 switch、pick 和 compensate

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Rania Khalaf (rkhalaf@watson.ibm.com), 软件工程师, IBM TJ Watson Research Center
Nirmal Mukhi (nmukhi@us.ibm.com), 软件工程师, IBM TJ Watson Research Center

2003 年 5 月 01 日

本文阐明另外三个 BPEL 活动的使用:switch、pick 和 compensate。除了介绍如何使用 <switch> 在条件上进行分支之外,我们还将说明如何利用 <pick> 根据传入消息或超时进行分支。我们还将给出一个简单明了的补偿示例来说明如何撤销已提交的动作。

引言

在前几篇文章中我们讨论了一个简单的可调用 Web 服务的流,之后加入了另一个伙伴、通过链接和条件实现的控制逻辑、使用赋值的数据操作、嵌套活动和作用域,最后讨论了相关性集合和错误处理程序。本文中我们使用贷款批准示例并给出三种不同情况来说明复合活动 <switch><pick> 的使用,还给出了一个简单的补偿处理程序。

首先来看一下我们已经创建的流程,并去掉最后几个活动。 图 1用一个大号粗体问号代替这些活动,我们将在后面的试验中使用这个问号。为了回顾前面的内容,我们设定有一个想要申请贷款的客户。客户向流程发送一个消息,之后流程检查贷款量是高于还是低于某个特定的数额。如果贷款额偏低,则调用一个 Web 服务来检查申请人是否风险较小,如果是的话将同意贷款。否则调用贷款评估者来检查申请人的信息并决定是否同意贷款。这个过程非常类似本系列文章 第 5 部分中的流。


我们提供三种不同情况来替代问号




回页首


第 1 种情况:使用 <switch> 进行分支

第一种情况下, <switch> 活动用于执行分支,与我们在讨论相关性的文章中所见到的操作相同,但不使用链接。原理在于,如果申请人符合贷款条件,他就发送另一个消息请求处理贷款。如果新请求中的贷款额高于最初申请的贷款额,流程将出错。如果不是,将返回一个应答通知申请人贷款已处理。

在前面的示例中这通过 <receive> 活动实现,之后跟随两个指向 <throw><reply> 的链接。现在,我们将保留 <receive> ,但其他地方使用 <switch> 活动。这个活动类似于 Java 环境下的开关编程构件,区别在于最多只能选择一个分支。它包含几个分支条件,每个分支条件后跟一个活动。分支语句依次求值,而第一个求出 true 值的语句将执行它的活动。

我们要添加的 <switch> 将包含两个分支:一个检查贷款额是否小于或等于最初申请额,并包含一个 <reply> 活动;另一个在所有其他情况下运行,它使用 <otherwise> 构造定义,并包含一个 <throw> 活动。第二个分支当然也可以由检查贷款额是否过高的条件组成。我们在这里阐明 <otherwise> 构造是为了提醒您 <switch> 可以不总是为一组链接的活动所替代,因为将每种可能的情况用布尔条件进行列举是不太可能而且不实际的。 <receive><reply><throw> 完全是从 前面的文章中复制得来。 图 2显示了我们添加用来替代上面问号的那部分,相应的 BPEL4WS 语法见 清单 1


使用 switch
清单 1. 使用 <switch>
<receive name="acceptance-receive"
     partner="customer"
     portType="lns:loanApprovalPT"
     operation="obtain"
     container="acceptanceRequest"> 
  <target linkName="reply-to-receive"/> 
  <source linkName="receive-to-switch"/> 
  <correlations> 
    <correlation set="loanIdentifier"/> 
  </correlations> 
</receive> 
<switch name="check-final-amount"> 
  <target linkName="receive-to-switch"/> 
  <case condition="bpws:getContainerData('acceptanceRequest', 'amount') <= 
       bpws:getContainerData('request', 'amount')"> 
    <reply name="grant-reply"
         partner="customer"
         portType="lns:loanApprovalPT"
         operation="obtain"
         container="approvalInfo"/> 
  </case> 
  <otherwise> 
    <throw name="grant-failure" faultName="lns:loanProcessFault"/> 
  </otherwise> 
</switch>





回页首


第 2 种情况:使用 <pick> 响应事件

这种情况描述了一种实现相同功能的不同的方法。与之前一样,申请人应该发送一条请求贷款申请得以处理的消息。如同您所见,消息到达可以通过 <receive> 活动模拟。作为另一种选择,您也可以将消息到达视为一个事件的发生。BPEL4WS 提供了一个特定活动,它是针对所有异步事件的定义和处理而设计的。这就是 <pick> 活动,它可以处理两种事件发生:

  1. 消息到达:使用 <pick> 活动中的 <onMessage> 元素进行模拟。语法非常类似 receive。 <onMessage> 使您还可以定义一个活动来处理消息到达。
  2. 预设计时器过期:使用 <pick> 中的 <onAlarm> 元素进行模拟。您可以指定一个持续时间段,在其结束时触发警报,执行相关联的活动。

<pick> 执行与发生即完成的事件相关联的活动。在我们特定的例子中,我们使用 pick 中的 <onMessage> 来模拟申请人消息的到达。相关联的活动就是对 onMessage 的应答。另外, <pick> 还有一个 <onAlarm> 元素。它用作对申请人响应的超时。在之前的情况下,如果申请人从不响应,则流程停留在相同的未完成状态。在这个版本中,使用 <onAlarm><pick> 活动执行 <onAlarm> 段(该段只包含一个 <empty> 活动,因为我们不打算做除了超时以外的事情)并完成,如果申请人的消息不在 <pick> 启动后的 30 秒之内到达,则结果是流程示例完成。因此如果消息到达事件先于计时器开始计时,则处理申请人的消息并执行 <switch> ,否则执行 <empty> 而 pick 以这种方式终止。

我们添加用于替代上面的问号的段在 图 3中显示,相应的 BPEL4WS 语法见 清单 2


使用带有 onMessage 和 onAlarm 的 pick
清单 2. 使用带有 <onMessage><onAlarm><pick>
<pick createInstance="no" name="check-acceptance"> 
      <target linkName="reply-to-pick"/> 
      <onMessage partner="customer"
           portType="lns:loanApprovalPT"
           operation="obtain"
           container="acceptanceRequest"> 
        <correlations> 
          <correlation set="loanIdentifier"/> 
        </correlations> 
        <switch name="check-final-amount"> 
          <case condition="bpws:getContainerData('acceptanceRequest', 'amount') <=
               bpws:getContainerData('request', 'amount')"> 
            <reply name="grant-reply"
                 partner="customer"
                 portType="lns:loanApprovalPT"
                 operation="obtain"
                 container="approvalInfo"/> 
          </case> 
          <otherwise> 
            <throw name="grant-failure" faultName="lns:loanProcessFault"/> 
          </otherwise> 
        </switch> 
      </onMessage> 
      <onAlarm for="'PT30S'"> 
        <empty/> 
      </onAlarm> 
    </pick>





回页首


第 3 种情况:使用 <compensate> 撤销操作

我们的最后一种情况是,如果客户发送的贷款额高于已经批准的金额,将请求流程补偿风险评估调用。第一步是用带有补偿处理程序的作用域覆盖那个 <invoke> 活动( 图 1,左侧)。由于这主要用于说明目的,我们只在处理程序内写入 <empty> 。在作用域成功运行之后,它的补偿处理程序等待一个信号以实际启动运行。这个信号可以是显式的,由运行 <compensate> 活动而得,也可以是由于出错而得来的隐式信号。请注意,要显式补偿的作用域必须有名称。

一个更为有用的处理程序可以包含 <invoke> ,指向贷款评估者上的 cancel 操作。我们在这里只使用 <empty> ,因此您不必修改 Java 代码和贷款评估者服务的 WSDL 文件。

清单 3所示代码片断中,添加部分用粗体显示。另请参阅 图 4


添加补偿处理程序
清单 3. 添加补充处理程序
				
        <scope name="assessor-scope"> 
   <target linkName="receive-to-assess"/> 
   <compensationHandler> 
      <empty/> 
    </compensationHandler>
    <invoke name="invokeAssessor" partner="assessor"  
                portType="asns:riskAssessmentPT"  operation="check" 
                inputContainer="request"  outputContainer="riskAssessment"> 
       <source linkName="assess-to-setMessage"  
                   transitionCondition=
                   "bpws:getContainerData('riskAssessment', 'risk')='low'"/> 
       <source linkName="assess-to-approval"  
                   transitionCondition=
                   "bpws:getContainerData('riskAssessment', 'risk')!='low'"/> 
     </invoke> 
        </scope>
			
      

在 BPEL 中,您只能调用来自错误内部的显式补偿,或者调用要补偿的部分的作用域中的补偿处理程序。因此,我们将修改流程上的错误处理程序,使其包括一个指向 assessor-scope<compensate> 活动;因此请回到流程,修改错误处理程序的定义以使它包括 compensate 活动。现在如果有错误,我们将首先补偿前面所提的作用域,将一条表示所请求的贷款额过高的消息放入容器中,并将它反馈给客户:


在作用域上添加错误处理程序
清单 4. 添加补偿处理程序
  <process .... >
    ....
  <faultHandlers>
    <catch faultName="lns:loanProcessFault" 
           faultContainer="error">
       <sequence name="fault-sequence">
         <compensate scope="assessor-scope"/>
         <assign name="assigninvalid"> 
           <copy> 
             <from expression="'invalid request: amount too high'"/> 
             <to container="approvalInfo" part="accept"/> 
           </copy> 
         </assign> 
         <reply name="grant-reply" partner="customer"  
                portType="lns:loanApprovalPT"  
                operation="obtain"  container="approvalInfo"/> 
       </sequence>
    </catch>
  </faultHandlers>
 

现在我们回头来看看问号部分。我们来重新使用 <pick><switch> 结构。我们还是用 <pick> 来等待消息,在一段时间后作超时处理并结束进程。现在,我们使用类似您在上面所见到的 <switch> 处理进入消息,而不是使用简单应答。它检查所请求的贷款额是否低于或等于客户可以申请的数额。如果低于,我们将应答客户。如果不是,我们打算结束流程,但首先想用补偿处理程序补偿作用域,并返回表示请求的贷款额过高的消息。为此,我们使用 throw活动抛出错误,该活动将触发我们刚才定义的处理程序。之后处理程序将执行补偿和应答操作。如果 30 秒过后我们还没有听到任何声音,警报就被触发,而流程就会结束。


pick/switch 结合以及使用 compensate 的显式赔偿
清单 5. pick/switch 结合以及使用 <compensate> 的显式补偿
<pick createInstance="no" name="check-acceptance"> 
    <target linkName="reply-to-pick"/> 
    <onMessage partner="customer"
         portType="lns:loanApprovalPT"
         operation="obtain"
         container="acceptanceRequest"> 
      <correlations> 
        <correlation set="loanIdentifier"/> 
      </correlations> 
      <switch name="check-final-amount"> 
        <case condition="bpws:getContainerData('acceptanceRequest', 'amount') <=
             bpws:getContainerData('request', 'amount')"> 
          <reply name="grant-reply"
               partner="customer"
               portType="lns:loanApprovalPT"
               operation="obtain"
               container="approvalInfo"/> 
        </case> 
        <otherwise> 
           <throw faultName="lns:loanProcessFault"/>        
        </otherwise> 
      </switch> 
    </onMessage> 
    <onAlarm for="'PT30S'"> 
      <empty name="alarm-empty"/> 
    </onAlarm> 
</pick>





回页首


在 BPWS4J 中运行三种情况

请阅读来自本系列文章 第 7 部分关于运行流程的部分。请注意我们的情况采用测试功能所用的相同服务(例如所有的 WSDL 文件,包括流程接口,都与此处所述一致)和相同客户。唯一的区别就是用于每种情况的 BPEL 文件与此处所使用的文件稍有不同,因为不同情况的流程逻辑是不同的。

每种情况都描述了同名流程的一个已修改版本。这个名称用于标识一个部署流程。因此如果您运行一种情况并想尝试另一种情况,您需要取消第一个流程的部署以便下一种情况的部署开始运转。

要运行每种情况,请按照 以前的文章所描述的那样来部署流程(即对不同的情况使用不同的 BPEL 文件)。下面是用于每种情况的 BPEL 文件列表,以及关于如何使用客户机来测试功能的注释。部署所需的这些 BPEL 文件和所有 WSDL 文件都包括在压缩文件中,您可以从 参考资料部分中下载它们。

第 1 种情况:使用 <switch> 进行分支


使用 loanapproval-switch.bpel 文件。

您可以像本系列以前的文章所描述的那样来运行客户机 ,因为流程的特点总是一样的。如前所述,这只是 switch 的一种应用,用来说明表达逻辑的另一种方法。

第 2 种情况:使用 <pick> 响应事件


使用 loanapproval-pick.bpel 文件。

在这里,我们的流程具有前一种情况下未出现的特点,这就是超时的使用。要测试这个特点,您可以使用客户机来请求贷款批准。一旦流程应答,该特点就会期待客户机询问是否可获取贷款。但它只为客户发送消息等待 30 秒时间,如果计时结束时消息仍未到达,则流程完成。要测试它,一旦贷款批准完成,请不要再次运行客户机以发送请求来获取贷款;相反,什么都不要做。在您的服务器控制台,您将观察到流程的完成(想了解更多详细信息,您可以打开日志记录查看)。如果客户机在流程完成后才发送 obtain消息,BPWS4J 引擎无法将其与正在运行的流程实例相匹配,这样就会返回一个错误。

第 3 种情况:使用 <compensate> 撤销操作


使用 loanapproval-compensation.bpel 文件。

这具有与 pick 的情况类似的特点,额外的步骤就是 BPEL 文件描述将要运行补偿块的情况。为测试这一点,您应该首先打开日志记录。日志将使您了解到补偿块被执行,因为客户机不知道此活动。请运行您的客户机(确保您使用来自 第 7 部分的脚本)请求批准贷款。之后,再次运行客户机请求获取贷款,但指定高于已批准的数额的贷款额。您将在日志消息上看到补偿块的执行,并收到 amount too high的响应。





回页首


结束语

在这些情况下,我们已经综合说明了 BPEL4WS 的大部分主要概念。作为本系列教程类型的文章的最后一篇,本文使您更好地了解如何集中使用活动来创建简单的和复杂的流程。尽请尝试我们在这里已经实现的内容,以便您深入了解这种语言:使用最终得到的 .bpel 文件并套用语法来尝试语言中的不同结构,观察它们如何影响控制流以及您想在 Web 服务组合中表达的内容的复杂性。您可以使用 alphaWorks 上 BPWS4J 的 Eclipse 编辑器(请参阅 参考资料),并在图解上替换活动或者修改其属性,而不必担心影响流程的其他部分。例如,您可以使用一个嵌套的活动并请求将该活动打包到作用域中,之后为其添加处理程序。



参考资料

  • 请参加本文的 讨论论坛。(您也可以点击文章顶部或底部的 讨论来访问此论坛。)

  • 请注意本文参照的是 BPEL4WS 规范的 版本 1.0。现在这个规范有了最新版本( BPEL4WS1.1),并且很快将会有一篇文章来描述两个版本之间的关键区别。

  • 从 alphaWorks 下载 BPWS4J 引擎

  • 请下载包含代码示例的 压缩(zip)文件



作者简介

Rania Khalaf是 IBM T.J. Watson Research Center 的 Component Systems 小组的一名软件工程师。2001 年,她从 MIT 获得学士学位和工程硕士学位后加入 IBM。Rania 是 IBM BPEL4WS 引擎 BPWS4J 的创作者之一,您可以从 alphaWorks 获得 BPWS4J。您可以通过 rkhalaf@watson.ibm.com与 Rania 联系。


Nirmal Mukhi是 IBM T.J. Watson Research Center 的 Component Systems 小组的一名软件工程师。他从 Indiana University 获得硕士学位。在 IBM 他负责 Web 服务方面的研究,并参与过 WSIF、Web Services Gateway 和 BPWS4J 等项目。您可以通过 nmukhi@us.ibm.com与 Nirmal 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款