原帖地址:http://www.ibm.com/developerworks/cn/webservices/ws-appwsif/

2002 年 6 月 01 日

Web 服務調用框架(Web Services Invocation Framework)提供一種無需考慮 Web 服務的傳輸協議及其具體位置而能調用它的方法。這就允許使用 WSIF 以相同的方式去調用基于非 SOAP(non-SOAP)的服務,從而簡化了應用程序的開發。學習有關 WSIF 被提交給 Apache Software Group 后的最新改變。

介紹

既無須考慮提供 Web 服務的方法,也不要考慮該服務位于何處,Web 服務調用框架(Web Services Invocation Framework,WSIF)只是個用于調用該服務的 Java API,它很簡單。它使開發者從必須針對具體的傳輸協議或服務環境的開發服務這一限制條件中解放出來。因此,它包含有能提供與綁定無關的訪問任意 Web 服務的 API。根據運行時對服務的元數據進行研究的結果,它允許對 Web 服務執行無存根或全動態的調用。它還允許您能在運行時將某個綁定的已更新實現插入到 WSIF 中。如果您使用了 WSIF 的提供者的方案,它還能允許您將一個新的綁定在運行時插入到 WSIF 中。這就允許進行調用的服務將選擇綁定的時間推遲到運行時。最后,由于它是嚴格按照 WSDL 的,因而任何可以用 WSDL 來描述的服務它都能調用。

WSIF 最初發表在 2001 年 10 月 的 alphaWorks 上(請參閱 參考資料)。自公布之后 alphaWorks 發行版已有超過 4000 份的下載記錄。WSIF 的一位創造者寫了兩篇關于 WSIF 的優秀文章,描述了 WSIF 的動機及其使用,我們鼓勵您去閱讀它們(請到 參考資料參閱 Web service invocation sans SOAP,第 1 部分與第 2 部分)。

本文是對 WSIF 貢獻給 Apache Software Foundation 的響應。WSIF 源代碼已捐贈給了由 Axis 工程贊助的 Apache XML 項目。在 Apache CVS 樹中得到,名為 xml-axis-wsif。在: http://cvs.apache.org/viewcvs.cgi/xml-axis-wsif您也可以瀏覽到該代碼。

在本文中我會講講 WSIF 的動機、用法和體系結構。并概述了自 alphaWorks 公布以來對 WSIF 所進行的更改,還要看一下實驗性的以及前瞻性的觀點。但在開始所有工作之前,我要快速地概括說明一下 WSDL。





回頁首


有關 WSDL 的一些背景

Web 服務描述語言(Web Services Description Language,請參閱 參考資料)本來就是可擴展的 ― 從一開始,設計人員就在 WSDL 中實現了服務接口與實現的分離。

在 WSDL 中,服務被定義為三個截然不同的部分:

  1. PortType。PortType 定義了由服務提供的抽象接口。一個 PortType 定義了一組 Operation。每一個 operation 可能為 In-Out(請求-響應)、In-Only、Out-Only 或 Out-In(懇求-響應)。每一個 operation 定義了 input 和/或 output Message。一個 message 又是被定義為一組 Part,而每一個 part 有一個由模式定義的類型。
  2. Binding。一個 binding 定義了如何在一個抽象 PortType 與一個真實的服務格式和協議之間建立映射關系。例如,SOAP 綁定定義了編碼風格、SOAPAction 頭和 body(targetURI)的名稱空間等等。
  3. Port。Port 定義了可用服務的實際位置(端點)― 例如,SOAP 服務所在的 HTTP URL。

在現在的 WSDL 中,每一個 Port 有且只有一個 binding,而每一個 binding 有一個獨一無二的 PortType。反過來(也是更重要的),每一個 Service(即 PortType)可能有多個 Port,而每一個 Port 則代表了一個作為替換的訪問該服務的位置和綁定。

由于我們所需要的是基于 WSDL 而不是直接基于 SOAP 的 API,因而在設計 WSIF 時我們把 WSDL 作為參考對象。WSIF 實際上是一個插入對傳輸與格式支持的框架。在 WSIF 中它們叫 provider,而最明顯的 provider 就是支持 SOAP 的 provider。

添加 可擴展性元素(extensibility elements)到 WSDL 中不僅允許創建 SOAP 描述,還能允許創建其他服務實現的描述。典型情況下,使用已存在的應用程序組件 ― 如 Java 類、EJB(Enterprise JavaBeans)或 COM 對象來實現 Web 服務。這些組件有時候就是舊系統上的應用程序 包裝器,如大型機事務系統。通過擴展 WSDL 以便能描述已有的組件模型,我們能捕獲到公開的可用 SOAP 服務和底層的組件之間的相關關系,以及兩個可用實現實際上是相同的業務服務的事實。事實上,為現有的組件添加可擴展性元素會做更多的事情 ― 也給現有的組件模型增加了面向服務的體系結構的描述能力。





回頁首


WSIF 的動機

WSIF 的動機是我們原來希望看到“面向服務的體系結構”(Services Oriented Architecture)比 SOAP 變得更廣泛。有許多不同協議、傳輸和分布式計算技術比現在的 SOAP 提供的功能要多的多 ― 尤其在管理、事務、安全和其他服務質量(Quality of Service,OoS)特性方面上。正當 SOAP 迅速變得越來越受歡迎時,主要的問題實際上是投資之一了。許多已在象 CORBA 等技術上進行過投資的公司希望能繼續使用和保持以前的技術。另一方面,帶 SOAP 的 Web 服務有一個獨一無二的優點 ― 用于描述和發現的基礎結構。在 UDDI 目錄中或檢驗文檔中都能找到 WSDL 文件,任何人都能下載之,并可使用某個通用的可用工具來生成使用該服務的代碼,而無須考慮是在本地網絡中還是在因特網中。這種描述語言的通用可用性也正刺激著其他工具的增加 ― 例如,用于將服務編排組合在一起的語言(XLANG 和 WSFL)的開發。

我們真的想使 WSDL 的可擴展性與結構變成現實。WSDL 允許我們用可擴展性元素描述已存在系統。例如,我們已經寫出了以下的 WSDL 擴展,能描述在使用連接器的 CICS 和 IMS 中的事務、能描述對遠程無狀態會話 Enterprise JavaBean 的調用、以及能描述基于 JMS 消息傳遞系統上的 SOAP 與非 SOAP 消息。可是,盡管在描述這些東西時,這些擴展很有用,但執行它們時卻不那么有用了。

現在的 WSDL 不僅僅是一個描述層 ― 它還有一個在工具里的真正實現,在這些工具中可以使用 WSDL 描述來生成訪問服務的存根。所以如果添加了非 SOAP 系統的描述,我們就處在失去該優點的危險之中。WSIF 解決了這個問題。事實上,作為一個可插入框架,WSIF 允許插入 provider。一個 provider是一段代碼,該代碼支持某一 WSDL 擴展,并允許根據具體的實現調用服務。這就意味著客戶端代碼是與該實現無關的,而僅僅取決于服務的 PortType。WSIF 也允許延遲綁定,此時一個新的提供者和描述在運行時是可用的,已有的客戶端就能利用這個新的實現。最后,WSIF 還容許客戶端把端口選擇委托給基礎結構和運行時,這樣就容許在服務質量特點或業務策略的基礎上來選擇實現。

WSDL 的結構容許某一 Web 服務有多個實現和共享相同 PortType 的多個 Port。換句話說,WSDL 容許相同接口既可有 SOAP 綁定又可以有 IIOP 的綁定。我們想讓我們的 API 能容許相同的客戶端代碼可訪問任意可用的 binding ― 如果代碼是寫來用于 PortType 的,則它可能是一個使用哪個 port 和 binding 的部署或配置設置(或一個代碼選擇)。

我們為 WSIF 建立了下列必要條件。它必須是:

  1. 支持任意有效的 WSDL 擴展。
  2. 支持由 WSDL 描述的服務的動態調用。
  3. 支持基于 WSDL PortType 的 API。
  4. 容許延遲綁定到不同的格式與傳輸。
  5. 支持編譯的方法與動態的方式。
  6. 支持最小代碼部署說明(n 個提供者,無須每個服務都有代碼)。
  7. 支持服務請求數據的不同的內存表示法。





回頁首


WSIF 用法

有兩種使用 WSIF 的方法:基于存根的模型和動態調用接口(Dynamic Invocation Interface,DII)。

存根模型

基于存根的模型容許用戶使用常見的程序設計模型來調用 Web 服務的業務方法。有一個標準化的從 WSDL 定義的接口到 Java 定義的接口的映射 ― 在 JAX-RPC 標準中。有人試圖通過 JCP/JSR 過程把 WSIF 模型與 JAX-RPC 集成起來,但感覺上 WSIF 太具有試驗性了以至于不能在那時引入它。我們所做的工作是創建了一個松散集成。JAX-RPC 定義了服務定義接口(Service Definition Interface,SDI)― 存根的業務接口。WSIF 也定義了一個與 JAX-RPC 所使用的相同的 SDI 的接口。所以,雖然 WSIF 與 JAX-RPC 并不共享接口,但它們還是能共享工具(例如,Axis WSDL2Java)。 清單 1列出了存根模型的一個示例。


清單 1、WSIF 中使用存根模型的示例
1   WSIFService sq = ServiceFactory.newInstance().
            getService("http://my.com/svcs/stockquote.wsdl");
            2   MyService mySvcStub = sq.getStub("soap", MyService.class);
            3   mySvcStub.myMethod();
            

動態調用接口

DII 緊密地建模在 WSDL 基礎上。在 WSDL 中,一個 operation 有一個 input message 以及一個可選的 output message 或 fault message。而在 WSIF 中,我們可看到一個類似的層次結構。二者之間有著密切的對應關系。事實上,我們的第一代就是一一對應的,但我們已重構了該對應關系以使之變得更合乎邏輯和(略微地)更簡單。 表 1:WSDL 與 WSIF 實體對應關系列出了對應關系。

表 1:WSDL 與 WSIF 實體的對應關系

WSDL WSIF
(WSIL/UDDI) WSIFServiceFactory 或 JNDI
Service WSIFService
Port WSIFPort
Binding (無相應的實體)
Operation WSIFOperation
Message WSIFMessage
Part (無相應的實體)

要使用 DII,您需要:

  1. 選擇一個 port。
  2. 創建一個 operation。
  3. 創建并植入到 in-message。
  4. 執行該 operation。
  5. 從 out-message 中讀出響應數據。

清單 2列出一個簡單的示例。


清單 2、WSIF 中使用動態調用接口的例子
1   WSIFService sq = ServiceFactory.newInstance().
            getService("http://my.com/svcs/stockquote.wsdl");
            2   WSIFPort defPort = sq.getPort();
            3   WSIFOperation getQ = defPort.createOperation("getQuote");
            4   WSIFMessage inMessage = getQ.createInputMessage();
            5   inMessage.setStringPart("symbol", "IBM");
            6   ...
            7   getQ.executeRequestResponse(inMessage, outMsg, fltMsg);
            8   outMessage.getFloatPart("value");
            

命令行工具 DynamicInvoker 是 WSIF alphaWorks 發行版的一個功能,它能調用接口內含有簡單類型的任意 Web 服務。有時候,服務的參數使用了復雜類型,我們經常遇到的請求之一就是容許這些服務的動態調用。就是帶著這個想法來設計 WSIF 的,但由于在第一代中我們只支持使用 JavaBean 組件來表示復雜類型,所以,若沒有首先生成一個合適的 JavaBean 組件我們不可以調用需要這些復雜類型的服務。為補救該不足,我們已經開發出了一組叫 JROM 的類(最初時它僅代表 Java 記錄對象模型(Java Record Object Model,JROM)― 但事實上它已經變成了一個專用名 ― 念作 Jay-Rom)。JROM 是一個抽象的樹結構,它能在內存中表示大部分模式復雜類型。把 JROM 想象成一個輕量級的 DOM,樹上的每片葉對象是一個基本類型(不象 DOM,每片葉對象都是字符串)。

即使在 classpath 中沒有與模式復雜類型相匹配的 Java 類型,通過和 WSIF 一起使用 JROM,動態調用也是允許的。這是一個非常強大的方法,尤其表現在構建諸如網關、流程引擎或測試客戶端這樣的 Web 服務系統方面。還不是所有的提供者都支持 JROM,但我們已經在 ApacheSOAP 提供者中添加了對 JROM 的支持。

雖然不是 WSIF 開放源代碼的一部分,但在 alphaWorks 處是可以下載到 JROM 的,請參閱 www.alphaworks.ibm.com/tech/jrom





回頁首


WSIF 體系結構

WSIF 體系結構是建立在大量工廠上的。使用工廠是因為我們希望隱藏所使用的對象是否為“靜態的”還是為“動態的”。當對象是靜態時,它的生成是為了滿足某個特定的 port、message 或 operation 的。這容許構建一個快速實現。而當對象是動態時,對象會在運行時使用 WSDL 描述來處理某特定的 port、operation 或 message 處理。目前,WSIF 主要使用動態對象,這也正是最初我們實現它們的方式。事實上我們設想了動態機制的幾個不同層面。

全動態體系結構

象流程控制引擎這樣的例子中,流程描述是作為一個 XML 文檔而提供的,此時可能會用到該體系結構。在消息為創建一個新的聚集服務而被發送給其他操作之前,流程引擎會操作該消息。一個 provider 對應每個 binding 類型有一段部署代碼。因而,如果存在著 b個不同 WSDL binding 擴展,就應該有 b個構件。

半動態體系結構

定義 PortType 的 message 是靜態編譯的,而 binding 卻是動態的。這就不僅需要一個基于 PortType 的代碼部署,還要加上每個 provider 對應于每 binding 類型的一個代碼部署。所以,如果存在著 p個 PortType 和 b個的 WSDL binding,則就會有 p + b個構件。在接口發生改變時,已編譯好的消息不得不被重新部署。

靜態體系結構

WSDL binding 與 port 都是被靜態編譯的。在這種情況下,在處理時已定義的缺省 port 會被編譯。每個實體對應每個綁定實例有一個代碼部署。所以,構件的數目應該是 p x b





回頁首


自 alphaWorks 發行以來的變化

自 alphaWorks 發行以來已有了許多的變化。鼓勵您自己能看看開放源代碼樹。主要的變化是:

  • WSIFMessageWSIFPart模型的簡化。我們刪除了 WSIFPart接口而把之合并到 WSIFMessage中。為能捕獲使用 WSIFMessage不同的樣式,我們增加了一個叫 getRepresentationStyle()的方法。
  • PortFactory對象改名為 Service
  • 增加了 ServiceFactory接口。
  • 從使用 PortTypeCompiler到使用 J2SE 1.3 動態代理支持的改變。
  • 增加了新的綁定與傳輸 ― 基于 JMS 的 SOAP、EJB 綁定、基于 Apache Axis 的 SOAP 提供者。
  • 支持跟蹤與日志記錄活動。
  • 使用 J2SE JAR service provider 規范支持新 provider 的動態注冊(請參閱 參考資料)。





回頁首


對 WSIF 的試驗性補充

目前,有很多要通過 WSIF 探索的領域,其中有異步請求-響應消息和上下文意識的消息。

異步請求-響應

目前我們有了一個異步請求-響應模型,響應在一個不同于原始請求的執行線程中被處理。為了支持它,基本上,請求者會注冊一個回調對象或處理程序,在接收到響應時會調用之。

請求消息被送出時,處理程序對象也會被傳送給 WSIFOperation。WSIF 提供了一個關聯服務,它根據指定的標識符保存該處理程序。 WSIFOperation調用 WSIF 關聯服務,它保存了服務本身和處理程序,該程序使用了請求的關聯標識。這些都發生在當前事務的內部。如果請求用戶指明了某個事務隊列,那么會在當前事務內部完成這些工作。

WSIFOperationWSIFResponseHandler對象都是可序列化的,因而在請求與響應之間系統發生故障時,處理程序對象的狀態可被保持。因為關聯服務也是可插入的,所以為了高可用性,也就可以提供諸如原子事務和持久性等其他特性。

當響應來得有一些延遲時,偵聽器線程會獲得它并把它傳遞給已保存好的 WSIFOperation。操作包含有把響應數據編出到某一 WSIFMessage中的邏輯,然后會以 outMessage為參數,執行處理程序方法 executeAsyncResponse()。偵聽器線程運行在與原始請求不一樣的事務上的,因而響應也就是一個與請求不同的獨立事務。

例如, 清單 3演示了在 WSIFResponseHandler 類基礎上如何實現一個處理程序的。一個被設計成能理解響應消息和使用響應值的處理程序。


清單 3、在 WSIF 中實現異步處理程序
1   public class QuoteHandler implements WSIFResponseHandler  {
            2
            3   protected String symbol = null; // local storage of the symbol we wanted quoted
            4   public void setSymbol(String value) { symbol = value };
            5   public void executeAsyncResponse(WSIFMessage out, WSIFMessage fault) {
            6            // this simplified example assumes no failures and that the symbol has
            been set correctly
            7       float quoteValue = out.getPart ("quote");
            8       updateQuoteDB(symbol, quoteValue);
            9   }
            

清單 4演示了服務調用。


清單 4、使用 WSIF 異步調用服務
1   String symbol = "IBM";
            2   QuoteHandler quoteHandler = new QuoteHandler();
            3   quoteHandler.setSymbol(symbol); // the handler needs to know
            the symbol when it gets executed later.
            4
            5   WSIFMessage inMessage;
            6   WSIFOperation quoteOperation;
            7   ...
            8   inMessage.setXXXPart(...) // set up inMessage for invocation
            9   quoteOperation.executeAsyncRequestResponse(inMessage, quoteHandler);
            

上下文意識消息

我們也希望能支持設置與使用上下文的想法。雖然在 W3C 的 WS-Description 工作組已有一些有關本主題的討論,但還沒有實際的語法或語義學。

例如,某個 SOAP/HTTP 端口可能要求一個 HTTP 用戶名與密碼。雖然該消息是特定于該調用的,但通常情況下卻不是服務的參數。常規情況下,上下文是被定義為一組名稱-值對。盡管如此,由于 Web 服務傾向于使用 XML Schema 類型來定義數據類型,所以我們選擇使用一組已命名的 Part來表示上下文的名稱-值對,每一個 Part 等于一個 XML Schema 類型的實例,這種表示方法與 WSIFMessages使用的表示方法是相同的。這比使用 屬性概念稍微多一些開銷,但卻能更好地與 WSDL 和 WSIF 的其它特性相適合。

WSIPortWSIFOperation上的 setContext()getContext()方法允許應用程序設計人員或存根將上下文信息傳遞給 binding。Port 實現可能會使用該上下文 ― 例如來更新某個 SOAP 頭。還沒有關于 Port 如何使用上下文的定義。





回頁首


未來的想法

我們希望看到在 WSIF 中有很多領域不斷發展,來提升其功能性和有用性。首先,我們希望把傳輸從消息格式中分離出來的重構能力。我們定義了 WSIFFormatter類以允許訪問數據以及數據到某個本地格式的轉換。

接下來,一個消息內數據的抽象樹表示可能是很用的。目前,我們主要是使用從模式編譯出的類來攜帶 WSIFMessage中的服務參數。但在 WSIF 的某個實現中,即 Web Services Gateway(請參閱 參考資料)中,我們使用一個名為 JROM的抽象樹概念,它密切地映射到某個 XML Schema。

我們還希望能看到更多更好的用于許多已有的傳輸類型與對象系統的提供者。最后,對用 C 或 C++ 創建服務的開發者來說,一個用 C 或用 C++ 編寫的 WSIF 實現肯定是有幫助的。





回頁首


總結

Web 服務調用框架是提供一個面向服務的框架的開放源代碼倡議,它允許可在 WSDL 中描述和以通用方式調用 SOAP 和非 SOAP 服務。為了支持新的傳輸和協議,WSIF 定義了一個可插入接口(provider 接口)。通過支持代理接口和動態調用接口,WSIF 允許最終用戶與系統軟件開發人員使用 WSIF 提供的多種協議支持。WSIF 還支持延遲綁定,服務就可以被重新定位成新的協議類型而無須改變代碼。WSIF 的開放源代碼包括針對 Java、EJB 和 SOAP(基于 HTTP 與 JMS)的 provider。WSIF 已經被使用在 WebSphere 企業版 4.1 與 Versata 的 Logic Server WebServices Add-On 中。

致謝

按照字母順序排列,對 WSIF 的發行作出貢獻的人主要有:Aleksander A. Slominski、Anthony Elder、Dieter Koenig、Gerhard Pfau、Jeremy Hughes、Mark Whitlock、Matthew J. Duftler、Michael Beisiegel、Nirmal Mukhi、Owen Burroughs、Paul Fremantle、Piotr Przybylski 和 Sanjiva Weerawarana。



 

參考資料



 

關于作者

 

Paul Fremantle 是 IBM 的 Hursley Laboratory 的體系架構師,致力于 IBM WebSphere Application Server 內的 Web 服務組件以及如 WSDL4J 和 WSIF 等開放源代碼項目的研究。Paul 是 Java 標準 JSR110: Java APIs for WSDL 的副主管。Paul 以前曾是 IBM Global Services 軟件組的 WebSphere 專家和體系架構師。在加入 IBM 之前,他在制藥業做過咨詢顧問。

他的出版物包括將 EJB 從 WebLogic 移植到 WebSphere 的文章和一本紅皮書:“The XML Files: Using XML and XSL in WebSphere”。Paul 經常參加 ApacheCon、XML Europe、Software Architecture 和其他業界會議。他擁有牛津大學的數學與哲學文學碩士學位和計算技術的理學碩士學位。

可通過 pzf@uk.ibm.com與他聯系。