?
在SOA的上下文中設(shè)計(jì)WebServices必須處理好一系列問題,包括可重用性和靈活性等。然而,一旦最初的設(shè)計(jì)是讓獨(dú)立的服務(wù)一起以集成的方式出現(xiàn)就會(huì)導(dǎo)致另一些需求的出現(xiàn),其中大多數(shù)可以被業(yè)務(wù)過程執(zhí)行語(yǔ)言(Business Process Execution Language ,BPEL)來處理。
BPEL是由IBM、Microsoft和BEA在2003年共同發(fā)明的。但從誕生尹始,BPEL就得到了Web services標(biāo)準(zhǔn)的引導(dǎo)。現(xiàn)在,在OASIS的資助下已經(jīng)升到了2.0版本,有超過35家公司正在參與規(guī)劃未來標(biāo)準(zhǔn)的方向,它將成為處理Web services編排的標(biāo)準(zhǔn)。
但如何通過BPEL來進(jìn)行Web services編排呢?一些其它公司把它稱為Web services工作流,意思是已經(jīng)使用基礎(chǔ)的Web services協(xié)議棧SOAP/WSDL/UDDI。讓我們來談這個(gè)問題之前先假設(shè)你被派去遷移一個(gè)信用卡應(yīng)用系統(tǒng)并使用到Web services。
由于自身的特性,我們的信用卡應(yīng)用程序必須與第三方系統(tǒng)交互,并且在同樣的過程中有成百的調(diào)用。她們基于同樣的Web services,這樣可以滿足不同系統(tǒng)用戶的要求。
BPEL就是用來構(gòu)建這些Web services決策鏈或工作流的。但是,在更深入探討它的具體使用之前,你應(yīng)該認(rèn)識(shí)到一個(gè)重要事情或者說必備條件,即BPEL背后的價(jià)值:可重用性。
為了盡可能多的構(gòu)建基于Web services的業(yè)務(wù)過程并用BPEL把它們粘合到一起,必須使每個(gè)獨(dú)立的服務(wù)都達(dá)到精確的平衡。使用粗粒度的Web services比使用BPEL創(chuàng)建不同決策鏈的整體目標(biāo)更重要,因?yàn)樗鼈兛赡苁峭暾臉I(yè)務(wù)過程。同樣的,細(xì)粒度的服務(wù)不利于重用和構(gòu)建BPEL工作流。
這個(gè)過程本質(zhì)上與面向?qū)ο蟮脑O(shè)計(jì)很類似,即一個(gè)對(duì)象需要完成足夠的工作來證明它的存在才能不被認(rèn)為是上層調(diào)用對(duì)象。你應(yīng)該用同樣的方式考慮Web service。Web service 的可重用性是SOA的一個(gè)主要原則,這自不必說,而在使用BPEL的時(shí)候這一點(diǎn)體現(xiàn)的更加明顯。
作為核心,BPEL用XML語(yǔ)法來表達(dá)Web service交互,而像聲明變量、流控、事務(wù)和錯(cuò)誤處理等其它事情你可以讓另一種語(yǔ)言來負(fù)責(zé)編排協(xié)作。
因?yàn)檫@樣也隱含著Web servcies基本的SOAP/WSDL/UDDI方法,因此BPEL要求其自身環(huán)境能夠執(zhí)行這些命令,我們稱之為BPEL引擎。
現(xiàn)在,BPEL引擎存在著不同的成熟度和復(fù)雜性。當(dāng)SOA領(lǐng)域的一些廠商用其它核心產(chǎn)品例如ESB來緊密集成他們的BPEL引擎時(shí),其他人則在同樣的領(lǐng)域創(chuàng)建單獨(dú)的產(chǎn)品。
為了牢固掌握BPEL,我們通過最中立和不侵權(quán)的方法,即一個(gè)叫做ActiveBPEL的開源Java BPEL引擎來看看下面的BPEL示例。
ActiveBPEL是一個(gè)單獨(dú)的BPEL引擎,可以在任何Java/J2EE應(yīng)用服務(wù)器下以Web應(yīng)用(WAR文件)的形式運(yùn)轉(zhuǎn)。由于ActiveBPEL屬于開源產(chǎn)品,所以它與事實(shí)上的Java/J2EE開源Tomcat、Apache's Web services框架Axis以及流行的構(gòu)建工具Ant緊密的結(jié)合在一起。
這些特點(diǎn)使ActiveBPEL成為開發(fā)BPEL功能的優(yōu)秀產(chǎn)品,因?yàn)樗谝呀?jīng)被熟知并已獲得應(yīng)用的技術(shù)。如果你的機(jī)器上還沒有這些包,可以下載它們并按照基本安裝命令進(jìn)行安裝。我們不會(huì)談?wù)撆渲脝栴},因?yàn)楸疚牡哪康氖亲屇隳芨斓氖褂肂PEL。
讓我們從高層審視ActiveBPEL編排的過程。你的銀行系統(tǒng)需要執(zhí)行一個(gè)信用卡應(yīng)用過程,并根據(jù)一系列詳細(xì)的決策鏈返回一個(gè)簡(jiǎn)單的"yes" or a "no"。這些決策鏈需要拿到貸款數(shù)額、某個(gè)律師的信用歷史或支出地點(diǎn),所有這一切都是通過調(diào)用單獨(dú)的Web services來完成的。
根據(jù)市場(chǎng)條件或銀行政策,這個(gè)業(yè)務(wù)過程將很可能改變信用卡應(yīng)用的驗(yàn)證方式。用上層Web services返回結(jié)果是相當(dāng)不靈活的方法,因?yàn)樗沟妹看涡薷腤eb services之后都要繼續(xù)修改業(yè)務(wù)邏輯。另一方面,讓BPEL過程來編排應(yīng)用邏輯會(huì)給我們帶來兩大好處。
BPEL的XML語(yǔ)法授予我們一種指令級(jí)別,通過它可以改變應(yīng)用的工作流而不必在現(xiàn)場(chǎng)業(yè)務(wù)邏輯之后再修改。這為我們以后在更復(fù)雜的工作流情況下重用某套核心Web services提供了方便。
假設(shè)我們已經(jīng)為我們的核心Web servcies完成了最后的設(shè)計(jì)。我們有兩個(gè)Web services,它們組成了一個(gè)approver和一個(gè)assessor。初始分析顯示這兩個(gè)服務(wù)都能很容易地被其他信用演進(jìn)過程重用。因此,現(xiàn)在來看BPEL的偽代碼并看看為創(chuàng)建BPEL-XML語(yǔ)法我們需要做什么。
if (creditapplication < $10,000) {
??????? if (assessor returns "lowrisk")
??????????????? return "yes"
??????? else
??????????????? return approver response
}
else
??????? return approver response
首先是檢查信用卡應(yīng)用的數(shù)額。如果它的量超過$10,000,我們就把輸入的數(shù)據(jù)轉(zhuǎn)交給approver Web service以獲得一個(gè)響應(yīng)。反之,如果數(shù)量在$10,000以下,我們就先調(diào)用assessor Web service,讓它返回一個(gè)回答,如果響應(yīng)是"lowrisk",那么應(yīng)用程序立即被證實(shí)。否則我們就需要進(jìn)一步調(diào)用approver Web service。
這是一個(gè)很直接的業(yè)務(wù)過程,很明顯的展示了BPEL的強(qiáng)大,達(dá)到了為我們的核心Web services編排不同調(diào)用場(chǎng)景的目的。現(xiàn)在,讓我們來看看實(shí)際的BPEL-XML語(yǔ)法。
<?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>等等。繼續(xù)講解BPEL語(yǔ)法的含義超出了本問的范圍,但如果你跟進(jìn)的話你就能發(fā)現(xiàn)在偽代碼中有同樣的模式。還要注意到,很多BPEL過程是使用特殊的編輯器創(chuàng)建的,這樣可以避免手寫XML時(shí)的錯(cuò)誤。
在看過實(shí)際的BPEL過程后,你大概會(huì)問這跟實(shí)際的Web servcies的UPI或WSDL描述符有什么關(guān)系。在ActiveBPEL中,它需要你定義一個(gè)過程部署描述符(Process Deployment Descriptor)文件來告訴BPEL引擎你的過程。下面就是這個(gè)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>
注意這個(gè)描述符是如何與剛才的BPEL過程協(xié)作的。它聲明了<process location="bpel/loanapproval.bpel">來為每個(gè)<partnerLink>定義物理地址,并聲明了多個(gè)WSDL描述符。
重要的是,這個(gè)文件與我們的Web services一起綁定到BPEL過程。通過上面的講解,你應(yīng)該能意識(shí)到Web services WSDL文件組成了任何BPEL過程的主體。
前面的因?yàn)閃SDL定義了一個(gè)Web service的方法、消息和特征。而一個(gè)BPEL過程則通過它的引擎使得豐富的使用信息被包含在這些標(biāo)準(zhǔn)Web services文件中,以此來編排需要的業(yè)務(wù)過程。
本文通過ActiveBPEL讓大家了解BPEL。如果你已經(jīng)看過Loan Approval Application for ActiveBPEL 并知道它的內(nèi)部工作方式,那么現(xiàn)在就能體會(huì)到BPEL對(duì)給你的Web services和SOA開發(fā)所帶來的價(jià)值。