级别: 初级 Suhayl Masud (SuhaylA@DifferentThinking.com), 发起人和首席架构师, Different Thinking
2003 年 10 月 01 日 仅仅在两台机器间简单地发送基于 SOAP 的消息不能成为真正意义上的 Web 服务 这是一个相对狭窄的观点,它使更大的蓝图变得模糊。为了电子化地开展业务,您需要鼓励能够您以现实世界中开展业务的方式思考并实践的技术。Web 服务是鼓励您这样思考的非常重要的第一步,在本文中,Suhayl 描述了怎样使用 BPEL4WS 创建可执行的业务流程。
解释 BPEL4WS
可以用Business Process Execution Language for Web Services(BPEL4WS)来“组合”新的和以前定义的
Web 服务,以创建电子商务对话。与 BPEL4WS
一起提供的还有几个规范(WS-Transaction 和 WS-Coordination),这些规范通过添加更多的事务处理和提供跨平台、系统以及执行引擎的处理能力,使
BPEL4WS 的处理能力得到进一步的提高。
BPEL4WS
定义了 Web
服务的编排和相互关联的业务合作伙伴的角色,并且还包括一些高级特征,像对已经执行的动作进行异常处理和补偿处理。BPEL4WS
流程并不局限于只与其他的 BPEL4WS 流程进行交互,它们能与任何平台上新的或以前定义的
Web 服务进行交互,这些平台可以运行也可以不运行在 BPEL4WS
环境中。
BPEL4WS
组件
在这一部分,我将简单地描述 BPEL4WS
的一些组件。要获得更详细的描述,请参阅参考资料中引用的
BPEL4WS 系列。在您阅读该部分时,您可以参照
图1,它可能对您阅读很有帮助。
流程(Process)
在
BPEL4WS 中定义了两种类型的流程:
- 抽象流程(Abstract
Process):它相当于在电子商务对话中定义公共流程(Public
Process)。
- 可执行流程(Executable
Process):运行在组织内部。
这两个流程的主要区别是抽象流程不能被执行,而被用作两个合作伙伴间的一种协议,该协议定义了怎样在一个电子商务对话中进行交互。抽象流程仅仅定义了公共流程,而没有包括在每一个组织内部进行这些公共流程时该真正做些什么。
可执行流程正好相反,它被用来在组织内部执行这些业务流程。可执行流程可以与几个
Web
服务组合在一起(用业务逻辑连接),并访问敏感性数据,以实现组织的业务目标。
合作伙伴
从概念上讲,BPEL4WS
流程是通过与合作伙伴交互来接收、调用和应答活动的。这些合作伙伴可以是组织内部的
Web 服务,也可以是业务合作伙伴的组织内的 Web
服务。流程定义首先描述包括什么部分(Web
服务)以及它们扮演什么角色。合作伙伴描述的最重要的部分是服务链接类型;它是一种机制,用来联系合作伙伴与合作伙伴可以执行的操作(根据
WSDL 定义)。
容器
BPEL4WS
流程用容器来保存状态信息,比如临时变量、传入和传出消息等等。容器是真正的
WSDL
消息类型,而且它可以定义为调用、接收和应答活动中的输入或输出容器。
关联集(Correlation
Set
关联集使消息找到正确的端口类型(port
Type)成为可能,而且更重要的是,找到正确的流程实例。例如,假设组织
A 正在与组织 B 的进行几个订购单流程,当组织 B
接受了某个订购单时,这个信号必须发送到组织
A 正确的订购单流程实例,否则组织 A 可能对哪一个业务请求被接受作出错误的假设。
错误处理器、补偿处理器和范围
当一个错误在
WSDL 端口发生时,它可能会发出一个错误信息。当这个错误信息到达
BPEL4WS 流程后,该流程将使用一个错误处理器来判断怎样从错误中恢复。一个可能的恢复方法是“撤销”流程到目前为之所有做过的事情。因为这种恢复方法非常依赖于错误发生时流程已经进行到什么地方,所以最好的恢复机制是使用有范围限制的活动。
有范围限制的活动为一组活动定义了错误处理器,其中,每个活动都嵌在一个“catch”声明中,这样的话,如果有错误发生的话,这个活动的错误处理器就将处理该错误。错误处理器使用补偿处理器来“撤销”之前已经提交的动作。有范围限制的活动保证了所有定义在范围内的活动要么全部成功完成,要么都“被补偿”。
要使用补偿处理器,合作伙伴需要为每个“做”操作提供一个相应的“撤销”操作。例如,假设有一个操作,允许从一个名为
SafeAirlines 的公司“买票(BuyTicket)”,那么 SafeAirlines
公司必须也提供一个“取消买票(CancelTicket)”的操作。
活动(Activity)
BPEL4WS
流程用活动把 Web 服务编排到更长久运行的电子商务对话中。BPEL4WS
提供了一组可以在流程中使用的基本活动和结构化活动。两类活动的不同之处在于,基本活动不能包含其他的活动,而结构化活动可以包含其他的活动。这些基本活动包括:空(empty)、调用(invoke)、接收(receive)、应答(reply)、分配(assign)、等待(wait)、抛出(throw)和终止(terminate)。结构化活动包括:流(flow)、switch、while、顺序(sequence)、挑选(pick)和范围(scope)。
BPEL4WS
流程使用调用(invoke)、接收(receive)和应答(reply)活动与其他
Web 服务进行交互。BPEL4WS
流程只允许在一个流程中有一个活动。因此,如果您需要管理一组活动,您就可以用结构化活动来包含该组。
表1提供了对 BPEL4WS 流程(BPEL4WS
Process)的组件的总结。
构造可靠的异步流程
要在两个业务合作伙伴间启动一个现实的公共流程,把它创建为一个异步流程是非常必要的,而且这对于灵活的且更长久运行的业务对话来说是非常关键的。我们可以回顾一下,BPEL4WS
流程的层次是在 Web 服务之上的,即把业务流程添加到 Web 服务提供的基本功能中。BPEL4WS
流程引入了有状态的交互,使相互独立的 Web
服务关联起来,并且定义了合作伙伴、活动、数据容器、条件声明和许多其他的功能,以提供灵活的描述性更强的电子商务对话。
在缺省情况下,Web
服务执行同步操作,这意味着服务请求者发送一个请求,然后等待,直到接收到来自服务提供者的响应为止。但是如果网络连接失败了怎么办?需要花费很长的时间来提供这个响应,期间请求者又能做些什么?当公司 A 正在等待响应时,它又怎么知道公司 B 正在处理它的请求,或根本就没有收到它的请求?这种通信方法有两个问题:它是同步的并且是不可靠的。
“业务事务(business
transactions)”是特别典型的需要长时间运行的活动,它可能花费数小时来响应,在这个事务中,双方都需要确认消息已经被对方接收到了。一个使消息比较可靠的简单方法就是,每个团体当收到消息后都发送回一个收到了的确认“信号”。在这种模型下,即使公司
B 花费了 2 个小时来对一个订购单(Purchase Order,PO)请求进行响应,它也会发送一个确认收到订购单请求的信号。采用这种方式后,公司
A 就知道了它的订购单请求已经被公司
B 接收到了,否则公司 A
可能重复发送相同的订购单请求,这样就会造成混乱并且打断业务对话。
在一个异步流程中,仅仅提供服务的公司
B 需要实现 Web 服务,而公司 A
只需要简单地发送一个消息并得到订购单处理响应。要使用
Web 服务启动异步通信,公司 B 需要实现一个 Web 服务,而且公司
A 也需要。这样公司 A 与公司 B 在一个流程中就是等同体了——即双方都能在必要时开始一个对话。
在一个异步流程中,
当公司 A 发送一个订购单请求后,它仅仅收到对方已经收到了该请求的确认信息。随后,公司
B 针对公司 A 调用一个 Web 服务,处理完后公司 B 发送订购单确认作为对公司
A 的请求,然后公司 A 将发送订购单确认已收到作为对公司 B 的响应。在在一系列的文章中,我已经对
BPEL4WS 流程扩展了一些简单的想法,例如允许在业务合作伙伴之间进行有状态的、异步的、可靠的交互。
在 WSDL 中的改进空间
如果我没有提到
WSDL
是一个面世时间非常短的、而且还有挺多不成熟的地方需要在“版本2”中解决的技术成果,那么对创建现实世界的 Web
服务的讨论就是不完整的。不过令人鼓舞的是,W3C 已经有几个组开始着眼于 Web 服务的各种不同方面的研究,其中包括了
Web 服务架构组(Web Services Architecture
Group)、Web 服务描述组(Web services Description Group)以及最近的
Web 服务编排组(WS Choreography
Group)。这些组的成员有从 EDI 、RosettaNet 和 ebXML
来的富有经验的老手,他们非常有希望能保持创新,又能利用来自比较成熟的标准的各种解决方案。
如果我们严格地看待
WSDL ,Web 服务定义鼓励有这样一种关系,它更多的是服务提供者服务匿名的服务请求者。WSDL文件简单地告诉客户端怎样访问这个服务,但没有告诉服务提供者怎样找到服务请求者。而真实的电子商务对话更需要是一个平等的角色,这就意味着双方都可以是服务提供者和请求者。在
WSDL 中,服务提供者不能开始一个对话,它必须等待一个请求并仅仅发送响应。这种设置使得建立一个类似同事与同事间的关系非常的困难,而且也使得构造需要异步操作的对话变得很别扭。
BPEL4WS
是一种最近的技术成果,它需要时间来变得成熟。使用 BPEL4WS
时还有一个问题是,我们需要使用一个面世时间甚至更短的
α 版本的 IBM Business Process Execution Language for Web Services
Java
TM Run Time(BPWS4J)引擎。 BPWS4J 引擎没有实现整个 BPEL4WS
规范,而且已经引起了一些细小的问题。而最重要的一个问题是,在写这篇文章时,BPWS4J 仅仅允许 RPC 调用。当您需要传送一个大一点的文档时,如果以
RPC 调用的方式发送该文档将有问题,这是一个非常令人讨厌的流程。
不过要记住
BPWS4J 仅仅是一个在 α 阶段的研究工具,而不是针对真实的业务部署。
构造电子商务对话
您可能需要各种各样的编程语言及工具来构造和进行电子商务对话。她、我都在使用电子商务对话定义,即
RosettaNet 公司的编排及消息定义。电子商务对话是融合了 WSDL
和
BPEL4WS 编写而成的,并且内部的 Web 服务可以与用 Java
语言编写的应用程序进行交互。
构造公共
Web 服务定义
下面我以为两个公共的和一个私有的
Web 服务构造 WSDL 定义开始,首先是要部署到 ACME 的 Web 服务(
图2中的第二个组件)的
poRequester.wsdl 。这个 Web 服务允许库存管理员(Inventory Manager
)通过为需要的物品下订购单来补充库存。要获得如何使用 WSDL
的更详细信息,请参阅
参考资料。
启动了
WSDL 定义的 BPEL4WS 需要一些额外的信息,而且由于 BPEL4WS 流程使用了
WSDL 文件,它也需要在文件中添加一个服务链接定义。服务链接使
BPEL4WS 流程中的合作伙伴能链接到定义在 Web 服务中的实际“动作”。另外一个不同于一般的
WSDL 的差异是,您不需要描述 WSDL 文件的绑定(bindings)部分,BPEL4WS
会自动地生成必要的绑定(bindings)来与定义了的端口类型(port
types)进行交互。
下面我们来看看 poRequester.wsdl 文件(请看
清单1)。
清单1. poRequester.wsdl
<!-- Author: Suhayl Masud. SuhaylA@DifferentThinking.com http://www.DifferentThinking.com -->
<definitions targetNamespace="http://www.acme.com/services/poRequester"
xmlns:ACME="http://www.acme.com/services/poRequester"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:slt="http://schemas.xmlsoap.org/ws/2002/07/service-link/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="ReplenishRequestType">
<part name="PORequest" type="xsd:string"/>
</message>
<message name="ReceiptAckType">
<part name="receiptAck" type="xsd:string"/>
</message>
<portType name="replenishRequestPort">
<operation name="replenishRequest">
<input message="ACME:ReplenishRequestType"/>
<output message="ACME:ReceiptAckType"/>
</operation>
</portType>
<slt:serviceLinkType name="replenishRequestSLT">
<slt:role name="inventoryService">
<slt:portType name="ACME:replenishRequestPort"/>
</slt:role>
</slt:serviceLinkType>
<!-- Note: This process will be installed on BPEL Engine, it will generate-->
<!-- The bindings information automatically -->
<service name="ACMEPORequesterServiceBP">
</service>
</definitions>
|
这个 Web 服务有两个消息,一个订购单请求消息,另外一个是请求收到的确认消息。在完整的场景中,订购单消息是一个完整的
RosettaNet 订购单,而且确认收到的消息也更详细,并且包括一个它要确认的消息副本。这些特征将我的下一篇文章中讲到,现在我们继续保持这个两个消息都比较简单。
BPEL4WS
流程通过服务链接定义了合作伙伴的能力,服务链接把一个合作伙伴链接到
WSDL 文件中的一个端口类型(Port
Type)和一组操作上,正如您在
清单1中看到的。
在一个典型的 WSDL 定义中,下一部分是定义要发送到 Web
服务的消息格式的绑定信息,以及消息要发送到的地址。不过,既然我们作为
BPEL4WS 流程的一个相关组件来部署这个 Web 服务,BPWS4J 引擎会生成必要的绑定,以便
BPEL4WS 流程能接受该 Web
服务,并监听为任何行为定义的端口。这样您就能在
清单1
中看到,WSDL 文件的绑定(bindings)及服务(services)部分是空的。
JoeLaptops
上的公共 Web 服务 LaptopsIncPlacePO.wsdl (
清单2中的第四个组件)把订购单请求作为输入,而把收到确认(Receipt Acknowledgement)作为响应,这在结构上非常类似于我们前面定义的
Web 服务(请看
清单2
)。
清单2. LaptopsIncPlacePO.wsdl
<!-- Author: Suhayl Masud. SuhaylA@DifferentThinking.com http://www.DifferentThinking.com -->
<definitions targetNamespace="http://www.laptops.com/wsdl/POService"
xmlns:LPTS="http://www.laptops.com/wsdl/POService"
xmlns:LPTS2="http://www.laptops.com/wsdl/SalesService"
xmlns:LPTS3="http://www.laptops.com/wsdl/ShippingService"
xmlns:slt="http://schemas.xmlsoap.org/ws/2002/07/service-link/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="PORequestType">
<part name="PORequest" type="xsd:string"/>
</message>
<message name="ReceiptAckPORequestType">
<part name="receipt" type="xsd:string"/>
</message>
<portType name="placePORequestPort">
<operation name="placePORequest">
<input message="LPTS:PORequestType"/>
<output message="LPTS:ReceiptAckPORequestType"/>
</operation>
</portType>
<slt:serviceLinkType name="PORequestSLT">
<slt:role name="buyer">
<slt:portType name="LPTS:placePORequestPort"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="InternalPORequestSLT">
<slt:role name="internalSales">
<slt:portType name="LPTS2:SalesServicePT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="InternalPOShippingSLT">
<slt:role name="internalShipping">
<slt:portType name="LPTS3:ShippingServicePT"/>
</slt:role>
</slt:serviceLinkType>
<!-- Note: This process will be installed on BPEL Engine, it will generate-->
<!-- The bindings information automatically -->
<service name="placePORequestServiceBP">
</service>
</definitions>
|
请看
清单2
中的公共流程,您可以发现,ACME 发送了一个订购单请求给 JoeLaptops
,然后收到一个确认收到的消息。随后,JoeLaptops 发送给 ACME
一个确认收到订购单的响应。要让 ACME 能收到这个确认收到订购单的消息,我随后将在 ACME 中定义 Web
服务 poAcceptanceReceiver.wsdl(
清单2
的组件六)。它的目的是用来接受一个确认收到订购单的消息,并发送一个响应表示确认收到该订购单的消息已收到。这个是非常直接简单的,它遵守前面两个定义使用的相同逻辑(请看
清单3)。
清单3. poAcceptanceReceiver.wsdl
<!-- Author: Suhayl Masud. SuhayAl@DifferentThinking.com http://www.DifferentThinking.com -->
<definitions targetNamespace="http://www.acme.com/services/poAcceptanceReceiver"
xmlns:ac="http://www.acme.com/services/poAcceptanceReceiver"
xmlns:acl="http://www.acme.com/services/acmeAccountsService"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:slt="http://schemas.xmlsoap.org/ws/2002/07/service-link/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="POAcceptanceType">
<part name="POAcceptance" type="xsd:string"/>
</message>
<message name="ReceiptAckType">
<part name="receiptAck" type="xsd:string"/>
</message>
<portType name="sendPOAcceptancePort">
<operation name="sendPOAcceptance">
<input message="ac:POAcceptanceType"/>
<output message="ac:ReceiptAckType"/>
</operation>
</portType>
<slt:serviceLinkType name="POAcceptanceSLT">
<slt:role name="POService">
<slt:portType name="ac:sendPOAcceptancePort"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="AccountsServiceSLT">
<slt:role name="service">
<slt:portType name="acl:AccountsServicePT"/>
</slt:role>
</slt:serviceLinkType>
<!-- Note: This process will be installed on BPEL Engine, it will generate-->
<!-- The bindings information automatically -->
<service name="ACMEPOReceiverServiceBP">
</service>
</definitions>
|
现在您已经定义了相应的 WSDL 定义,下一步就是创建
BPEL4WS 流程,这个流程将使用 Web 服务定义来引导业务流程。
构造
BPEL4WS 流程
从概念上来说,您仅需要构造两个 BPEL4WS
流程,一个为 ACME ,另一个为 JoeLaptops 。但事实上,由于早期的
α 版本的 BPWS4J 引擎有一些小的 Bug ,您需要把 ACME 的流程分为两个分离的
BPEL4WS 流程。
ACME
的第一个 BPEL4WS 流程是 ACMEPORequester.BPEL
(
图2中的组件一),层次上它位于我们前面创建的 poRequester.wsdl 之上(也可以看
图
3)。这个流程首先从库存管理员那里得到一个订购单请求,然后通过调用位于
JoeLaptops 上的 Web 服务的 placePORequest 操作把请求发送给
JoeLaptops 。这个流程仅仅把调用结果返回给库存管理员,对您的简单场景来说结果就是确认收到的消息。
ACME
的第二个 BPEL4WS 流程是 ACMEPOReceiver.BPEL(
图2的第十三个组件),层次上位于 poAcceptanceReceiver.wsdl 之上。这个流程接受由
JoeLaptops 发送的确认收到订购单的消息,然后给 JoeLaptops
发送回一个确认收到的消息。
JoeLaptops 上有第三个 BPEL4WS
流程(
图2的第五个组件)。它从 ACMEPORequester
流程接受一个订购单请求,并给请求者发送一个接收消息,随后它给请求者(ACMEPOReceiver
流程)发送确认收到订购单的信息并收到一个已接受信息。
During
在 BPEL4WS 的构造及测试阶段,您需要检查两个 BPWS4J 引擎(用两个引擎来模拟
ACME 及 JoeLaptops 的环境)上的日志文件,以便能更深入了解流程是怎么工作的。要继续您的定义,您需要随后定义
ACMEPORequester.BPEL(请看
清单4)。
清单4. ACMEPORequester.bpel
<!-- Author: Suhayl Masud. Suhayl@DifferentThinking.com http://www.DifferentThinking.com -->
<process name="acmePORequesterProcess"
targetNamespace="http://www.acme.com/services/acmePORequesterProcess"
xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
xmlns:acmePOR="http://www.acme.com/services/poRequester"
xmlns:laptopsPO="http://www.laptops.com/wsdl/POService">
<!-- This process has two partners, the internal ACME inventory service that triggers the -->
<!-- process and the POFulfiller (Laptops Inc), to whom the PORequest is sent -->
<partners>
<partner name="inventoryService" serviceLinkType="acmePOR:replenishRequestSLT"/>
<partner name="POFulfiller" serviceLinkType="laptopsPO:PORequestSLT"/>
</partners>
<containers>
<container name="replenishRequestCTR" messageType="acmePOR:ReplenishRequestType"/>
<container name="replenishResponseCTR" messageType="acmePOR:ReceiptAckType"/>
<container name="outputPORequestAckContainer"
messageType="laptopsPO:ReceiptAckPORequestType"/>
</containers>
<sequence name="placePOSequence">
<!-- Receive the inventory manager PO Request and store it in replenishRequest container-->
<receive name="ReplenishRecieve"
partner="inventoryService" portType="acmePOR:replenishRequestPort"
operation="replenishRequest"
container="replenishRequestCTR" createInstance="yes">
</receive>
<!-- Initialize container -->
<assign>
<copy>
<from expression="'initializing'"/>
<to container="replenishResponseCTR" part="receiptAck"/>
</copy>
</assign>
<!-- Invoke placePORequest service at Laptops Inc -->
<invoke name="PlacePOwithSeller"
partner="POFulfiller" portType="laptopsPO:placePORequest" operation="placePORequest"
inputContainer="replenishRequestCTR"
outputContainer="outputPORequestAckContainer"/>
<!-- Copy Receipt Ack from Laptops Inc to be sent to Inventory Manager -->
<assign>
<copy>
<from container="outputPORequestAckContainer" part="receipt"/>
<to container="replenishResponseCTR" part="receiptAck"/>
</copy>
</assign>
<!-- Reply to inventory manager with the receipt ack of the PO Order sent by laptops Inc -->
<reply name="ReplenishResponse"
partner="inventoryService" portType="acmePOR:replenishRequestPort"
operation="replenishRequest"
container="replenishResponseCTR">
</reply>
</sequence>
</process>
|
在这个流程中有两个合作伙伴。第一个合作伙伴是定义在下面的库存服务的库存管理员。库存管理员通过定义在“replenishRequestSLT”服务链接中的操作来与流程进行交互。第二个合作伙伴担当流程中的订单执行处理角色,这个合作伙伴定义描述了这个流程能通过定义在
JoeLaptops 上的 WSDL 文件中的“PORequestSLT”服务链接与 JoeLaptops 进行交互:
<partners>
<partner name="inventoryService" serviceLinkType="acmePOR:replenishRequestSLT"/>
<partner name="POFulfiller" serviceLinkType="laptopsPO:PORequestSLT"/>
</partners>
|
下一步,您需要创建容器来保存流程收到及要发送的数据。在这个流程中有四个容器,在前两个中,一个用来从库存管理员那里接受数据,另一个用来保存响应数据。后两个容器用来与
JoeLaptops
进行交互,它们作为调用请求的一部分,随包含要发送到
JoeLaptop 的消息的输入容器及包含从 JoeLaptops
收到的消息的输出容器一起发送:
<containers>
<container name="replenishRequest" messageType="acmePOR:ReplenishRequestType"/>
<container name="replenishResponse" messageType="acmePOR:ReceiptAckType"/>
<container name="inputPORequestContainer" messageType="laptopsPO:PORequestType"/>
<container name="outputPORequestAckContainer"
messageType="laptopsPO:ReceiptAckPORequestType"/>
</containers>
|
定义完流程的合作伙伴及容器后,您现在可以定义流程的活动了。您将使用一个简单的顺序活动来协调该流程,它需要执行以下任务:
开始顺序
- 从库存管理员那里接受一个订购单请求。
- 从请求容器中分配数据到要发送给
JoeLaptops 的容器中。
- 调用与 JoeLaptops
“下订购单”请求。
- 从 JoeLaptops
分配信息到正要发送给库存管理员的容器中。
- JoeLaptops
对库存管理员进行响应。
结束顺序
下面是顺序的定义:
<sequence name="placePOSequence">
<receive name="ReplenishRecieve"
partner="inventoryService" portType="acmePOR:replenishRequestPort"
operation="replenishRequest"
container="replenishRequest" createInstance="yes">
</receive>
|
接收活动(receive
activity)定义了能与流程进行交互的合作伙伴的类型,以及它们能使用的操作。
传入的请求消息保存在 replenishRequest
容器中。下一个感兴趣的活动就是调用活动(invoke activity):
<invoke name="PlacePOwithSeller"
partner="POFulfiller" portType="laptopsPO:placePORequest" operation="placePORequest"
inputContainer="inputPORequestContainer" outputContainer="outputPORequestAckContainer">
</invoke>
|
这个调用活动需要接收者的两个动作,在本例中接收者就是 JoeLaptops
。在上面的定义中,调用活动与定义在 LaptopsIncPlacePO.wsdl
文件中的 Web 服务 JoeLaptops 的 placePORequest
操作进行交互。调用行为的输入容器被认为是操作的输入消息,而输出容器将接收来自 placePORequest
操作的输出消息。上面的定义将可以使 acmePORequesterProcess
给 JoeLaptops 下一个订购单请求,并且在输出容器中接受确认收到的消息。
下面我们将定义一个分配活动(assign activity),将从 JoeLaptops
返回的信息复制到容器中,该容器将用来发送信息给库存管理员:
<assign name="copyreceivedreceipt">
<copy>
<from container="outputPORequestAckContainer" part="receipt"/>
<to container="replenishResponse" part="receiptAck"/>
</copy>
</assign>
|
在这个简单的场景中,发送回库存管理员的信息仅仅是一个确认收到的消息。在完整的场景中,库存管理员会收到一个确认收到订购单的消息。
现在为了封装
acmePORequesterProcess,我们将定义响应行为(reply activity),它是对库存管理员在流程开始时发送的请求进行响应。下面是一个可能的响应行为:
<reply name="ReplenishResponse"
partner="inventoryService" portType="acmePOR:replenishRequestPort"
operation="replenishRequest"
container="replenishResponse">
</reply>
</sequence>
</process>
|
ACME 的第二个 BPEL4WS 流程是 ACMEPOReceiver.BPEL
(请看
清单5),它包括以下顺序活动:
开始顺序
- 接收来自
JoeLaptops 的订购单确认收到。
- 分配一个确认收到消息给响应行为发送的容器中。
- 用收到确认应答 JoeLaptops。
结束顺序
清单5. ACMEPOReceiver.bpel
<!-- Author: Suhayl Masud. SuhaylA@DifferentThinking.com http://www.DifferentThinking.com -->
<process name="acmePOReceiverProcess"
targetNamespace="http://www.acme.com/services/acmePOReceiverProcess"
xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
xmlns:acmePOA="http://www.acme.com/services/poAcceptanceReceiver"
xmlns:acmeAcct="http://www.acme.com/service/accountsService"
xmlns:laptopsPO="http://www.laptops.com/services/LaptopsIncPlacePO">
<!-- The process has two partners. JoeLaptops, which is the acceptance notifier -->
<!-- and sends the process the PO confirmation, and the second partner is an internal -->
<!-- Accounts service that is informed of the latest purchase that needs to be paid -->
<partners>
<partner name="POAcceptanceNotifier" serviceLinkType="acmePOA:POAcceptanceSLT"/>
<partner name="Accounting" serviceLinkType="acmeAcct:AccountsServiceSLT"/>
</partners>
<containers>
<container name="POAcceptanceContainer" messageType="acmePOA:POAcceptanceType"/>
<container name="POAcceptanceReceipt" messageType="acmePOA:ReceiptAckType"/>
<container name="PurchaseReport" messageType="acmeAcct:reportPurchaseMsg"/>
</containers>
<sequence name="receivePOSequence">
<!-- The process receives the PO confirmation from JoeLaptops -->
<receive name="ReceivePOAcceptance"
partner="POAcceptanceNotifier" portType="acmePOA:sendPOAcceptancePort"
operation="sendPOAcceptance"
container="POAcceptanceContainer" createInstance="yes">
</receive>
<!-- Copy acknowledgement of the PO Confirmation to be sent to JoeLaptops -->
<assign>
<copy>
<from expression="'Acme corp has received your PO Acceptance. Thank you'"/>
<to container="POAcceptanceReceipt" part="receiptAck"/>
</copy>
</assign>
<!-- Send JoeLaptops Receipt Acknowledging the PO Confirmation -->
<reply name="POAcceptanceReceiptAck"
partner="POAcceptanceNotifier" portType="acmePOA:sendPOAcceptancePort"
operation="sendPOAcceptance"
container="POAcceptanceReceipt">
</reply>
<!-- Copy the POConfirmation to a purchase report container -->
<assign>
<copy>
<from container="POAcceptanceContainer" part="POAcceptance"/>
<to container="PurchaseReport" part="purchaseReport"/>
</copy>
</assign>
<!-- Send the accounting service the purchase report -->
<invoke name="invoke" partner="Accounting" portType="acmeAcct:AccountsServicePT"
operation="reportPurchase" inputContainer="PurchaseReport"
outputContainer="POAcceptanceReceipt"/>
</sequence>
</process>
|
最后,这里是 LaptopPlacePOProcess 做了什么(请看
图2的组件五),以及它是怎样定义的:
开始顺序
- 接收来自
ACMEPORequester 流程的订购单请求(PO Request)。
- 在应答活动用到的容器中分配一个接收消息。
- 应答
ACMEPORequester 流程。
- 在调用活动用到的输入容器中分配一个收到消息。
- 调用
ACMEPOReceiver 流程并发送订购单接受(PO Acceptance)的消息。
结束顺序
这个简单的场景中的接受消息是一个简单的字符串。在完整的场景中,它将会是一个订购单接受消息。(请看
清单 6)。
清单6. ACMEPOReceiver.bpel
<!-- Author: Suhayl Masud. SuhaylA@DifferentThinking.com http://www.DifferentThinking.com -->
<process xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
name="laptopsPlacePOProcess"
targetNamespace="http://www.laptops.com/services/POService"
xmlns:tns="http://www.laptops.com/wsdl/POService"
xmlns:tns2="http://localhost:8080/laptops/laptopsSalesService.wsdl"
xmlns:tns3="http://localhost:8080/laptops/laptopsShippingService.wsdl"
xmlns:acme="http://localhost:8080/acme/ACMEPOAcceptance.wsdl">
<!-- Define Partners -->
<partners>
<partner name="buyer" serviceLinkType="tns:PORequestSLT"/>
<partner name="internalSales" serviceLinkType="tns:InternalPORequestSLT"/>
<partner name="internalShipping" serviceLinkType="tns:InternalPOShippingSLT"/>
<partner name="acceptanceReceiver" serviceLinkType="acme:PORequestSLT"/>
</partners>
<!-- Define Containers used when interacting with partners -->
<containers>
<container name="PORequestCtr" messageType="tns:PORequestType"/>
<container name="internalPORequest" messageType="tns2:fillPORequest"/>
<container name="internalPOAcceptance" messageType="tns2:poConfirmation"/>
<container name="internalShippingOrder" messageType="tns3:shipPurchaseOrder"/>
<container name="internalSOResp" messageType="tns3:shipPOResp"/>
<container name="POAcceptance" messageType="acme:POAcceptanceType"/>
<container name="buyerReceipt" messageType="acme:ReceiptAckType"/>
<container name="LaptopsReceiptAck" messageType="tns:ReceiptAckPORequestType"/>
</containers>
<sequence name="placePOSequence">
<!-- Receive PORequest from ACME -->
<receive name="RecievePORequest"
partner="buyer" portType="tns:placePORequestPort" operation="placePORequest"
container="PORequestCtr" createInstance="yes">
</receive>
<!-- initialize Containers -->
<assign>
<copy>
<from expression="'initializing'"/>
<to container="internalPORequest" part="poRequest"/>
</copy>
</assign>
<assign>
<copy>
<from expression="'Receipt Acknowledgement This message is to acknowledge
that Laptops Inc have received your PO Request. We are working on the
response which you will receive shortly'"/>
<to container="LaptopsReceiptAck" part="receipt"/>
</copy>
</assign>
<!-- initialize internalShippingOrder -->
<assign>
<copy>
<from expression="'initializing'"/>
<to container="internalShippingOrder" part="shipPOAcceptance"/>
</copy>
</assign>
<assign>
<copy>
<from expression="'initializing'"/>
<to container="internalShippingOrder" part="poShippingOrder"/>
</copy>
</assign>
<!-- initialize internalSOResponse -->
<assign>
<copy>
<from expression="'initializing'"/>
<to container="internalSOResp" part="shipResponse"/>
</copy>
</assign>
<!-- Send ACME Receipt Acknowledgment of PORequest -->
<reply name="ReceiptAckActivity"
partner="buyer" portType="tns:placePORequestPort" operation="placePORequest"
container="LaptopsReceiptAck">
</reply>
<!-- copy PORequest to container used for internal PORequest -->
<assign>
<copy>
<from container="PORequestCtr" part="PORequest"/>
<to container="internalPORequest" part="poRequest"/>
</copy>
</assign>
<!-- Ask Internal Sales department to process PORequest -->
<invoke name="invoke" partner="internalSales" portType="tns2:SalesServicePT"
operation="fillPO" inputContainer="internalPORequest"
outputContainer="internalPOAcceptance"/>
<!-- Copy the response from Internal Sales to send to ACME -->
<assign>
<copy>
<from container="internalPOAcceptance" part="poConfirmation"/>
<to container="POAcceptance" part="POAcceptance"/>
</copy>
</assign>
<!-- Send ACME substantive message, letting ACME know if the PO was accepted -->
<invoke name="SendBuyerPOAcceptance"
partner="acceptanceReceiver" portType="acme:sendPOAcceptancePort"
operation="sendPOAcceptance"
inputContainer="POAcceptance" outputContainer="buyerReceipt">
</invoke>
<!-- Copy ACME's receipt message of the POConfirmation to container for
internal shipping service -->
<assign>
<copy>
<from container="buyerReceipt" part="receiptAck"/>
<to container="internalShippingOrder" part="shipPOAcceptance"/>
</copy>
</assign>
<!-- Copy POConfirmation to container for internal shipping service -->
<assign>
<copy>
<from container="internalPOAcceptance" part="poConfirmation"/>
<to container="internalShippingOrder" part="poShippingOrder"/>
</copy>
</assign>
<!-- Send POConfirmation and ACME's receipt acknowledging the POConfirmation to
the shipping service -->
<invoke name="invokeShipping" partner="internalShipping" portType="tns3:ShippingServicePT"
operation="shipPO" inputContainer="internalShippingOrder" outputContainer="internalSOResp"/>
</sequence>
</process>
|
我们还是在这里吗?差不多吧!要使 BPEL4WS
流程与内部应用程序及 Web 服务进行交互,您必须构造
图2描述的其余6个组件。您也需要开始交换基于 RosettaNet
的实际订购单数据。在下一篇文章中,我将把这里创建的简单场景扩展为一个健壮的电子商务对话。
参考资料
关于作者
对本文的评价
|