客户发出一个贷款请求;请求得到处理,然后客户弄清楚贷款是否得到了批准。一开始,中间那个步骤将包括向启用 Web 服务的金融机构发送申请并将决定告诉客户。从客户的角度看,该流程将使用他的申请,然后给他发送一个应答。
建立流程:
上面的行为包括:获得消息、然后调用金融机构的 Web 服务以及最后应答客户。在 BPEL 中用 `
1<receive> ` 、 ` <invoke> ` 和 ` <reply> ` 活动定义了这三个操作。然而,该流程需要定义这些简单的活动彼此之间的关系,以便知道如何以及何时运行这些活动。在 BPEL 中通过使用结构化活动来定义这些关系,这些结构化活动在如何运行它们包含的活动方面定义了一些限制。在这个示例中,您想让这三个操作一个接一个地发生。在 BPEL 中可以通过使用 <sequence> 活动获得这样的顺序,<sequence> 活动首先包含 ` <receive> ` 来消费消息,然后跟着是一个 ` <invoke> ` 来和金融机构交谈,最后以 ` <reply> ` 来向客户发送应答。因此,上面的云状图将包含以这个顺序进行的三个活动的流程,并且可以调用金融机构,如 图 2 所示。
2
3
4
5
6创建服务描述:使用 WSDL
7BPEL 整合对所涉及的服务的 WSDL 描述依赖性很大,这是为了引用正在被交换的消息、正在被调用的操作以及这些操作所属于的 portType。在这个示例中,您将需要金融机构以及这个流程本身的描述。要考虑到金融界使用一组统一的消息来描述贷款信息,并且在 清单 1 的贷款定义中定义了这些消息。
8
9**清单 1:贷款定义 WSDL(loandefinitions.wsdl)**
10
11
12
13 <definitions targetnamespace="http://tempuri.org/services/loandefinitions" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tempuri.org/services/loandefinitions" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
14<message name="creditInformationMessage">
15<part name="firstName" type="xsd:string"></part>
16<part name="name" type="xsd:string"></part>
17<part name="amount" type="xsd:integer"></part>
18</message>
19<message name="loanRequestErrorMessage">
20<part name="errorCode" type="xsd:integer"></part>
21</message>
22</definitions>
23
24
25
26---
27
28假定您知道有一个提供贷款批准服务的金融机构,其描述如下面 清单 2 所示。该金融机构只包含单个操作“approve”,它用这个操作确定一个贷款请求的状态。该操作把有关客户的信息作为输入,然后输出一条包含应答的批准消息。在上面的 loandefinitions WSDL 中定义了输入消息的定义。
29
30**清单 2:贷款批准者 WSDL(loanapprover.wsdl)**
31
32
33
34 <definitions targetnamespace="http://tempuri.org/services/loanapprover" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:loandef="http://tempuri.org/services/loandefinitions" xmlns:tns="http://tempuri.org/services/loanapprover" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
35<import location="http://localhost:8080/bpws-samples/loanapproval/loandefinitions.wsdl" namespace="http://tempuri.org/services/loandefinitions"></import>
36<message name="approvalMessage">
37<part name="accept" type="xsd:string"></part>
38</message>
39<porttype name="loanApprovalPT">
40<operation name="approve">
41<input message="loandef:creditInformationMessage"/>
42<output message="tns:approvalMessage"></output>
43<fault message="loandef:loanRequestErrorMessage" name="loanProcessFault"></fault>
44</operation>
45</porttype>
46<binding ...=""> ... </binding>
47<service name="LoanApprover">....</service>
48</definitions>
49
50
51
52---
53
54流程本身只是将输入消息转发到这个服务中,并将输出消息从这个服务中转发出去。因此,流程将通过引用上面的 portType 来向用户提供同一个描述。还要做的一件事情是为所使用的服务定义 ` serviceLinkType ` 。 ` serviceLinkType ` 最多定义两个角色,这两个角色引用的是由 ` serviceLinkType ` 链接在一起的任意两个服务提供和需要的 portType。对于这个示例来说,这个 ` serviceLinkType ` 将被用于把客户链接到流程,以及将流程链接到贷款批准者。因为流程本身和贷款批者都提供“approver”portType,所以只需要一个角色,并且它们二者都不要求用户支持另一个 portType。您为流程创建下面的 清单 3 中的代码:
55
56**清单 3:贷款批准 WSDL(loan-approval.wsdl)**
57
58
59
60 <definitions targetnamespace="http://loans.org/wsdl/loan-approval" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apns="http://tempuri.org/services/loanapprover" xmlns:lns="http://loans.org/wsdl/loan-approval" xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/06/service-link/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
61<import location="http://localhost:8080/bpws-samples/loanapproval/loanapprover.wsdl" namespace="http://tempuri.org/services/loanapprover"></import>
62<import location="http://localhost:8080/bpws-samples/loanapproval/loandefinitions.wsdl" namespace="http://tempuri.org/services/loandefinitions"></import>
63<slnk:servicelinktype name="loanApprovalLinkType">
64<slnk:role name="approver">
65<porttype name="apns:loanApprovalPT"></porttype>
66</slnk:role>
67</slnk:servicelinktype>
68<service name="loanapprovalServiceBP"></service>
69</definitions>
70
71
72
73---
74
75创建流程
76创建这个流程的所有要求现在都已满足。您以 ` <process> ` 元素开始定义,包括将使流程能够引用需要的 WSDL 信息的名称空间(在这个名称空间( _http://..../loandefinitions_ )中定义了消息定义)、贷款批准者的目标名称空间( _http://.../loanapprover_ )以及流程自己的 WSDL 的目标名称空间( _http://.../loan-approval_ )。流程现在可以用贷款批准者服务作为一个组件。
77
78
79
80 <process name="loanApprovalProcess" targetnamespace="http://acme.com/simpleloanprocessing" xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/" xmlns:apns="http://tempuri.org/services/loanapprover" xmlns:lns="http://loans.org/wsdl/loan-approval" xmlns:loandef="http://tempuri.org/services/loandefinitions">
81
82
83
84---
85
86下一步是声明涉及的各方。定义了已命名的伙伴,每个伙伴都有一个 WSDL ` serviceLinkType ` 表明其特征。对于这个示例来说,伙伴是客户和金融机构。伙伴的 ` myRole/partnerRole ` 属性指定了给定的 ` serviceLinkType ` 的伙伴和流程将如何交互。 ` myRole ` 属性引用的是流程在 ` serviceLinkType ` 中将要扮演的角色,而 ` partnerRole ` 指定了伙伴将要扮演的角色。在下面的伙伴定义中对此进行了阐述。贷款批准流程为客户提供了 ` loanApprovalPT ` 功能,金融机构接着又为流程提供了这个功能。在上面的 图 1 和 2 中可以看到这个关系。
87
88
89
90 <partners>
91<partner myrole="approver" name="customer" servicelinktype="lns:loanApproveLinkType"></partner>
92<partner name="approver" partnerrole="approver" servicelinktype="lns:loanApprovalLinkType"></partner>
93</partners>
94
95
96
97---
98
99定义了伙伴之后,您就基本上为开始添加组成整合的活动做好了准备。我们再回头看一下您想让流程做什么。为了申请贷款,客户向流程发送一条消息,流程询问金融机构是否将接受贷款申请,然后用另一条消息(要么是接受申请的消息,要么是拒绝申请的消息)应答客户。在 BPEL 中您如何做到这一点呢?首先,您需要将传入的消息放在 BPEL 活动可以访问到它的地方。在 BPEL 中,数据被写入到数据容器中并且从数据容器进行访问,这些数据容器可以保存特定的 WSDL 消息类型的实例。
100
101从客户伙伴和 loanApprovalPT 的定义可以很明显地看出,客户将发送一条 ` creditInformationMessage ` 类型的消息,然后得到一条 ` approvalMessage ` 类型的应答。因此,添加了下面的容器列表,这些容器在 图 2 中用蓝色圆柱体表示:
102
103
104
105
106 <containers>
107<container messagetype="loandef:CreditInformationMessage" name="request"></container>
108<container messagetype="apns:approvalMessage" name="approvalInfo"></container>
109</containers>
110
111
112
113---
114
115与进程交互:接收、调用、应答
116流程可能仅包含一个活动,在这个例子中就是 ` <sequence> ` 。现在,您可以将一个 _receive_ 活动添加到 _sequence_ 中,这个 _receive_ 活动可以接收客户的消息并把它保存到适当的容器中。 _receive_ 活动的定义必须包括将向活动发送它的消息的伙伴,以及伙伴将把它作为这条消息的目的地的流程的端口类型和操作。以这一信息为基础,一旦流程获得一条消息,它就搜索一个具有匹配的 partner-portType-operation 三元组的 _receive_ 活动,然后把消息交给这个 _receive_ 活动。为了避免混淆,BPEL4WS 规范规定在同一时间不能有多个带有相同的 partner-portType-operation 三元组的接收活动处于活动状态。随后,活动将把消息放在指定的容器中,然后结束。您开始 _sequence_ 活动,然后将 _receive_ 添加到其中:
117
118
119
120 <sequence>
121<receive container="request" createinstance="yes" name="receive1" operation="approve" partner="customer" porttype="apns:loanApprovalPT">
122</receive>
123
124
125
126---
127
128下一步是询问启用 Web 服务的金融机构是否将接受贷款。这是用一个常规的 Web 服务调用来完成的,该调用是在流程中由 Invoke 活动定义。Invoke 活动在运行时将使用其输入容器(input container)中的消息来调用指定的 Web 服务,并将得到的应答放入到输出容器(output container)中,然后结束。请注意:将在“approver”伙伴上进行调用以执行 ` approve ` 操作。
129
130
131
132 <invoke inputcontainer="request" name="invokeapprover" operation="approve" outputcontainer="approvalInfo" partner="approver" porttype="apns:loanApprovalPT">
133</invoke>
134
135
136
137---
138
139为了使流程对客户的请求作出应答,流程使用一个 Reply 活动。一旦一个应答活动到达,将用流程所拥有的 partner-portType-operation 三元组来找出要把应答发送给谁。因此,为了应答通过 Receive 活动到达的消息,您将需要一个带有相同的三元组的 Reply 活动。在这个例子中,您想要告诉客户金融机构的决定是什么,所以在调用的输出容器中将会发现要被发送的消息: ` approvalInfo ` 。完成应答之后,流程结束。您关闭了 sequence 标记和 process 标记。
140
141
142
143
144 <reply container="approvalInfo" name="reply" operation="approve" partner="customer" porttype="apns:loanApprovalPT">
145</reply>
146</sequence>
147</sequence></process>
148
149
150
151---
152全部组合在一起
153
154
155流程被部署之后就一直在等待,直到有人启动它为止。如果您稍加注意,就能看到 _receive_ 包含了一个名为“createInstance”、被设置为真的属性。这向我们展示了进入流程的一个入口点。 图 3 阐述了贷款批准流程将如何运行。
156
157**图 3. 运行贷款批准流程。**
158
159
160_图注:箭头上的数字表示步骤发生的顺序。黑色信封是包含贷款请求的消息。红色信封是包含对该请求的应答的消息。_
161
162客户机向流程管理器发送了一条带有适当的三元组的消息之后,就会有一个流程实例被创建并开始运行。在给定的示例中,流程将启动 _sequence_ ,这个 _sequence_ 接着将启动 _receive_ 。消息已经到达,所以它将被放入“request”容器中。然后将发生 _invoke_ 。在调用后得到的消息被放入到“approvalInfo”容器中后, _reply_ 将获取这个消息并将其发送给客户,此时流程的实例结束。同一个流程的多个实例可以同时运行。当我解释更复杂的流程时,您将看到如何使用相关性(correlation)将消息路由到同一个流程的正确实例上以及如何从同一个流程的正确实例把消息路由出去。</process></reply></invoke></receive></sequence></sequence></reply></invoke></receive>