原文引自:http://www.oracle.com/technology/global/cn/pub/articles/matjaz_bpel1.html
??????????????????????????????????????????????????????????????????????????? BPEL 實例教程
開發人員:J2EE 和 Web 服務BPEL 實例教程
作者:Matjaz Juric
了解如何創建一個將一系列虛擬的、與旅行相關的 web 服務結合起來的示例業務流程,然后將其部署到 Oracle BPEL Process Manager 運行時環境。
面向 Web 服務的業務流程執行語言(BPEL 或 BPEL4WS)是一種使用 Web 服務定義和執行業務流程的語言。BPEL 使您可以通過組合、編排和協調 Web 服務自上而下地實現面向服務的體系結構 (SOA)。BPEL 提供了一種相對簡單易懂的方法,可將多個 Web 服務組合到一個新的復合服務(稱作業務流程)中。
本文將介紹如何創建一個將一系列虛擬的、與旅行相關的 web 服務結合起來的示例業務流程,然后將其部署到 Oracle BPEL Process Manager 運行時環境。
BPEL 背景知識
首先,介紹一些背景知識。BPEL 基于 XML 和 Web 服務構建;它使用一種基于 Web 的語言,該語言支持 web 服務技術系列,包括 SOAP、WSDL、UDDI、Web 服務可靠性消息、Web 服務尋址、Web 服務協調以及 Web 服務事務。
BPEL 代表了兩種早期工作流語言 - Web 服務流語言 (WSFL) 和 XLANG 的交匯。WSFL 由 IBM 基于有向圖概念設計。XLANG 是一種由 Microsoft 設計的塊結構化語言。BPEL 組合了這兩種方法,并提供了豐富的詞匯來描述業務流程。
BPEL 的第一個版本誕生于 2002 年 8 月。此后,隨著許多主要供應商(包括 Oracle)的紛紛加入了,催生了多項修改和改進,并于 2003 年 3 月推出了 1.1 版。2003 年 4 月,BPEL 提交結構化信息標準促進組織 (OASIS) 以實現標準化,并組建了 Web 服務業務流程執行語言技術委員會 (WSBPEL TC)。該努力使 BPEL 在業界獲得更廣范圍的認可。
在企業內部,BPEL 用于標準化企業應用程序集成以及將此集成擴展到先前孤立的系統。在企業之間,BPEL 使與業務合作伙伴的集成變得更容易、更高效。BPEL 激發企業進一步定義它們的業務流程,從而導致業務流程的優化、重新設計以及選擇最合適的流程,進而實現了組織的進一步優化。BPEL 中描述的業務流程定義并不影響現有系統,因此對升級產生了促進作用。在已經或將要通過 Web 服務公開功能的環境中,BPEL 是一項重要的技術。隨著 Web 服務的不斷普及,BPEL 的重要性也隨之提高。
編制與編排
Web 服務通常公開某些應用程序或信息系統的操作。因此,組合多個 Web 服務實際上涉及基礎應用程序及其功能的集成。
可以用兩種方式組合 Web 服務:
在編制(通常用于專用業務流程)中,一個中央流程(可以是另一個 Web 服務)控制相關的 Web 服務并協調對操作所涉及 Web 服務的不同操作的執行。相關的 Web 服務并不“知道”(也無需知道)它們參與了組合流程并在參與更高級別的業務流程。只有編制的中央協調員知道此目標,因此編制主要集中于操作的顯式定義以及 Web 服務的調用順序。(見圖 1。)
 |
圖 1:通過編制組合 Web 服務 |
而編排并不依賴某個中央協調員。相反,編排所涉及的每個 Web 服務完全知道執行其操作的時間以及交互對象。編排是一種強調在公共業務流程中交換消息的協作方式。編排的所有參與者都需要知道業務流程、要執行的操作、要交換的消息以及消息交換的時間。(見圖 2。)
 |
圖 2:通過編排組合 Web 服務 |
從組合 Web 服務以執行業務流程的角度而言,編制是一個更靈活的范例,它相對于編排而言具有以下優點:
- 元件流程的協調由某個已知的協調員集中管理
- 可以組合 Web 服務而不必使它們知道它們正在參與更大的業務流程
- 可以準備其他方案以防發生故障。
BPEL 支持兩種不同的業務流程描述方法(支持編制和編排):
- 可執行流程允許指定業務流程的準確細節。它們遵循編制范例,并可由編制引擎執行。
- 抽象業務協議允許只指定雙方之間的公共消息交換。它們不包含流程的內部細節并且無法執行。它們遵循編排范例。
現在,我們來逐步演示如何創建可執行的 BPEL 業務流程;可以下載它的代碼并將其部署到 Oracle BPEL Process Manager。我們將假設已經按照安裝指導成功安裝了 Oracle BPEL Process Manager,并假設它使用缺省端口 9700。如果在安裝過程中選擇了其他端口,則必須相應地修改示例。
構建業務流程
BPEL 流程指定參與的 Web 服務的確切調用順序 - 順序地或并行地。使用 BPEL,您可以表述條件行為。例如,某個 Web 服務的調用可以取決于上次調用的值。還可以構造循環、聲明變量、復制和賦予值、定義故障處理程序等。通過組合所有這些構造,您可以以算法的形式定義復雜業務流程。實際上,由于業務流程本質上屬于活動圖,因此使用統一建模語言 (UML) 活動圖表示它們可能很有用。
通常情況下,BPEL 業務流程接收請求。為了滿足請求,該流程調用相關的 Web 服務,然后響應原始調用方。由于 BPEL 流程與其他 Web 服務通信,因此它在很大程度上依賴于復合型 Web 服務調用的 Web 服務 的 WSDL 描述。
我們來看一個示例。一個 BPEL 流程由多個步驟組成,每個步驟稱作“活動”。BPEL 支持基元活動和結構活動。基元活動表示基本構造,用于如下所示的常見任務:
- 使用 <invoke> 調用其他 Web 服務
- 使用 <receive>(接收請求)等待客戶端通過發送消息調用業務流程
- 使用 <reply> 生成同步操作的響應
- 使用 <assign> 操作數據變量
- 使用 <throw> 指示故障和異常
- 使用 <wait> 等待一段時間
- 使用 <terminate> 終止整個流程。
然后,我們可以組合這些基元活動以及其他基元活動,以定義準確指定業務流程步驟的復雜算法。為組合基元活動,BPEL 支持幾個結構活動。其中最重要的是:- 順序 (<sequence>),它允許定義一組將按順序調用的活動。
- 流 (<flow>),用于定義一組將并行調用的活動
- Case-switch 構造 (<switch>),用于實現分支
- While (<while>),用于定義循環
- 使用 <pick> 能夠選擇多個替換路徑之一。
每個 BPEL 業務還將使用 <partnerLink> 定義合作伙伴鏈接,使用 <variable> 聲明變量。
為了理解 BPEL 是如何描述業務流程的,我們將定義雇員出差安排的簡化業務流程:客戶端調用此業務流程,指定雇員姓名、目的地、出發日期以及返回日期。此 BPEL 業務流程首先檢查雇員出差狀態。我們將假設存在一個可用于進行此類檢查的 Web 服務。然后,此 BPEL 流程將檢查以下兩家航空公司的機票價格:美國航空公司和達美航空公司。我們將再次假設這兩家航空公司均提供了可用于進行此類檢查的 Web 服務。最后,此 BPEL 流程將選擇較低的價格并將出差計劃返回給客戶端。
然后,我們將構建一個異步 BPEL 流程。我們將假設用于檢查雇員出差狀態的 Web 服務是同步的。由于可以立即獲取此數據并將其返回給調用方,因此這是一個合理的方法。為了獲取機票價格,我們使用異步調用。由于確認飛機航班時刻表可能需要稍長的時間,因此這也是一個合理的方法。為簡化示例,我們假設以上兩家航空公司均提供了 Web 服務,且這兩個 Web 服務完全相同(即提供相同的端口類型和操作)。
在實際情形下,您通常無法選擇 Web 服務,而是必須使用您的合作伙伴提供的服務。如果您有幸能夠同時設計 Web 服務和 BPEL 流程,則應考慮用哪個接口更好。通常,您將對持續時間較長的操作使用異步服務,而對在相對較短的時間內返回結果的操作使用同步服務。如果使用異步 Web 服務,則 BPEL 流程通常也是異步的。
當您用 BPEL 定義業務流程時,您實際上定義了一個由現有服務組成的新 Web 服務。該新 BPEL 復合 Web 服務的接口使用一組端口類型來提供類似任何其他 Web 服務的操作。要調用用 BPEL 描述的業務流程,則必須調用生成的復合 Web 服務。圖 3 是我們流程的示意圖。
 |
圖 3:出差安排示例 BPEL 流程 |
在開發此示例 BPEL 流程的過程中,您將經歷下列步驟:
- 熟悉相關的 Web 服務
- 為此 BPEL 流程定義 WSDL
- 定義合作伙伴鏈接類型
- 開發此 BPEL 流程:
第 1 步:列出相關 Web 服務的清單
在您開始編寫 BPEL 流程定義之前,必須先熟悉從業務流程中調用的 Web 服務。這些服務稱作合作伙伴 Web 服務。本示例使用雇員出差狀態 Web 服務以及美國航空公司和達美航空公司 Web 服務(這兩個 Web 服務具有相同的 WSDL 描述)。(同樣,本示例中使用的 Web 服務是虛構的。)
雇員出差狀態 Web 服務雇員出差狀態 Web 服務提供 EmployeeTravelStatusPT 端口類型,通過它可以使用 EmployeeTravelStatus 操作檢查雇員出差狀態。此操作將返回雇員可以使用的乘機標準(可能為經濟艙、商務艙或頭等艙)。(見圖 4。)
 |
圖 4:雇員出差狀態 Web 服務 |
航空公司 Web 服務航空公司 Web 服務是異步的;因此它指定了兩個端口類型:第一個端口類型 FlightAvailabilityPT 用于使用 FlightAvailability 操作檢查航班可用性。為返回結果,該 Web 服務指定了第二個端口類型 FlightCallbackPT。此端口類型指定 FlightTicketCallback 操作。
盡管航空公司 Web 服務定義了兩個端口類型,但它只實現 FlightAvailabilityPT。FlightCallbackPT 則由作為 Web 服務客戶端的 BPEL 流程實現。圖 5 是此 Web 服務體系結構的示意圖:
 |
圖 5:航空公司 Web 服務 |
第 2 步:為 BPEL 流程定義 WSDL
接下來,我們必須將此業務出差 BPEL 公開為 Web 服務。因此,第二步是為它定義 WSDL。此流程將必須從它的客戶端接收消息并返回結果。它必須公開 TravelApprovalPT 端口類型,后者將指定一個輸入消息。它還必須聲明 ClientCallbackPT 端口類型(用于使用回調將結果異步返回給客戶端)。圖 6 說明了此流程。
 |
圖 6:此 BPEL 流程的 WSDL |
第 3 步:定義合作伙伴鏈接類型
第三步是定義合作伙伴鏈接類型。合作伙伴鏈接類型表示 BPEL 流程與相關方(包括 BPEL 流程調用的 Web 服務以及調用 BPEL 流程的客戶端)之間的交互。
本示例包含三個不同的合作伙伴:客戶端、雇員出差狀態服務和航空公司服務。理想情況下,每個 Web 服務都應在 WSDL 中定義相應的合作伙伴鏈接類型。(實際情形可能不是這樣的。)然后,我們可以使用 WSDL 包裝合作伙伴 Web 服務(導入 Web 服務的 WSDL 并定義合作伙伴鏈接類型)。或者,我們可以在 BPEL 流程的 WSDL 中定義所有合作伙伴鏈接。但由于此方法違反了封裝原則,因此不建議使用。
對于本示例,我們定義了三個合作伙伴鏈接類型(每個類型位于 Web 服務的相應 WSDL 中):
- travelLT:用于描述此 BPEL 流程客戶端與此 BPEL 流程本身之間的交互。此交互是異步交互。此合作伙伴鏈接類型在此 BPEL 流程的 WSDL 中定義。
- employeeLT:用于描述此 BPEL 流程與雇員出差狀態 Web 服務之間的交互。此交互是同步交互。此合作伙伴鏈接類型在雇員 Web 服務的 WSDL 中定義。
- flightLT:描述此 BPEL 流程與航空公司 Web 服務之間的交互。此交互是異步交互,且航空公司 Web 服務對此 BPEL 流程調用一個回調。此合作伙伴鏈接類型在航空公司 Web 服務的 WSDL 中定義。
每個合作伙伴鏈接可以擁有一個或兩個角色,我們必須為每個角色指定它使用的 portType。對于同步操作,由于操作只是單向調用,因此每個合作伙伴鏈接類型僅有一個角色。例如,此 BPEL 流程對雇員出差狀態 Web 服務調用 EmployeeTravelStatus 操作。由于它是同步操作,因此此 BPEL 流程等待完成并僅在完成操作后取得響應。
對于異步回調操作,我們必須指定兩個角色。第一個角色描述客戶端操作調用。第二個角色描述回調操作調用。在本示例中,BPEL 流程與航空公司 Web 服務之間存在一個異步關系。
正如我們已經指出的,我們需要三個合作伙伴鏈接類型:兩個鏈接類型指定兩個角色(因為它們是異步的),一個鏈接類型指定一個角色(因為它是同步的)。
合作伙伴鏈接類型在特殊命名空間http://schemas.xmlsoap.org/ws/2003/05/partner-link/ 的 WSDL 定義。首先,我們在客戶端使用的 BPEL 流程 WSDL 中定義 travelLT 鏈接類型以調用此 BPEL 流程。所需的第一個角色是出差服務(即,我們的 BPEL 流程)的角色。客戶端使用 TravelApprovalPT 端口類型與此 BPEL 服務通信。第二個角色 travelServiceCustomer 描述了此 BPEL 流程將在 ClientCallbackPT 端口類型中對其執行回調的客戶端的特征:
<plnk:partnerLinkType name="travelLT">
<plnk:role name="travelService">
<plnk:portType name="tns:TravelApprovalPT" />
</plnk:role>
<plnk:role name="travelServiceCustomer">
<plnk:portType name="tns:ClientCallbackPT" />
</plnk:role>
</plnk:partnerLinkType>
第二個鏈接類型是 employeeLT。它用于描述此 BPEL 流程與雇員出差狀態 Web 服務之間的通信,并在此雇員 Web 服務的 WSDL 中定義。此交互是同步交互,因此我們需要一個名為 employeeTravelStatusService 的角色。此 BPEL 流程使用雇員 Web 服務上的 EmployeeTravelStatusPT:
<plnk:partnerLinkType name="employeeLT">
<plnk:role name="employeeTravelStatusService">
<plnk:portType name="tns:EmployeeTravelStatusPT" />
</plnk:role>
</plnk:partnerLinkType>
最后一個合作伙伴鏈接類型 flightLT 用于描述此 BPEL 流程與航空公司 Web 服務之間的通信。此通信是異步通信。此 BPEL 流程對航空公司 Web 服務調用一個異步操作。此 Web 服務在完成請求后對此 BPEL 流程調用一個回調。因此,我們需要兩個角色。第一個角色描述航空公司 Web 服務對于此 BPEL 流程服務的角色,即航空公司服務 (airlineService)。此 BPEL 流程使用 FlightAvailabilityPT 端口類型進行異步調用。第二個角色描述了此 BPEL 流程對于航空公司 Web 服務的角色。對于航空公司 Web 服務而言,此 BPEL 流程是一個航空公司客戶,因此角色名稱為 airlineCustomer。航空公司 Web 服務使用 FlightCallbackPT 端口類型進行回調。此合作伙伴鏈接類型在航空公司 Web 服務的 WSDL 中定義:
<plnk:partnerLinkType name="flightLT">
<plnk:role name="airlineService">
<plnk:portType name="tns:FlightAvailabilityPT" />
</plnk:role>
<plnk:role name="airlineCustomer">
<plnk:portType name="tns:FlightCallbackPT" />
</plnk:role>
</plnk:partnerLinkType>
了解合作伙伴鏈接類型對于開發 BPEL 流程規范至關重要。有時,它可以幫助生成所有交互的圖表。定義合作伙伴鏈接類型后,我們已經完成了準備階段,并準備開始編寫業務流程定義。
第 4 步:創建業務流程
現在,您就可以開始編寫 BPEL 流程了。通常,BPEL 流程等待客戶端傳入的消息,以啟動業務流程的執行。在本示例中,客戶端通過發送輸入消息TravelRequest 啟動此 BPEL 流程。然后,此 BPEL 流程通過發送 EmployeeTravelStatusRequest 消息調用雇員出差狀態 Web 服務。由于此調用是同步調用,因此它等待 EmployeeTravelStatusResponse 消息。然后,此 BPEL 流程通過向上述兩家航空公司 Web 服務發送 FlightTicketRequest 消息對它們進行并發異步調用。每個航空公司 Web 服務通過發送 TravelReponse 消息進行回調。然后,此 BPEL 流程選擇較合適的航空公司并使用 TravelResponse 消息對客戶端進行回調。
我們首先編寫一個空的 BPEL 流程提綱,它展示了每個 BPEL 流程定義文檔的基本結構:
<process name="BusinessTravelProcess" ... >
<partnerLinks>
<!-- The declaration of partner links -->
</partnerLinks>
<variables>
<!-- The declaration of variables -->
</variables>
<sequence>
<!-- The definition of the BPEL business process main body -->
</sequence>
</process>
我們首先添加所需的命名空間。此處,我們必須定義目標命名空間以及用于訪問雇員和航空公司 WSDL 以及此 BPEL 流程 WSDL 的命名空間。我們還必須為所有 BPEL 活動標記聲明命名空間(此處采用缺省命名空間,以便不必限定每個 BPEL 標記名)。BPEL 活動命名空間必須為 http://schemas.xmlsoap.org/ws/2003/03/business-process/:
<process name="BusinessTravelProcess"
targetNamespace="http://packtpub.com/bpel/travel/"
xmlns="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/" >
...
合作伙伴鏈接接下來,我們必須定義合作伙伴鏈接,它們定義與此 BPEL 流程交互的不同方。每個合作伙伴鏈接都與描述其特性的特定 partnerLinkType 相關。每個合作伙伴鏈接還最多指定兩個屬性:
- myRole:表明業務流程本身的角色。
- partnerRole:表明合作伙伴的角色。
合作伙伴鏈接僅可以指定一個角色,通常同步請求/響應操作也僅能指定一個角色。對于異步操作,它指定兩個角色。在本示例中,我們定義四個角色。第一個合作伙伴鏈接稱作客戶端,由 travelLT 合作伙伴鏈接類型描述其特性。此客戶端調用該業務流程。我們需要指定 myRole 屬性以描述此 BPEL 流程 (travelService) 的角色。我們必須指定第二個角色:partnerRole。此處,該角色為 travelServiceCustomer,它描述 BPEL 流程客戶端的特性。
第二個合作伙伴鏈接稱作 employeeTravelStatus,由 employeeLT 合作伙伴鏈接類型描述其特性。它是 BPEL 流程與 Web 服務之間的一個同步請求/響應關系;我們再次僅指定一個角色。此時,該角色為 partnerRole,這是因為我們描述了 Web 服務(它是此 BPEL 流程的合作伙伴)的角色:
最后兩個合作伙伴鏈接對應于航空公司 Web 服務。由于它們使用同一類型的 Web 服務,因此我們基于一個合作伙伴鏈接類型 flightLT 指定兩個合作伙伴鏈接。此處,由于我們使用異步回調通信,因此需要兩個角色。此 BPEL 流程 (myRole) 對于航空公司 Web 服務的角色為 airlineCustomer,而航空公司 (partnerRole) 的角色為 airlineService:
<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>
變量 BPEL 流程中的變量用于存儲消息以及對這些消息進行重新格式化和轉換。您通常需要為發送到合作伙伴以及從合作伙伴收到的每個消息定義一個變量。就我們的流程而言,我們需要七個變量。我們將它們命名為 TravelRequest、EmployeeTravelStatusRequest、EmployeeTravelStatusResponse、FlightDetails、FlightResponseAA、FlightResponseDA 和 TravelResponse。
我們必須為每個變量指定類型。可以使用 WSDL 消息類型、XML 模式簡單類型或 XML 模式元素。在我們的示例中,我們對所有變量使用 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>
BPEL 流程主體流程主體指定調用合作伙伴 Web 服務的順序。它通常以 <sequence>(用于定義多個將按順序執行的操作)開始。在順序中,我們首先指定啟動業務流程的輸入消息。我們使用 <receive> 構造(它等待匹配消息,在本示例中為 TravelRequest 消息)實現此目的。在 <receive> 構造中,我們不直接指定消息。而是指定合作伙伴鏈接、端口類型、操作名稱以及可選變量(用于保存收到的消息以用于隨后的操作)。
我們將消息接收與客戶端合作伙伴鏈接在一起,并等待對端口類型 TravelApprovalPT 調用 TravelApproval 操作。我們將收到的消息存儲到 TravelRequest 變量中:
<sequence>
<!-- Receive the initial request for business travel from client -->
<receive partnerLink="client"
portType="trv:TravelApprovalPT"
operation="TravelApproval"
variable="TravelRequest"
createInstance="yes" />
...
<receive> 等待客戶端調用 TravelApproval 操作,并將傳入的消息以及有關業務出差的參數存儲到 TravelRequest 變量中。此處,此變量名與消息名相同,但并不一定要相同。
接下來,我們需要調用雇員出差狀態 Web 服務。但在調用之前,我們必須為此 Web 服務準備輸入。查看雇員 Web 服務的 WSDL,可以看到我們必須發送由雇員部分組成的消息。我們可以通過復制客戶端發送的消息的雇員部分來構造此消息。編寫相應的賦值語句:
...
<!-- Prepare the input for the Employee Travel Status Web Service -->
<assign>
<copy>
<from variable="TravelRequest" part="employee"/>
<to variable="EmployeeTravelStatusRequest" part="employee"/>
</copy>
</assign>
...
現在,我們就可以調用雇員出差狀態 Web 服務了。為了進行同步調用,我們使用 <invoke> 活動。我們使用 employeeTravelStatus 合作伙伴鏈接,并對 EmployeeTravelStatusPT 端口類型調用 EmployeeTravelStatus 操作。我們已經在 EmployeeTravelStatusRequest 變量中準備了輸入消息。由于它是同步調用,因此該調用等待回應并將其存儲在 EmployeeTravelStatusResponse 變量中:
...
<!-- Synchronously invoke the Employee Travel Status Web Service -->
<invoke partnerLink="employeeTravelStatus"
portType="emp:EmployeeTravelStatusPT"
operation="EmployeeTravelStatus"
inputVariable="EmployeeTravelStatusRequest"
outputVariable="EmployeeTravelStatusResponse" />
...
下一步是調用上述兩個航空公司 Web 服務。同樣,我們先準備所需的輸入消息(這兩個 Web 服務的輸入消息相同)。FlightTicketRequest 消息包含兩部分:
- flightData:它從客戶端消息 (TravelRequest) 中檢索而得。
- travelClass:它從 EmployeeTravelStatusResponse 變量中檢索而得。
因此,我們編寫一個包含兩個 copy 元素的賦值:
...
<!-- Prepare the input for AA and DA -->
<assign>
<copy>
<from variable="TravelRequest" part="flightData"/>
<to variable="FlightDetails" part="flightData"/>
</copy>
<copy>
<from variable="EmployeeTravelStatusResponse" part="travelClass"/>
<to variable="FlightDetails" part="travelClass"/>
</copy>
</assign>
...
輸入數據包含需要傳遞給航空公司 Web 服務的數據。由于格式相同,因此我們可以使用一個簡單復制直接傳遞它。在實際情況下,通常需要執行轉換。為此,可以使用具有 <assign> 的 XPath 表達式、使用轉換服務(如 XSLT 引擎)或使用由特定 BPEL 服務器提供的轉換功能。
現在,我們準備調用這兩個航空公司 Web 服務。我們將進行并發的異步調用。為表述并發,BPEL 提供了 <flow> 活動。對每個 Web 服務的調用將包含兩個步驟:
- 使用 <invoke> 活動進行異步調用。
- 使用 <receive> 活動等待回調。
我們使用 <sequence> 對這兩個活動進行分組。這兩個調用只在合作伙伴鏈接名稱上存在差別。我們對一個調用使用 AmericanAirlines,對另一個調用使用 DeltaAirlines。兩者均對 FlightAvailabilityPT 端口類型調用 FlightAvailability 操作,發送 FlightDetails 變量中的消息。
使用 <receive> 活動接收回調。我們再次使用這兩個合作伙伴鏈接名。<receive> 等待對 FlightCallbackPT 端口類型調用 FlightTicketCallback 操作。我們將結果消息分別存儲到 FlightResponseAA 和 FlightResponseDA 變量中:
...
<!-- 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>
<sequence>
<!-- Async invoke of the DA web service and wait for the callback-->
<invoke partnerLink="DeltaAirlines"
portType="aln:FlightAvailabilityPT"
operation="FlightAvailability"
inputVariable="FlightDetails" />
<receive partnerLink="DeltaAirlines"
portType="aln:FlightCallbackPT"
operation="FlightTicketCallback"
variable="FlightResponseDA" />
</sequence>
</flow>
...
在該流程的這個階段,我們收到兩個機票報價。在下一步中,我們必須選擇一個機票報價。為此,我們使用 <switch> 活動。
...
<!-- Select the best offer and construct the TravelResponse -->
<switch>
<case condition="bpws:getVariableData('FlightResponseAA',
'confirmationData','/confirmationData/Price')
<= bpws:getVariableData('FlightResponseDA',
'confirmationData','/confirmationData/Price')">
<!-- Select American Airlines -->
<assign>
<copy>
<from variable="FlightResponseAA" />
<to variable="TravelResponse" />
</copy>
</assign>
</case>
<otherwise>
<!-- Select Delta Airlines -->
<assign>
<copy>
<from variable="FlightResponseDA" />
<to variable="TravelResponse" />
</copy>
</assign>
</otherwise>
</switch>
...
在 <case> 元素中,我們檢查美國航空公司的機票報價 (FlightResponseAA) 是等于還是低于達美航空公司的機票報價 (FlightResponseDA)。為此,我們使用 BPEL 函數 getVariableData 并指定變量名。價格位于 confirmationData 消息的內部,雖然它是唯一的消息部分,但我們仍必須指定它。我們還必須指定查詢表達式以找到價格元素。此處,我們采用簡單的 XPath 1.0 表達式。
如果美國航空公司的機票報價低于達美航空公司的機票報價,則將 FlightResponseAA 變量復制到 TravelResponse 變量(我們最終將此變量返回給客戶端)。否則,我們將復制 FlightResponseDA 變量。
我們已經到達此 BPEL 業務流程的最后一步 — 使用 <invoke> 活動將回調返回給客戶端。對于此回調,我們使用客戶端合作伙伴鏈接并對 ClientCallbackPT 端口類型調用 ClientCallback 操作。保存答復消息的變量為 TravelResponse:
...
<!-- Make a callback to the client -->
<invoke partnerLink="client"
portType="trv:ClientCallbackPT"
operation="ClientCallback"
inputVariable="TravelResponse" />
</sequence>
</process>
到此,我們已經完成了我們的第一個 BPEL 業務流程規范。您可以看到,BPEL 并不是很復雜,并允許相對簡單和自然的業務流程規范。 第 5 步:部署和測試
我們部署到 Oracle BPEL Process Manager 的每個 BPEL 流程都需要一個流程描述符。BPEL 標準不包括此流程描述符,且它特定于 BPEL 服務器。部署流程描述符是流程在給定平臺上的唯一實現部分,必須重寫它才能在不同 BPEL 引擎上運行該流程。Oracle 流程描述符是一個 XML 文件,它指定有關 BPEL 流程的以下細節:BPEL 源文件名、BPEL 流程名 (ID)、所有合作伙伴鏈接 WSDL Web 服務的 WSDL 位置以及可選的配置屬性。流程描述符的默認文件名為 bpel.xml,但我們可以使用任何其他名稱:
<BPELSuitcase>
<BPELProcess src="Travel.bpel" id="TravelProcessCh4">
<partnerLinkBindings>
<partnerLinkBinding name="client">
<property name="wsdlLocation">
Travel.wsdl
</property>
</partnerLinkBinding>
<partnerLinkBinding name="employeeTravelStatus">
<property name="wsdlLocation">
http://localhost:9700/orabpel/default/Employee/Employee?wsdl
</property>
</partnerLinkBinding>
<partnerLinkBinding name="AmericanAirlines">
<property name="wsdlLocation">
http://localhost:9700/orabpel/default/AmericanAirline/AmericanAirline?wsdl
</property>
</partnerLinkBinding>
<partnerLinkBinding name="DeltaAirlines">
<property name="wsdlLocation">
http://localhost:9700/orabpel/default/DeltaAirline/DeltaAirline?wsdl
</property>
</partnerLinkBinding>
</partnerLinkBindings>
</BPELProcess>
</BPELSuitcase>
我們現在準備啟動 BPEL Process Manager。可以從開始菜單(如果使用 Windows)或通過執行 c:\orabpel\bin 目錄中的 startOraBPEL 腳本(假設 Oracle BPEL Process Manager 已經安裝到 c:\orabpel 中)來執行此操作。為便于訪問,建議將此目錄包含在 PATH 中。
Oracle BPEL Process Manager 包含一個名為 obant 的 Ant 實用程序,用于配置復雜的編譯和部署方案。obant 只是一個圍繞標準 Ant 的包裝程序,用于設置環境,然后調用標準 Ant Java 任務。要使用它,我們必須準備相應的項目文件(通常名為 build.xml)。以下是出差示例流程的項目文件:
<?xml version="1.0"?>
<project name="TravelProcessCh4" default="main" basedir=".">
<property name="deploy" value="default"/>
<property name="rev" value="1.0"/>
<target name="main">
<bpelc home="${home}" rev="${rev}" deploy="${deploy}"/>
</target>
</project>
要編譯和部署此 BPEL 流程,我們只需從命令行啟動 obant。
既然我們已經在 Oracle BPEL 服務器上成功部署了 BPEL 流程,那我們就執行它。Oracle BPEL Process Manager 提供了一個 BPEL 控制臺,通過它可以在 BPEL 服務器域中執行、監視、管理和調試 BPEL 流程。可以通過 http://localhost:9700/BPELConsole/ 訪問 BPEL 控制臺。我們必須單擊流程名、填寫以下表單并按 Post XML Message 按鈕:
 |
圖 7:BPEL 控制臺 |
我們現在看到一個屏幕,通知我們正在異步處理流程實例。我們可以選擇執行、實例審計或實例調試的可視化流。實例的可視化流以圖形方式顯示了 BPEL 流程實例的執行。我們可以監視流程的執行及其狀態(正在運行、已完成、已取消或過時):
 |
圖 8:實例流的圖形視圖 |
Oracle BPEL Process Manager 提供了多個用于審計、管理、調試和部署 BPEL 流程的選項。有關更多信息,請查看產品文檔。
結論
現在,您已經熟悉了使用 BPEL 組合 Web 服務的基本概念,您可以更深入地研究更高級的概念。我的下一篇文章將介紹一些高級 BPEL 特性,如故障處理、范圍、補償、并發活動以及事件處理。
Matjaz B. Juric 擁有計算機與信息科學的博士學位。他是用于 Web 服務的業務流程執行語言一書 (Packt Publishing) 的作者。Matjaz 還是專家級 J2EE EAI、專家級 EJB、應用的 J2EE 設計模式以及VB.NET 序列化指南(全部由 Wrox Press 出版)的合著者。Matjaz 還曾為 Java Developer's Journal、Java Report、Java World 以及其他出版物撰寫過文章。