在SOA的上下文中设计WebServices必须处理好一系列问题,包括可重用性和灵活性等。然而,一旦最初的设计是让独立的服务一起以集成的方式出现就会导致另一些需求的出现,其中大多数可以被业务过程执行语言(Business Process Execution Language ,BPEL)来处理。
BPEL是由IBM、Microsoft和BEA在2003年共同发明的。但从诞生尹始,BPEL就得到了Web services标准的引导。现在,在OASIS的资助下已经升到了2.0版本,有超过35家公司正在参与规划未来标准的方向,它将成为处理Web services编排的标准。
但如何通过BPEL来进行Web services编排呢?一些其它公司把它称为Web services工作流,意思是已经使用基础的Web services协议栈SOAP/WSDL/UDDI。让我们来谈这个问题之前先假设你被派去迁移一个信用卡应用系统并使用到Web services。
由于自身的特性,我们的信用卡应用程序必须与第三方系统交互,并且在同样的过程中有成百的调用。她们基于同样的Web services,这样可以满足不同系统用户的要求。
BPEL就是用来构建这些Web services决策链或工作流的。但是,在更深入探讨它的具体使用之前,你应该认识到一个重要事情或者说必备条件,即BPEL背后的价值:可重用性。
为了尽可能多的构建基于Web services的业务过程并用BPEL把它们粘合到一起,必须使每个独立的服务都达到精确的平衡。使用粗粒度的Web services比使用BPEL创建不同决策链的整体目标更重要,因为它们可能是完整的业务过程。同样的,细粒度的服务不利于重用和构建BPEL工作流。
这个过程本质上与面向对象的设计很类似,即一个对象需要完成足够的工作来证明它的存在才能不被认为是上层调用对象。你应该用同样的方式考虑Web service。Web service 的可重用性是SOA的一个主要原则,这自不必说,而在使用BPEL的时候这一点体现的更加明显。
作为核心,BPEL用XML语法来表达Web service交互,而像声明变量、流控、事务和错误处理等其它事情你可以让另一种语言来负责编排协作。
因为这样也隐含着Web servcies基本的SOAP/WSDL/UDDI方法,因此BPEL要求其自身环境能够执行这些命令,我们称之为BPEL引擎。
现在,BPEL引擎存在着不同的成熟度和复杂性。当SOA领域的一些厂商用其它核心产品例如ESB来紧密集成他们的BPEL引擎时,其他人则在同样的领域创建单独的产品。
为了牢固掌握BPEL,我们通过最中立和不侵权的方法,即一个叫做ActiveBPEL的开源Java BPEL引擎来看看下面的BPEL示例。
ActiveBPEL是一个单独的BPEL引擎,可以在任何Java/J2EE应用服务器下以Web应用(WAR文件)的形式运转。由于ActiveBPEL属于开源产品,所以它与事实上的Java/J2EE开源Tomcat、Apache's Web services框架Axis以及流行的构建工具Ant紧密的结合在一起。
这些特点使ActiveBPEL成为开发BPEL功能的优秀产品,因为它基于已经被熟知并已获得应用的技术。如果你的机器上还没有这些包,可以下载它们并按照基本安装命令进行安装。我们不会谈论配置问题,因为本文的目的是让你能更快的使用BPEL。
让我们从高层审视ActiveBPEL编排的过程。你的银行系统需要执行一个信用卡应用过程,并根据一系列详细的决策链返回一个简单的"yes" or a "no"。这些决策链需要拿到贷款数额、某个律师的信用历史或支出地点,所有这一切都是通过调用单独的Web services来完成的。
根据市场条件或银行政策,这个业务过程将很可能改变信用卡应用的验证方式。用上层Web services返回结果是相当不灵活的方法,因为它使得每次修改Web services之后都要继续修改业务逻辑。另一方面,让BPEL过程来编排应用逻辑会给我们带来两大好处。
BPEL的XML语法授予我们一种指令级别,通过它可以改变应用的工作流而不必在现场业务逻辑之后再修改。这为我们以后在更复杂的工作流情况下重用某套核心Web services提供了方便。
假设我们已经为我们的核心Web servcies完成了最后的设计。我们有两个Web services,它们组成了一个approver和一个assessor。初始分析显示这两个服务都能很容易地被其他信用演进过程重用。因此,现在来看BPEL的伪代码并看看为创建BPEL-XML语法我们需要做什么。
if (creditapplication < $10,000) {
if (assessor returns "lowrisk")
return "yes"
else
return approver response
}
else
return approver response
首先是检查信用卡应用的数额。如果它的量超过$10,000,我们就把输入的数据转交给approver Web service以获得一个响应。反之,如果数量在$10,000以下,我们就先调用assessor Web service,让它返回一个回答,如果响应是"lowrisk",那么应用程序立即被证实。否则我们就需要进一步调用approver Web service。
这是一个很直接的业务过程,很明显的展示了BPEL的强大,达到了为我们的核心Web services编排不同调用场景的目的。现在,让我们来看看实际的BPEL-XML语法。
<?xml version="1.0" encoding="UTF-8"?>
<process name="loanApprovalProcess" suppressJoinFailure="yes"
targetNamespace="http://acme.com/loanprocessing"
xmlns="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:apns="http://tempuri.org/services/loanapprover"
xmlns:asns="http://tempuri.org/services/loanassessor"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:lns="http://loans.org/wsdl/loan-approval" xmlns:loandef="http://tempuri.org/services/loandefinitions"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<partnerLinks>
<partnerLink myRole="approver" name="customer" partnerLinkType="lns:loanApprovalLinkType"/>
<partnerLink name="approver" partnerLinkType="lns:loanApprovalLinkType" partnerRole="approver"/>
<partnerLink name="assessor" partnerLinkType="lns:riskAssessmentLinkType" partnerRole="assessor"/>
</partnerLinks>
<variables>
<variable messageType="loandef:creditInformationMessage" name="request"/>
<variable messageType="asns:riskAssessmentMessage" name="riskAssessment"/>
<variable messageType="apns:approvalMessage" name="approvalInfo"/>
<variable messageType="loandef:loanRequestErrorMessage" name="error"/>
</variables>
<faultHandlers>
<catch faultName="apns:loanProcessFault" faultVariable="error">
<reply faultName="apns:loanProcessFault" operation="approve" partnerLink="customer"
portType="apns:loanApprovalPT" variable="error"/>
</catch>
</faultHandlers>
<flow>
<links>
<link name="receive-to-approval"/>
<link name="receive-to-assess"/>
<link name="approval-to-reply"/>
<link name="assess-to-setMessage"/>
<link name="assess-to-approval"/>
<link name="setMessage-to-reply"/>
</links>
<receive createInstance="yes" name="receive1" operation="approve" partnerLink="customer"
portType="apns:loanApprovalPT" variable="request">
<source linkName="receive-to-approval" transitionCondition="bpws:getVariableData('request',
'amount')>=10000"/>
<source linkName="receive-to-assess" transitionCondition="bpws:getVariableData('request',
'amount')<10000"/>
</receive>
<invoke inputVariable="request" name="invokeapprover" operation="approve"
outputVariable="approvalInfo" partnerLink="approver"
portType="apns:loanApprovalPT">
<target linkName="receive-to-approval"/>
<target linkName="assess-to-approval"/>
<source linkName="approval-to-reply"/>
</invoke>
<invoke inputVariable="request" name="invokeAssessor" operation="check" outputVariable="riskAssessment"
partnerLink="assessor"
portType="asns:riskAssessmentPT">
<target linkName="receive-to-assess"/>
<source linkName="assess-to-setMessage" transitionCondition=
"bpws:getVariableData('riskAssessment', 'risk')='low'"/>
<source linkName="assess-to-approval" transitionCondition="bpws:getVariableData
('riskAssessment', 'risk')!='low'"/>
</invoke>
<reply name="reply" operation="approve" partnerLink="customer" portType="apns:loanApprovalPT"
variable="approvalInfo">
<target linkName="approval-to-reply"/>
<target linkName="setMessage-to-reply"/>
</reply>
<assign name="assign">
<target linkName="assess-to-setMessage"/>
<source linkName="setMessage-to-reply"/>
<copy>
<from expression="'approved'"/>
<to part="accept" variable="approvalInfo"/>
</copy>
</assign>
</flow>
</process>
注意BPEL的元素有<partnerLink>, <variable>, <faultHandler>, <flow>, >target>等等。继续讲解BPEL语法的含义超出了本问的范围,但如果你跟进的话你就能发现在伪代码中有同样的模式。还要注意到,很多BPEL过程是使用特殊的编辑器创建的,这样可以避免手写XML时的错误。
在看过实际的BPEL过程后,你大概会问这跟实际的Web servcies的UPI或WSDL描述符有什么关系。在ActiveBPEL中,它需要你定义一个过程部署描述符(Process Deployment Descriptor)文件来告诉BPEL引擎你的过程。下面就是这个PDD文件。
<process location="bpel/loanapproval.bpel" name="bpelns:loanApprovalProcess"
xmlns="http://www.active-endpoints.com/schemas/deploy/pdd.xsd"
xmlns:bpelns="http://acme.com/loanprocessing"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing">
<!-- Partner Link for inbound request from customer -->
<partnerLinks>
<partnerLink name="customer">
<myRole allowedRoles="" binding="RPC" service="ApproveLoan"/>
</partnerLink>
<!-- Partner Link for outbound request to delegate approver -->
<partnerLink name="approver">
<partnerRole endpointReference="static">
<wsa:EndpointReference xmlns:approver="http://tempuri.org/services/loanapprover">
<wsa:Address>approver:anyURI</wsa:Address>
<wsa:ServiceName PortName="SOAPPort">approver:LoanApprover</wsa:ServiceName>
</wsa:EndpointReference>
</partnerRole>
</partnerLink>
<!-- Partner Link for outbound request to risk assessment process -->
<partnerLink name="assessor">
<partnerRole endpointReference="static">
<wsa:EndpointReference xmlns:assessor="http://tempuri.org/services/loanassessor">
<wsa:Address>assessor:anyURI</wsa:Address>
<wsa:ServiceName PortName="SOAPPort">assessor:LoanAssessor</wsa:ServiceName>
</wsa:EndpointReference>
</partnerRole>
</partnerLink>
</partnerLinks>
<wsdlReferences>
<wsdl location="project:/wsdl/loandefinitions.wsdl"
namespace="http://tempuri.org/services/loandefinitions"/>
<wsdl location="project:/wsdl/loanapproval.wsdl"
namespace="http://loans.org/wsdl/loan-approval"/>
<wsdl location="project:/wsdl/loanapprover.wsdl"
namespace="http://tempuri.org/services/loanapprover"/>
<wsdl location="project:/wsdl/loanassessor.wsdl"
namespace="http://tempuri.org/services/loanassessor"/>
</wsdlReferences>
</process>
注意这个描述符是如何与刚才的BPEL过程协作的。它声明了<process location="bpel/loanapproval.bpel">来为每个<partnerLink>定义物理地址,并声明了多个WSDL描述符。
重要的是,这个文件与我们的Web services一起绑定到BPEL过程。通过上面的讲解,你应该能意识到Web services WSDL文件组成了任何BPEL过程的主体。
前面的因为WSDL定义了一个Web service的方法、消息和特征。而一个BPEL过程则通过它的引擎使得丰富的使用信息被包含在这些标准Web services文件中,以此来编排需要的业务过程。
本文通过ActiveBPEL让大家了解BPEL。如果你已经看过Loan Approval Application for ActiveBPEL 并知道它的内部工作方式,那么现在就能体会到BPEL对给你的Web services和SOA开发所带来的价值。