客户发出一个贷款请求;请求得到处理,然后客户弄清楚贷款是否得到了批准。一开始,中间那个步骤将包括向启用 Web 服务的金融机构发送申请并将决定告诉客户。从客户的角度看,该流程将使用他的申请,然后给他发送一个应答。
<receive>、<invoke> 和 <reply> 活动定义了这三个操作。然而,该流程需要定义这些简单的活动彼此之间的关系,以便知道如何以及何时运行这些活动。在 BPEL 中通过使用结构化活动来定义这些关系,这些结构化活动在如何运行它们包含的活动方面定义了一些限制。在这个示例中,您想让这三个操作一个接一个地发生。在 BPEL 中可以通过使用 <sequence> 活动获得这样的顺序,<sequence> 活动首先包含 <receive> 来消费消息,然后跟着是一个 <invoke> 来和金融机构交谈,最后以 <reply> 来向客户发送应答。因此,上面的云状图将包含以这个顺序进行的三个活动的流程,并且可以调用金融机构,如图 2 所示。
BPEL 整合对所涉及的服务的 WSDL 描述依赖性很大,这是为了引用正在被交换的消息、正在被调用的操作以及这些操作所属于的 portType。在这个示例中,您将需要金融机构以及这个流程本身的描述。要考虑到金融界使用一组统一的消息来描述贷款信息,并且在清单 1 的贷款定义中定义了这些消息。
清单 1:贷款定义 WSDL(loandefinitions.wsdl) <definitions targetNamespace="http://tempuri.org/services/loandefinitions" xmlns:tns="http://tempuri.org/services/loandefinitions" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"> <message name="creditInformationMessage"> <part name="firstName" type="xsd:string"/> <part name="name" type="xsd:string"/> <part name="amount" type="xsd:integer"/> </message> <message name="loanRequestErrorMessage"> <part name="errorCode" type="xsd:integer"/> </message> </definitions>
假定您知道有一个提供贷款批准服务的金融机构,其描述如下面清单 2 所示。该金融机构只包含单个操作“approve”,它用这个操作确定一个贷款请求的状态。该操作把有关客户的信息作为输入,然后输出一条包含应答的批准消息。在上面的 loandefinitions WSDL 中定义了输入消息的定义。
清单 2:贷款批准者 WSDL(loanapprover.wsdl) <definitions targetNamespace="http://tempuri.org/services/loanapprover" xmlns:tns="http://tempuri.org/services/loanapprover" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:loandef="http://tempuri.org/services/loandefinitions" xmlns="http://schemas.xmlsoap.org/wsdl/"> <import namespace="http://tempuri.org/services/loandefinitions" location="http://localhost:8080/bpws-samples/loanapproval/loandefinitions.wsdl"/> <message name="approvalMessage"> <part name="accept" type="xsd:string"/> </message> <portType name="loanApprovalPT"> <operation name="approve"> <input message="loandef:creditInformationMessage"/> <output message="tns:approvalMessage"/> <fault name="loanProcessFault" message="loandef:loanRequestErrorMessage"/> </operation> </portType> <binding ...> ... </binding> <service name="LoanApprover">....</service> </definitions>
流程本身只是将输入消息转发到这个服务中,并将输出消息从这个服务中转发出去。因此,流程将通过引用上面的 portType 来向用户提供同一个描述。还要做的一件事情是为所使用的服务定义 serviceLinkType。serviceLinkType 最多定义两个角色,这两个角色引用的是由 serviceLinkType 链接在一起的任意两个服务提供和需要的 portType。对于这个示例来说,这个 serviceLinkType 将被用于把客户链接到流程,以及将流程链接到贷款批准者。因为流程本身和贷款批者都提供“approver”portType,所以只需要一个角色,并且它们二者都不要求用户支持另一个 portType。您为流程创建下面的清单 3 中的代码:
清单 3:贷款批准 WSDL(loan-approval.wsdl) <definitions targetNamespace="http://loans.org/wsdl/loan-approval" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/06/service-link/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:lns="http://loans.org/wsdl/loan-approval" xmlns:apns="http://tempuri.org/services/loanapprover"> <import namespace="http://tempuri.org/services/loanapprover" location="http://localhost:8080/bpws-samples/loanapproval/loanapprover.wsdl"/> <import namespace="http://tempuri.org/services/loandefinitions" location="http://localhost:8080/bpws-samples/loanapproval/loandefinitions.wsdl"/> <slnk:serviceLinkType name="loanApprovalLinkType"> <slnk:role name="approver"> <portType name="apns:loanApprovalPT"/> </slnk:role> </slnk:serviceLinkType> <service name="loanapprovalServiceBP"/> </definitions>
<process> 元素开始定义,包括将使流程能够引用需要的 WSDL 信息的名称空间(在这个名称空间(http://..../loandefinitions)中定义了消息定义)、贷款批准者的目标名称空间(http://.../loanapprover)以及流程自己的 WSDL 的目标名称空间(http://.../loan-approval)。流程现在可以用贷款批准者服务作为一个组件。
<process name="loanApprovalProcess" targetNamespace="http://acme.com/simpleloanprocessing" xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/" xmlns:lns="http://loans.org/wsdl/loan-approval" xmlns:loandef="http://tempuri.org/services/loandefinitions" xmlns:apns="http://tempuri.org/services/loanapprover">
下一步是声明涉及的各方。定义了已命名的伙伴,每个伙伴都有一个 WSDL serviceLinkType 表明其特征。对于这个示例来说,伙伴是客户和金融机构。伙伴的 myRole/partnerRole 属性指定了给定的 serviceLinkType 的伙伴和流程将如何交互。myRole 属性引用的是流程在 serviceLinkType 中将要扮演的角色,而 partnerRole 指定了伙伴将要扮演的角色。在下面的伙伴定义中对此进行了阐述。贷款批准流程为客户提供了 loanApprovalPT 功能,金融机构接着又为流程提供了这个功能。在上面的图 1 和 2 中可以看到这个关系。
<partners> <partner name="customer" serviceLinkType="lns:loanApproveLinkType" myRole="approver"/> <partner name="approver" serviceLinkType="lns:loanApprovalLinkType" partnerRole="approver"/> </partners>
定义了伙伴之后,您就基本上为开始添加组成整合的活动做好了准备。我们再回头看一下您想让流程做什么。为了申请贷款,客户向流程发送一条消息,流程询问金融机构是否将接受贷款申请,然后用另一条消息(要么是接受申请的消息,要么是拒绝申请的消息)应答客户。在 BPEL 中您如何做到这一点呢?首先,您需要将传入的消息放在 BPEL 活动可以访问到它的地方。在 BPEL 中,数据被写入到数据容器中并且从数据容器进行访问,这些数据容器可以保存特定的 WSDL 消息类型的实例。
从客户伙伴和 loanApprovalPT 的定义可以很明显地看出,客户将发送一条 creditInformationMessage 类型的消息,然后得到一条 approvalMessage 类型的应答。因此,添加了下面的容器列表,这些容器在图 2 中用蓝色圆柱体表示:
<containers> <container name="request" messageType="loandef:CreditInformationMessage"/> <container name="approvalInfo" messageType="apns:approvalMessage"/> </containers>
<sequence>。现在,您可以将一个 receive 活动添加到 sequence 中,这个 receive 活动可以接收客户的消息并把它保存到适当的容器中。receive 活动的定义必须包括将向活动发送它的消息的伙伴,以及伙伴将把它作为这条消息的目的地的流程的端口类型和操作。以这一信息为基础,一旦流程获得一条消息,它就搜索一个具有匹配的 partner-portType-operation 三元组的 receive 活动,然后把消息交给这个 receive 活动。为了避免混淆,BPEL4WS 规范规定在同一时间不能有多个带有相同的 partner-portType-operation 三元组的接收活动处于活动状态。随后,活动将把消息放在指定的容器中,然后结束。您开始 sequence 活动,然后将 receive 添加到其中:
<sequence> <receive name="receive1" partner="customer" portType="apns:loanApprovalPT" operation="approve" container="request" createInstance="yes"> </receive>
下一步是询问启用 Web 服务的金融机构是否将接受贷款。这是用一个常规的 Web 服务调用来完成的,该调用是在流程中由 Invoke 活动定义。Invoke 活动在运行时将使用其输入容器(input container)中的消息来调用指定的 Web 服务,并将得到的应答放入到输出容器(output container)中,然后结束。请注意:将在“approver”伙伴上进行调用以执行 approve 操作。
<invoke name="invokeapprover" partner="approver" portType="apns:loanApprovalPT" operation="approve" inputContainer="request" outputContainer="approvalInfo"> </invoke>
为了使流程对客户的请求作出应答,流程使用一个 Reply 活动。一旦一个应答活动到达,将用流程所拥有的 partner-portType-operation 三元组来找出要把应答发送给谁。因此,为了应答通过 Receive 活动到达的消息,您将需要一个带有相同的三元组的 Reply 活动。在这个例子中,您想要告诉客户金融机构的决定是什么,所以在调用的输出容器中将会发现要被发送的消息:approvalInfo。完成应答之后,流程结束。您关闭了 sequence 标记和 process 标记。
<reply name="reply" partner="customer" portType="apns:loanApprovalPT" operation="approve" container="approvalInfo"> </reply> </sequence></process>
receive 包含了一个名为“createInstance”、被设置为真的属性。这向我们展示了进入流程的一个入口点。图 3 阐述了贷款批准流程将如何运行。
图 3. 运行贷款批准流程。
图注:箭头上的数字表示步骤发生的顺序。黑色信封是包含贷款请求的消息。红色信封是包含对该请求的应答的消息。
客户机向流程管理器发送了一条带有适当的三元组的消息之后,就会有一个流程实例被创建并开始运行。在给定的示例中,流程将启动 sequence,这个 sequence 接着将启动 receive。消息已经到达,所以它将被放入“request”容器中。然后将发生 invoke。在调用后得到的消息被放入到“approvalInfo”容器中后,reply 将获取这个消息并将其发送给客户,此时流程的实例结束。同一个流程的多个实例可以同时运行。当我解释更复杂的流程时,您将看到如何使用相关性(correlation)将消息路由到同一个流程的正确实例上以及如何从同一个流程的正确实例把消息路由出去。