级别: 初级 Matthew J. Duftler (duftler@us.ibm.com), 软件工程师, IBM TJ Watson Research Center Francisco Curbera (curbera@us.ibm.com), Component Systems 小组主管, IBM TJ Watson Research Center Rania Khalaf (rkhalaf@watson.ibm.com), 软件工程师, IBM TJ Watson Research Center
2003 年 3 月 01 日 本系列前面第二部分中的示例演示了如何构建一个调用 Web 服务的简单的 BPEL4WS 流程。为了阐明链接、条件和 <assign> 活动的使用,本文将继续那个示例并将它扩展到 BPEL4WS 规范和 BPWS4J 示例所包含的贷款批准流程中。链接将活动连接在一起,并允许由每个活动的条件的规范来确定是否应该遵循该链接。在 BPEL4WS 中条件是 XPath 表达式,并且本文将显示条件如何合并流程的容器数据。当数据不是作为一个 <invoke> 活动的结果被直接复制时,您可以使用 <assign> 活动将数据复制到一个容器中。
引言
本文将把
我们先前创建的流程扩展到 BPEL4WS 规范和 BPWS4J 示例所包含的贷款批准示例中。文章阐明了用于定义编排的两个核心功能:使用受控制的链接来定义控制的流(flow),以及使用
<assign> 活动操作数据。我们假定您已经阅读并理解了先前的示例,并在这个基础上与您深入探讨。和先前的示例一样,我们将在总结时描述该流程是如何运行的以及它在 BPWS4J 引擎中的运行结果。
我们将为您演示一个处理相同贷款请求的流程 — 客户发出一个贷款请求,该请求得到处理,然后客户确定贷款是否得到了批准。最初,中间步骤包括简单地调用一个金融机构的 Web 服务并将该 Web 服务的应答反馈给客户。与这个基本步骤不同,您想要在应用程序处理过程中使用一些附加的逻辑。您可以通过执行以下一系列步骤来试着确定是否可以不去金融机构(
贷款批准者)进行全面审查而授权贷款;如果请求的贷款金额较大,那么该请求必须被发送到金融机构进行审查。如果请求的贷款金额较小,您就调用一个新的 Web 服务(名为
贷款评估者)来确定风险。如果评估者确定给申请者发放贷款的风险不高,那么该贷款请求将被批准。反之,请求将被发送到金融机构进行全面审查。
建立流程
在 BPEL 中,该流程是通过使用
<flow> 活动建立的。请记注
<flow> 活动允许您定义用于连接它所包含的活动的链接,因此您可以将逻辑放在这个活动中来完成上述处理。请保持
<receive> 和
<reply> 活动和以前完全一样,并添加两个
<invoke> 活动 — 一个用于评估者而另一个用于批准者。您也可以通过添加一个
<assign> 活动来把您的消息放入应答(reply)中。接着,使用链接将
<receive> 连接到两个调用(invoke)。用于控制这些链接的条件有两个:如果贷款金额小于 10,000,那么您想要调用评估者;而如果贷款金额大于或等于 10,000,则您想要调用批准者。然后,当另一个条件为风险高时,将评估者的调用链接到批准者的调用,而当风险低时,则将评估者的调用链接到
<assign> 活动。最后,当不指定任何条件(缺省)时,将批准者的调用和赋值(assign)链接到
<reply> 活动。对于这个流程来说,请牢记控制的流是由控制每个链接的条件的值所决定的。我们将在下面的文章中详细解释这些概念。最终的结构如
图 1所示。
服务描述
贷款评估者的 WSDL 描述如
清单 1所示。评估者的服务可以执行
check操作,该操作返回与发放给客户贷款有关的风险级别。
清单 1. 贷款评估者的 WSDL 片断(loanassessor.wsdl)
<definitions
targetNamespace="http://tempuri.org/services/loanassessor"
xmlns:tns="http://tempuri.org/services/loanassessor"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:loandef="http://tempuri.org/services/loandefinitions"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<import namespace="http://tempuri.org/services/loandefinitions"
location=
"http://localhost:8080/bpws4j-samples/loanapproval/loandefinitions.wsdl"/>
<message name="riskAssessmentMessage">
<part name="risk" type="xsd:string"/>
</message>
<portType name="riskAssessmentPT">
<operation name="check">
<input message=
"loandef:creditInformationMessage"/>
<output message="tns:riskAssessmentMessage"/>
<fault name="loanProcessFault" message=
"loandef:loanRequestErrorMessage"/>
</operation>
</portType>
<binding ...> ... </binding>
<service name="LoanAssessor">....</service>
</definitions>
|
在流程的定义中多添加一个
serviceLinkType ,为它与新的
assessor伙伴的交互建模(
清单 2)。所添加的服务链接类型表示如果某人想要成为一个评估者,他(或她)必须事先定义风险评估端口类型。
清单 2. 要添加在贷款评估 WSDL 中的代码
<slnk:serviceLinkType name="riskAssessmentLinkType">
<slnk:role name="assessor">
<portType name="asns:riskAssessmentPT"/>
</slnk:role>
</slnk:serviceLinkType>
|
创建流程
下一步是定义流程。从上次建立的流程开始,除去
<sequence> 和
<invoke> 。然后,为了将贷款评估者合并到流程中,以刚才定义在
serviceLinkType 中的评估者角色将它添加为一个伙伴,并且添加一个容器以接收它的输出消息。将下列内容添加到伙伴列表中:
<partner name="assessor"
serviceLinkType="lns:riskAssessmentLinkType"
partnerRole="assessor"/> |
将下列内容添加到容器列表中:
<container
name="riskAssessment"
messageType="asns:riskAssessmentMessage"/> |
流和链接
链接的出现使得
<flow> 活动的使用成为必然,该活动的定义从下面的
清单 3 开始。通过赋予链接名称,您可在
<flow> 活动本身定义要使用的链接。
在定义好链接之后,您可以用他们将
<receive> 活动链接到两个
<invoke> 活动。(注意:由于在 BPEL 中使用链接的名称来把两个活动链接在一起,因此这两个活动需要被包含在定义了相关链接的
<flow> 活动中。)链接的源活动的定义(即本例中的
<receive> 活动)对每个传出链接都将对应一个
<source linkName="[link_name] "> 元素,并且链接的目标活动的定义(即本例中每个
<invoke> 活动)对每个传入链接都将对应一个
<target linkName="[link_name]"> 。每个链接只能有一个源和一个目标。
一个链接在它的源活动运行前或正在运行时处于缺省状态。一旦源活动运行结束,它就会根据一个布尔值来激活每一个链接,而这个布尔值就是评估
转换条件(join condition)所得的结果。为了更确切地评估一个链接的布尔值,转换条件被定义在指向链接的
<source> 元素中。对于
<receive> 活动来说,转换条件测试请求的贷款金额是否低于 10,000。如果没有定义惶跫蚴褂萌笔≈?
true。
在流上定义链接并根据链接的条件指定从
<receive> 活动中的一个链接开始。您也可以定义评估者的
<invoke> 活动,它是
receive-to-assess链接的目标,也是它自己的两个链接的源。
清单 3. <flow> 活动的定义
<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 name="receive1" partner="customer"
portType="apns:loanApprovalPT"
operation="approve" 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 name="invokeAssessor" partner="assessor"
portType="asns:riskAssessmentPT"
operation="check" inputContainer="request"
outputContainer="riskAssessment">
<target linkName="receive-to-assess"/>
<source linkName="assess-to-setMessage"
transitionCondition=
"bpws:getContainerData('riskAssessment', 'risk')='low'"/>
<source linkName="assess-to-approval"
transitionCondition=
"bpws:getContainerData('riskAssessment', 'risk')!='low'"/>
</invoke>
|
条件和数据赋值
参照
图 1,请考虑从
<receive> 活动到评估者的
<invoke> 活动的链接。在上述流定义中,这是
receive-to-assess 链接,并且这个链接受到
bpws:getContainerData('request', 'amount')<10000 转换条件的控制。和所有条件一样,转换条件必须是 XPath 表达式并且返回值必须为布尔型。这个特殊的 XPath 表达式使用 BPEL4WS 引入的 XPath 扩展函数之一:
bpws:getContainerData(...) 。任何希望从流程内的容器中检索数据的表达式都可以调用
bpws:getContainerData(...) 函数。该函数的说明如下:
bpws:getContainerData("containerName", "partName", "locationPath"?) ,其中
containerName 是容器的名称,
partName 是相应容器中的一个部件的名称,而
locationPath 是指定部件的一个可选的绝对位置路径。
从先前的示例可以看出
bpws:getContainerData('request', 'amount') 函数从何处获取数据。该函数首先查找在 loanapproval.bpel 文件中定义的
request 容器。您可以从 request 容器的定义中看出该容器拥有一个
loandef:CreditInformationMessage 类型的消息。通过查阅 loandefinitions.wsdl 文件,您可以看出
loandef:CreditInformationMessage 消息类型包含一个名为
amount 的部件,它的类型为
xsd:integer 。现在您知道调用
bpws:getContainerData('request', 'amount') 函数将返回一个整数,如果这个整数小于 10000,则评估 XPath 表达式
bpws:getContainerData('request', 'amount')<10000 后将返回 true,反之则返回 false。结果,
<receive> 活动根据所得的布尔值激活
receive-to-assess链接。
同时也看一下
assess-to-setMessage 链接以确定如何评估它自己的转换条件(
bpws:getContainerData('riskAssessment', 'risk')='low' )。从以上定义的流程中可以看出
riskAssessment 容器拥有一个
asns:riskAssessmentMessage 类型的消息,您可以从 loanassessor.wsdl 文件中看出
asns:riskAssessmentMessage 消息类型仅包含一个部件:名为“risk”,类型为
xsd:string 。因此,调用
bpws:getContainerData('riskAssessment', 'risk') 函数会返回一个字符串,然后将它与字符串“low”进行比较。如果两者匹配,则整个 XPath 表达式的值为 true。由于条件基本上可以包含任何返回布尔值的 XPath 表达式,请牢记可以用多种方式获取相同的结果。例如,您可以用
contains(bpws:getContainerData('riskAssessment', 'risk'),'low') 或
starts-with(bpws:getContainerData('riskAssessment', 'risk'), 'lo') 取代
bpws:getContainerData('riskAssessment', 'risk')='low' 。
这时,已经有两个容器存在,但没有一个包含了可以直接反馈给客户的信息;其中一个包含客户的初始的请求信息,另一个则包含字符串
low。而需要反馈给客户的是字符串
yes,为此,您需要定义一个
<assign> 活动,如
清单 4所示。
清单 4. <assign> 活动
<assign name="assign">
<target linkName="assess-to-setMessage"/>
<source linkName="setMessage-to-reply"/>
<copy>
<from expression="'yes'"/>
<to container="approvalInfo" part="accept"/>
</copy>
</assign>
|
您可以从上面的清单中看出,
<assign> 活动是
assess-to-setMessage链接的目标,同时也是
setMessage-to-reply 链接的源。它包含一个复制(copy)元素,并且该元素使用
<from> 元素的通用表达式格式。当表达式使用
<from> 元素的通用表达式格式时,它可以是 XPath 所允许的任何东西,假设它返回一个 XPath 值类型(字符串、数字或布尔值)。在本例中,
<from> 元素的表达式只是简单地指定字符串
yes,然后将它复制到
approvalInfo容器的
accept部件中。您可以从先前的示例中看出
approvalInfo容器内包含的消息的
accept 部件的类型为
xsd:string 。
最后,保持批准者的
<invoke> 和
<reply> 活动的定义和先前一样(请参阅
清单 5),只是用先前定义的链接将这两个活动连接到剩余的活动。应答将发送贷款是否被批准的结果,这个结果现在应该在
approvalInfo 容器中。请记住至此该容器将被填充,因为不是
<assign> 活动将
yes 填入其中就是批准者的
<invoke> 活动将结果填入其中。就像在“各种活动以及内存中模型”一文(请参阅
参考资料)中描述的一样,作为某些链接的目标的活动会一直等待,直至它所有的链接都被激活,并从它嵌套的活动中获得控制权。这种活动的缺省行为是只有当它的链接激活值中至少有一个为
true,它才开始运行;否则,它就会非正常结束并将 false 发送给它的链接。所以批准者只有在获得它全部两个链接的值且其中必须有一个为 true 时才可以运行。
清单 5. 批准者的 <invoke> 和 <reply>
<invoke name="invokeapprover"
partner="approver" portType="apns:loanApprovalPT"
operation="approve"
inputContainer="request"
outputContainer="approvalInfo">
<target linkName="receive-to-approval"/>
<target linkName="assess-to-approval"/>
<source linkName="approval-to-reply" />
</invoke>
<reply name="reply" partner="customer"
portType="apns:loanApprovalPT"
operation="approve" container="approvalInfo">
<target linkName="setMessage-to-reply"/>
<target linkName="approval-to-reply"/>
</reply>
</flow>
</process>
|
这里我们主要讨论当活动的连接条件为 false 时会发生什么情况。在缺省情况下,这个问题转化为如果传入一个活动的所有的链接都为 false,那么会发生什么情况。我们先前提到这种情况下该活动将被禁用并且将 false 发送给它的链接;但这并不完全正确。事实是这种情况在 BPEL 中被认为是一个错误,该活动会抛出一个
joinFailure 错误,而如果无法捕捉到这个错误,则整个流程就会被禁用。然而,能够避免发生这种情况的一种方法是设置
<process> 元素的全局
suppressJoinFailure 属性。这样将避免传播任何
joinExceptions 。您可以从完整的 BPEL 文件中看出,在本示例中该属性被设置为
true。我们将在后续的文章中更详细地讨论异常。
总结
在这部分中,我们将为您展示执行该流程可能遵循的路径。下面的
图 2 显示了流程可能遵循的路径。(注意:为减少混乱,图中只显示了 flow 活动的要点。)请详细观察最左边的情景。一旦有客户发出贷款请求,流程就会启动,根据所请求的贷款金额是否超过 10,000,分别激活由
<receive> 活动传出的两个链接(一个为 false 链接,另一个为 true 链接)中的一个。假设第一种情况贷款金额很小,此时正确的链接是转向当前准备就绪的评估者的
<invoke> 活动的链接。而转向批准者的第二条链接变为 false,但是批准者只有等到它的
<invoke> 活动获取自身第二条链接的值之后才能做出反应。此时评估者的
<invoke> 活动运行。
假设评估者的返回值指出发放贷款的风险太大。此时
assessor-to-approver链接变为 true,并且由于批准者的一条链接的值为 true,批准者也会运行。值得注意的是,在 BPEL 中,带有多个传入链接的活动可以在传入的链接上定义它自己的条件(称为
连接条件)。如果这个活动需要更加复杂的检查而不仅仅是需要缺省值或传入链接上的值,那么就需要定义链接条件。此时调用了批准者。
一旦批准者的
<invoke> 活动完成(将它的结果放入
approvalInfo 容器),由它传出的链接就变为 true。这时您也许想要了解应答(reply)将如何运行,因为它不得不等待自身的第二条链接获得由
<assign> 活动传入的值。而事实上应答的第二条链接的确获得了一个值,并且这个值为 false。这是因为:在评估者的
<invoke> 活动结束之后,
assessor-to-approver链接变为 true,与此同时
assessor-to-setMessage 链接变为 false。由于没有其他传入链接,
<assign> 活动被禁用并将 false 发送给它的链接。现在
<reply> 活动的两个链接都被赋值且其中一个为 true。这样它就将消息发送给客户并且整个流程到此结束。
在 BPWS4J 引擎中运行流程
和我们先前的文章一样,如果您想要运行这个流程,您将需要下载并安装可以从 alphaWorks(请参阅
参考资料)上获得的 BPWS4J 引擎。
本文描述的流程就是和 BPWS4J 发行版一起分发的贷款批准示例,但我们除去了其中的错误处理程序。我们将在本系列的后续文章中讨论并阐明错误处理程序。
请遵循 BPWS4J 文档中和运行贷款批准示例相关的指令。
请记住,如果您想要了解更多幕后情况,请转到 webapps/bpws4j/WEB-INF/classes 目录下的 log4j.properties 文件并取消对第 24 行(
log4j.logger.bpws.runtime.flow.base=DEBUG )的注释。
下一次
在本系列的下一篇文章中,我们将讨论相关性和错误处理。其后会有另一个示例 BPEL 流,它将阐明一个合并了上述两个特征的具体流程。
参考资料
作者简介  | |  | Matthew J. Duftler 是 IBM T.J. Watson Research Center 的 Component Systems 小组的一名软件工程师。他是 Apache SOAP 的原作者之一,是 JSR110(Java APIs for WSDL)的带头人之一,还是 IBM BPEL4WS 引擎的创作者之一。您可以通过
duftler@us.ibm.com与 Matthew 联系。
|
 | |  | Francisco Curbera 是 IBM T.J. Watson Research Center 的 Component Systems 小组的一名研究人员,也是该组的主管。他是 BPEL4WS 规范、WSDL 规范以及 WSFL 规范的作者之一,也是 BPWS4J、Apache SOAP 和 WSTK 的开发者之一。他获得了哥伦比亚大学(Columbia University)的应用数学博士学位。您可以通过
curbera@us.ibm.com与他联系。
|
 | |  | Rania Khalaf 是 IBM T.J. Watson Research Center 的 Component Systems 小组的一名软件工程师。2001 年,她从 MIT 获得学士学位和工程硕士学位后进入 IBM。Rania 是 IBM BPEL4WS 引擎的创建者之一,您可以从 alphaWorks 上获得 BPWS4J。您可以通过
rkhalaf@watson.ibm.com与 Rania 联系。
|
对本文的评价
|