BPEL(业务流程执行语言)已经成为了构建SOA(面向服务体系)的重要技术之一,它能够将各种服务轻松、灵活的结合到业务流程中去。BPEL的重要性在于它将一种全新的概念——大型程序设计(体系结构设计)引入了应用开发当中。这个概念允许我们通过定义服务调用的顺序来进行快速的业务流程的开发。因此,应用程序(信息系统)可以更加的灵活并且更好的适应业务流程的变化。
业务流程通常具有动态性。企业不得不进行改善和调整,以迅捷的方式作出反应,优化并且调整业务流程以提高整个公司的响应。业务流程中的每一项变化和改进都必须反映到应用程序中去,以便为它们提供支持。虽然这种需求似乎不难实现,但实际并非如此。更改程序往往是很难的事情,这需要时间。因此,应用程序不能立即对业务流程的改变作出反应——它们需要一定的时间来实现、测试并且部署。
使信息系统更加灵活应变并且更好的与业务流程相配合是SOA的主要承诺。在这篇文章中,我将说明BPEL之所以如此重要的原因,并且为大家示范如何开发基于BEPL的程序。
面向服务的方法:
实现有效的自动化业务流程的SOA方法要求:
l 把程序外在的、可使用的功能作为服务使其标准化。
l 为各种服务之间的通信和管理构造企业总线,包括信息侦听,路由以及转换等。
l 用特殊的语言将程序外在的功能整合到业务流程中。
最新的分布式体系结构——Web Service 实现了第一个需求,对服务和它们之间的通信提供了集中、开放并且并列管理支持的ESB(企业服务总线)实现了第二个需求,第三个需求,将服务整合到业务流程中,由BPEL这种被普遍接受的能够定义和执行业务流程的特殊语言实现了。
用BPEL所描绘的业务流程,是并行服务调用和能够在一个或多个组织中产生结果的相关活动的集合。例如:一项策划商务旅行的业务流程将调用一些服务。在最简单的情况下,业务流程需要我们指定雇员的姓名,目的地,日期和其它一些具体事项。然后程序将调用Web Service 来核对雇员的身份。再根据雇员的身份来选择适当的旅行种类。最后调用一些航空公司(如美国航空公司、德尔塔航空公司等)的Web Service 来检查票价并购买价格最低的机票。
BPEL程序和其它Web Service 一样将功能公开给用户。从用户的角度来看,它和其它的Web Service 是完全一样的。这一点很重要,也很有用,它能让我们将各种服务集成到简单的程序中去,再将简单的程序组合成复杂的程序,如此循环。这也意味着每一个BPEL程序都是用WSDL(Web服务描述语言)来描述的。
核心概念
BPEL是一种基于XML的语言。构建一个BPEL程序分为多步。每一步称为一个行为。BPEL提供了简单的和结构化的行为。表示基本模型的简单行为可以用来构建通用的任务,诸如以下描述的:
n 调用Web Service,使用<invoke>
n 等待请求,使用<receive>
n 使用数据变量,使用<assign>
n 指出错误和异常,使用<throw>等
我们能把这些行为组合成复杂的算法来说明业务流程。BPEL提供了结构化的行为来组合简单的行为。最重要的是:
n 可以顺序(使用<sequence>)定义的一组行为,使这组行为能够按照顺序被调用
n 将一组行为定义为流(使用<flow>),可以使这组行为被并行调用
n 使用switch结构(<switch>)来实现各个分支
n 使用while(<while>)定义循环等
正如我们看到的,BPEL和一般的程序设计语言,如Java,并没有太大的区别。但是,我们也能注意到BPEL不同于Java的地方,那就是它提供了业务流程的特征。BPEL同样也提供了错误处理,事件处理以及相互作用集。它为处理快速复杂的并行流提供了方法。它也提供了相对简单的异步操作和回调等待。
BPEL程序需要BPEL服务器来作为运行的环境。BPEL服务器为我们提供了很好的控制程序运行的机制。通常,BPEL服务器提供对程序实例的运行和完成的控制。它们可以长期的运行,也可以终止程序的状态来收回资源。一些服务器提供了对程序行为的控制,并对它们进行监控。最终,使用BPEL服务器,所有的程序都能够集中部署,简单维护。所有的这些都能使BPEL服务器成为运行和管理程序的首选。
但是,选择BPEL服务器可能有些困难,因为有很多种BPEL的服务器。一些比较流行的BPEL服务器是基于Java EE(SUN公司为J2EE起的新名字)的,包括Oracle BPEL Process Manager,IBM WebSphere Business Integration Server Foundation,BEA WebLogic Integration和AquaLogic。至少还有四种开源的BPEL服务器可供选择:ActiveBPEL Engine,,FiveSight PXE,,bexee,,and Apache Agila。
示例
现在让我们来看一下上面提到过的那个商务旅行具体的BPEL程序例子。让我们建立一个异步程序来使用一个检测雇员身份的同步调用和两个获得飞机票价的异步调用。下面的图片显示了整体的流程结构。
左面我们可以看到客户端调用程序的过程。程序首先调用雇员旅行状态的Web服务。然后再分别同步和异步的调用两个检测票价的Web服务。这意味着程序必须实现一个回调的方法(和一个通信类型)来返回具体确认的飞机票情况。最后,程序向用户提供最合适的飞机票建议。在这个例子中,为了实现方便,我们不提供任何有关错误处理的实现,尽管这个在实际应用中很重要。
现在让我们开始编写BPEL代码。我们首先从程序的声明――根元素开始,这里我们可以定义程序的名字以及命名空间。
<process name="Travel" targetNamespace="http://packtpub.com/bpel/travel/" xmlns="http://schemas.xmlsoap.org/ws/2003/03/business-process/" xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/" xmlns:trv="http://packtpub.com/bpel/travel/" xmlns:emp="http://packtpub.com/service/employee/" xmlns:aln="http://packtpub.com/service/airline/" >
|
接下来,我们需要定义partner links。Partner links定义了程序不同的部分如何在BPEL程序中结合起来。这里包括了所有我们将要调用的Web Service和我们的客户端程序。每一个partner links有两个属性:myRole属性定义了该业务流程自身的角色,partnerRole则定义了与其它partner的角色。在我们的例子中,我们定义四个partner links:
<partnerLinks> <partnerLink name="client" partnerLinkType="trv:travelLT" myRole="travelService" partnerRole="travelServiceCustomer"/> <partnerLink name="employeeTravelStatus" partnerLinkType="emp:employeeLT" partnerRole="employeeTravelStatusService"/> <partnerLink name="AmericanAirlines" partnerLinkType="aln:flightLT" myRole="airlineCustomer" partnerRole="airlineService"/> <partnerLink name="DeltaAirlines" partnerLinkType="aln:flightLT" myRole="airlineCustomer" partnerRole="airlineService"/> </partnerLinks> |
我们需要变量来存储消息,并且重新格式化并发送它们。通常,我们为每一个发送到Web Service和从那里接收的信息使用一个变量。在我们的例子中,我们也需要一些变量。我们需要为每一个变量指定它的类型。我们可以使用WSDL消息类型,XML Schema 简单类型或者XML Schema元素。在例子中,我们将所有的变量定义为WSDL消息类型。
<variables> <!-- input for this process --> <variable name="TravelRequest" messageType="trv:TravelRequestMessage"/> <!-- input for the Employee Travel Status web service --> <variable name="EmployeeTravelStatusRequest" messageType="emp:EmployeeTravelStatusRequestMessage"/> <!-- output from the Employee Travel Status web service --> <variable name="EmployeeTravelStatusResponse" messageType="emp:EmployeeTravelStatusResponseMessage"/> <!-- input for American and Delta web services --> <variable name="FlightDetails" messageType="aln:FlightTicketRequestMessage"/> <!-- output from American Airlines --> <variable name="FlightResponseAA" messageType="aln:TravelResponseMessage"/> <!-- output from Delta Airlines --> <variable name="FlightResponseDA" messageType="aln:TravelResponseMessage"/> <!-- output from BPEL process --> <variable name="TravelResponse" messageType="aln:TravelResponseMessage"/> </variables> |
现在我们可以着手写主程序了。主程序只包括一个最高级的行为。通常<sequence>允许我们定义一些行为并且顺序的执行它们。在顺序执行的过程中,首先我们必须先指定输入给业务流程的消息。我们使用<receive>结构来等待匹配的信息。在我们的例子中,这个消息就是TravelRequest。在<receive>结构中,我们不需要指定特定的信息。更确切地说,我们可以指定其partner link,端口类型,操作名,和可选的、用于保存从运算结果那里接受到信息的变量。我们把消息接受和客户端相链接,并且等待TravelApproval操作在port type为TravelApprovalPT被调用。我们将接收到的信息存储在TravelRequest变量中:
<sequence> <!-- Receive the initial request for business travel from client --> <receive partnerLink="client" portType="trv:TravelApprovalPT" operation="TravelApproval" variable="TravelRequest" createInstance="yes" /> |
接下来,我们需要调用Employee Travel Status Web服务,在此之前,我们需要准备Web服务的输入。我们可以拷贝从客户端发送的消息中雇员的部分来构建我们的消息。现在我们可以调用Employee Travel Status Web服务了。我们使用<invoke>行为可以实现同步运行。我们可以在EmployeeTravelStatusPT port type上使用employeeTravelStatus partner link和调用EmployeeTravelStatus操作。我们已经准备好将消息输入到EmployeeTravelStatusRequest变量中。因为是同步执行的,所以调用将等待答复并且将其存储到EmployeeTravelStatusResponse变量中:
<!-- Prepare the input for the Employee Travel Status Web Service --> <assign> <copy> <from variable="TravelRequest" part="employee"/> <to variable="EmployeeTravelStatusRequest" part="employee"/> </copy> </assign> <!-- Synchronously invoke the Employee Travel Status Web Service --> <invoke partnerLink="employeeTravelStatus" portType="emp:EmployeeTravelStatusPT" operation="EmployeeTravelStatus" inputVariable="EmployeeTravelStatusRequest" outputVariable="EmployeeTravelStatusResponse" /> |
下一步是调用两个航空公司的Web服务。首先我们需要为两个Web服务准备所需的输入信息(两个Web服务的输入是相等的)。然后我们将进行并行的异步运行。为了表示并行性,BPEL提供了<flow>行为。对每一个Web服务的调用将由两部分组成:
1. 用作异步调用的<invoke>行为
2. 用作等待回调的<receive>行为
我们使用<sequence>来组织以上两个行为。这两个调用的不同之处仅仅在于partner link的名字。我们将其中一个起名为AmericaAirlines,另外一个为DeltaAirlines:
<!-- Make a concurrent invocation to AA in DA --> <flow> <sequence> <!-- Async invoke of the AA web service and wait for the callback --> <invoke partnerLink="AmericanAirlines" portType="aln:FlightAvailabilityPT" operation="FlightAvailability" inputVariable="FlightDetails" /> <receive partnerLink="AmericanAirlines" portType="aln:FlightCallbackPT" operation="FlightTicketCallback" variable="FlightResponseAA" /> </sequence> ... |
在这个阶段中,我们会得到两个票价建议。下一步,我们必须从两个中选取一个。因此,我们使用<switch>行为:
<switch> <case condition="bpws:getVariableData('FlightResponseAA','confirmationData', '/confirmationData/aln:Price') <= bpws:getVariableData('FlightResponseDA','confirmationData', '/confirmationData/aln:Price')"> <!-- Select American Airlines --> ... ... </case> <otherwise> <!-- Select Delta Airlines --> ... </otherwise> </switch> |
我们到了BPEL业务流程的最后一步——使用回调将结构返回给客户端:
<!-- Make a callback to the client --> <invoke partnerLink="client" portType="trv:ClientCallbackPT" operation="ClientCallback" inputVariable="TravelResponse" /> </sequence> </process> |
总结
我们可以看到BPEL只是SOA的一个重要的基础。它不同于普通的程序设计语言,如Java,而且它相对容易学习和使用。因为BPEL是专门设计用来定义业务流程的,所以它为业务流程的各种细节提供了很好的支持,如长期连续的事务,改错机制,事件管理,相关性等。BPEL非常适合应用在Java EE的平台上,而且很多的服务器也是这个平台上的。Java开发人员,特别是那些涉及企业应用程序和SOA开发的人员,需要认真的了解BPEL并且准备使用它提供的好处。