程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Web服務,第3部分: Axis2數據綁定

Java Web服務,第3部分: Axis2數據綁定

編輯:關於JAVA

相關文章:

Java Web服務,第1部分: Java Web服務在未來一年內的發展

Java Web服務,第2部分: 深度探索Axis2:AXIOM

Apache Axis2 Web 服務框架一開始就設計用於支持多種 XML 數據綁定方法。當前的版本 提供對 XMLBeans 和 JiBX 數據綁定以及專門針對 Axis2 開發的自定義 Axis 數據綁定 (Axis Data Binding,ADB)的全面支持。本文將說明如何將這些不同的數據綁定方法與 Axis2 結合使用,並說明為什麼可能會為應用程序優先選擇其中的一種方法。

盡管 XML 消息交換是 Web 服務的核心,但大部分 Web 服務應用程序都不會對 XML 的問 題進行考慮。相反,這些應用程序希望交換特定於應用程序的業務數據。在這種情況下,XML 僅僅是用於表示業務數據以支持 Web 服務接口的一個格式而已。XML 可很好地滿足此用途, 因為它提供了獨立於平台的表示形式,可供各種工具進行處理。但應用程序最終需要將 XML 轉換為其內部數據結構(或反向轉換),以便在應用程序內使用此數據。

數據綁定 是指處理 XML 和應用程序數據結構間的這種轉換的技術。可以為應用程序編寫 自定義數據綁定代碼,但大部分開發人員發現使用數據綁定框架更為方便,此類框架將以通 用的方式處理此轉換工作,適用於各種應用程序。Apache Axis2 Web 服務框架的一個主要優 勢在於,此框架從最開始就設計為使用各種數據綁定框架。可以選擇最適合您的需求的數據 綁定方法,並使用此方法來處理 XML 與數據結構間的轉換,同時使用 Axis2 框架(及擴展 )來處理實際的 Web 服務工作。

本文將通過使用三個受支持的不同數據綁定實現的同一個 Web 服務的示例代碼說明如何 使用 Axis2 提供的數據綁定靈活性。可以通過其中了解為何可能會優先選擇其中某個數據綁 定。

鏈接到 Axis2

在本系列的前一篇文章中,我們已經了解了 Axis2 用於 XML 消息綁定的 AXIOM 文檔模 型。AXIOM 與其他文檔模型的不同之處在於,它支持根據需要綁定模型,而不用一次性完成 此工作。當使用數據綁定框架在 XML 和應用程序數據結構之間進行轉換時,數據綁定 XML 通常只是 AXIOM 文檔模型的一個虛擬部件。除非由於某些原因而需要此模型(用於使用 WS -Security 進行加密或簽名時),否則就不會擴展為完整文檔模型。

為了隔離應用程序,避免直接使用 AXIOM 的情況,Axis2 支持從 Web 服務描述語言 (Web Services Description Language,WSDL)服務描述生成鏈接代碼。所生成的鏈接代碼 使用所選數據綁定框架處理數據結構與 XML 之間的轉換細節,讓您的應用程序直接訪問數據 結構。Axis2 還從另一方面提供了有一定限制的支持,從現有代碼生成 WSDL。

Axis2 可為服務客戶機和服務提供者生成鏈接代碼。客戶機鏈接代碼采用存根類的形式, 始終從 Axis2 org.apache.axis2.client.Stub 類進行擴展。提供者(或服務器)鏈接代碼 采用服務特定的實現框架的形式提供,並提供實現 org.apache.axis2.engine.MessageReceiver 接口的消息接收器類。客戶機和服務器鏈接代 碼生成工作都由 WSDL2Java 工具進行處理。接下來,我們將了解實際的鏈接代碼,然後將詳 細討論如何使用 WSDL2Java 工具,最後將簡單說明如何從現有代碼著手進行相關工作。

客戶機鏈接代碼

客戶端存根代碼為應用程序代碼定義訪問方法,以調用服務操作。首先要創建存根類的實 例,通常使用缺省構造函數(如果服務端點總是與用於生成存根的 WSDL 中定義的端點相同 ),或使用接受以字符串形式提供的其他端點引用的構造函數進行此工作。創建了存根的實 例後,可以選擇使用 org.apache.axis2.client.Stub 基類定義的方法來配置各個功能。然 後可以調用服務特定的訪問方法來實際調用操作。

清單 1 給出了一個示例,說明如何使用更改為客戶機系統 (localhost) 上的缺省 Tcpmon 端口 8800 的服務端點(其在 WSDL 中的任意設定值)創建存根。Tcpmon 是用於監 視 Web 服務交換的一個流行工具,因此在客戶機代碼中使用此選項通常非常有用。創建了存 根實例後,超時值的缺省值將改變(調試提供者代碼時也很有用,因為很容易就會超過標准 的 20 秒超時設置),並會調用服務方法。

清單 1. 客戶機存根用法示例

LibraryStub stub = new LibraryStub ("http://localhost:8800/axis2/services/library");
stub.getServiceClient().getOptions().setTimeoutInMilliseconds(10000000);
Types[] types = stub.getTypes();

清單 1 中的代碼顯示了同步方法調用,其中的客戶機線程將阻塞在服務調用內,在調用 完成且結果可用之後才會返回。Axis2 還支持使用回調接口進行異步調用。清單 2 顯示了經 過修改的清單 1 代碼,其中使用了一個小小的異步調用(應用程序代碼僅僅等待操作完成, 而不進行任何有用的工作)。

清單 2. 客戶機存根異步示例LibraryStub stub = new LibraryStub ("http://localhost:8800/axis2/services/library");
TypesCallback cb = new TypesCallback();
stub.startgetTypes(cb);
Type[] types;
synchronized (cb) {
while (!cb.m_done) {
 try {
  cb.wait();
 } catch (Exception e) {}
}
types = cb.m_result;
if (types == null) {
 throw cb.m_exception;
}
}
...
private static class TypesCallback extends LibraryCallbackHandler
{
private boolean m_done;
private Types[] m_result;
private Exception m_exception;

public synchronized void receiveResultgetTypes(Type[] resp) {
  m_result = resp;
  m_done = true;
  notify();
}

public synchronized void receiveErrorgetTypes(Exception e) {
  m_exception = e;
  m_done = true;
  notify();
}
}

對於 HTTP 連接(如清單 2 中的情況),響應通常將立即返回到客戶機。在將請求同響 應分離的傳輸——如 Java™ Message Service (JMS) 或簡單郵件傳輸協議 (Simple Mail Transfer Protocol,SMTP)——上操作時,異步調用最有用,因 為在這種情況下,請求發出時間和接收到響應的時間存在很大的延遲。當然,使用 HTTP 訪 問的服務還可能涉及到大量的處理延遲。對於具有此類延遲的 HTTP 服務,可以使用 WS- Addressing 來支持分離的響應,異步調用非常適合用於處理這些響應。

除了存根類(如果使用異步支持生成的話,還包括回調處理程序類)外,還有為客戶機代 碼生成的接口。接口定義與 WSDL portType 所定義的操作匹配的服務方法。存根實現此接口 ,並添加一些供內部使用的方法。可以直接使用存根,如清單 1 和清單 2 中所示,還可以 使用接口來僅僅使用屬於服務定義的方法。無論采用哪種方式,調用服務方法時,存根都將 使用所選數據綁定框架處理將請求數據對象轉換為 XML,以及將返回的 XML 轉換為響應數據 對象的工作。

如果希望在客戶機上直接使用 XML,則根本不需要使用生成的客戶機存根類;可以轉而使 用 org.apache.axis2.client.ServiceClient 類。這樣做意味著需要首先配置服務和操作, 然後調用 ServiceClient.createClient() 方法為操作創建 org.apache.axis2.client.OperationClient。為了方便起見,WSDL2Java 工具(本文稍後討 論)提供了相應的選項,可在即使直接使用 XML 的情況下生成存根類。這種情況下生成的存 根與數據綁定示例類似,但其中傳遞的是 AXIOM 元素而不是數據對象。

服務器鏈接代碼

Axis2 的服務器端鏈接代碼是作為 Axis2 服務器配置的一部分定義的消息接收器類。此 消息接收器必須實現 org.apache.axis2.engine.MessageReceiver 接口。此接口定義單個 void receive(org.apache.axis2.context.MessageContext) 方法。在接收到請求消息時, Axis2 框架將調用此方法,然後由此方法負責處理請求的所有處理工作(包括在合適的情況 下生成響應)。

如果直接使用 XML(采用 AXIOM 元素的形式),則可以利用服務器端鏈接的標准 org.apache.axis2.receivers.RawXML*MessageReceiver 類之一(其中 * 描述服務使用的消 息交換類型)。否則,就可以使用生成的消息接收器類,其在基於 Axis2 AXIOM 的接口和使 用數據對象的服務代碼之間進行適配。此服務代碼以框架實現的形式生成,其中包含直接引 發異常的服務方法。您需要向框架添加自己的代碼,以完成服務器端掛鉤。

清單 3 顯示了服務端框架的示例(為了便於閱讀,進行了格式調整),其中的 getBook () 方法保持生成時的原樣,getTypes() 方法通過委托到實際實現類進行實現。

清單 3. 服務器框架示例public class LibrarySkeleton
{
  private final LibraryServer m_server;

  public LibrarySkeleton() {
    m_server = new LibraryServer();
  }

  /**
   * Auto generated method signature
   *
   * @param isbn
   * @return book value
   */
  public com.sosnoski.ws.library.Book getBook(java.lang.String isbn) {
    //Todo fill this with the necessary business logic
    throw new java.lang.UnsupportedOperationException("Please implement " +
      this.getClass().getName() + "#getBook");
  }
  /**
   * Get the types of books included in library.
   *
   * @return types
   */
  public com.sosnoski.ws.library.Type[] getTypes() {
    return m_server.getTypes();
  }
}

直接向此類添加代碼的缺點在於,如果服務器接口更改,則需要重新生成此類並包含更改 。可以通過添加擴展生成的框架的獨立實現類來避免這種情況,從而能在不更改生成的代碼 的情況下重寫框架方法。為此,需要對生成的 services.xml 服務描述進行更改。所需的工 作很簡單,直接使用實現類名稱替換框架類名稱即可。本文稍後將討論的數據綁定示例全部 使用獨立的實現類方法。可以在下載部分獲得這些示例 Ant build.xml 文件,以了解如何自 動進行替換。

Axis2 工具

Axis2 提供了一系列工具來幫助開發人員使用此框架。其中最重要的是允許從 WSDL 服務 定義生成 Java 鏈接代碼(在下面討論)的工具和從現有 Java 代碼生成 WSDL 服務定義的 工具。

從 WSDL 生成代碼

Axis2 提供了 WSDL2Java 工具,用於從 WSDL 服務定義生成代碼。可以通過將 org.apache.axis2.wsdl.WSDL2Java 類作為 Java 應用程序運行來直接使用此工具,也可以 通過 Ant 任務、Maven 插件或 Eclipse 或 IDEA 插件。擁有這麼多選擇的缺點在於,從功 能和錯誤修補方面而言,備選方案通常滯後於基本 Java 應用程序,因此通常可能最好直接 運行 Java 應用程序(本文將對此進行討論)。

WSDL2Java 提供很多不同的命令行選項,而且選項的數量還會隨著時間的增加而增加。 Axis2 文檔包括了選項的完整參考,這裡將僅僅討論一些最為重要的內容:

-o path — 設置用於輸出類和文件的目標目錄(缺省輸出到工作目錄)

-p package-name — 設置生成的類的目標包(缺省為從 WSDL 命名空間生成)

-d name — 設置數據綁定框架(adb 表示 ADB,xmlbeans 表示 XMLBeans,jibx 表示 JiBX 以及 none 表示無數據綁定;adb 為缺省選項)

-uw — 取消 doc/lit-wrapped 消息的包裝,僅適用於受支持的框架(目前包括 ADB 和 JiBX)

-s — 僅生成同步客戶機接口

-ss — 生成服務器端代碼

-sd — 生成服務器端部署文件

-uri path — 為要生成的服務設置指向 WSDL 的路徑

還有一些專門針對特定數據綁定框架的 WSDL2Java 選項。稍後討論數據綁定示例時會看 到幾個此類選項。

從代碼生成 WSDL

Axis2 還提供了 Java2WSDL 工具,可用於從現有服務代碼生成 WSDL 服務定義。不過, 此工具有很多限制,包括無法使用 Java 集合類以及在從 Java 類生成的 XML 的結構處理方 面不靈活。造成這些限制的部分原因是,由於 Web 服務開發方式的改變,使得大家對此領域 沒有太多的興趣。

總的說來,Web 服務和 SOA 領域的很多權威都對從現有代碼生成 Web 服務不屑一顧。他 們感覺從代碼著手會增加 XML 消息結構與特定實現間的偶合,而 Web 服務的總體原則是 XML 應該獨立於實現。對此當然有很多支持的聲音,但也有人表示反對。其中一個原因涉及 到從頭編寫 WSDL 服務和 XML 模式定義的困難性。WSDL 和模式都是復雜的標准,用於處理 這些定義的可用工具都要求對標准足夠了解,才能夠有效地加以使用。如果開發人員在不以 標准為基礎的情況進行此工作,所得到的 WSDL 和模式經常比從代碼生成的更為凌亂。另一 個問題非常現實。開發人員通常擁有實現某個功能的現有代碼,需要將其作為 Web 服務公開 ,而他們希望能夠在不用進行大量更改的情況下使用現有代碼。因此從代碼生成 WSDL 在可 預知的未來一段時間內將仍然可能是個需要考慮的問題。

如果希望使用更為強大的工具代替 Java2WSDL,可以嘗試我開發的 Jibx2Wsdl(有關更多 信息,請參見參考資料)。Jibx2Wsdl 可從提供的一個或多個服務類生成完整的 WSDL 綁定 、模式綁定和 JiBX 綁定定義。它支持 Java 5 枚舉和通用集合,並同時保留了與舊版本 Java 虛擬機(Java Virtual Machine,JVM)的兼容性,可自動從 Java 源文件將 Javadoc 作為生成的 WSDL 和模式定義的文檔導出。Jibx2Wsdl 還提供了廣泛的自定義機制來控制服 務和 XML 表示形式從 Java 類派生的方式,其中甚至允許將 Java 5 之前的集合與類型化數 據一起使用。盡管 Jibx2Wsdl 專門設計為通過 JiBX 數據綁定框架(也是我創建的)簡化將 現有類作為 Web 服務部署的工作,但生成的 WSDL 和模式都是獨立於數據綁定的。可以將其 與其他 Java 數據綁定框架甚至其他平台一起使用——生成所需的一切對象,然 後去掉 JiBX 綁定並保留其他部分即可。

如果您使用 Java 5 或更高版本,則另一個備選方案是使用 Java Architecture for XML Binding (JAXB) 2.0 和 Java API for XML Web Services (JAX-WS) Annotation 來將數據 對象和服務類作為 Web 服務公開。這些 Annotation 並不提供與 Jibx2Wsdl 相同級別的自 定義,但其允許直接在源代碼中嵌入配置信息,有些開發人員很喜歡這樣做。Axis2 的 1.2 版為 JAXB 2.0 和 JAX-WS 提供了試驗支持,這而且會在將來的版本中進一步改進。 Jibx2Wsdl 以後的版本也可能支持使用 JAXB 2.0 和 JAX-WS Annotation 進行自定義。(本 系列後續文章中將更為深入地討論 JAXB 2.0 和 JAX-WS,請關注關於從代碼生成 WSDL 的這 個主題。)

數據綁定的比較

Axis2(對於 1.2 版)完全支持三種數據綁定備選方案,而且目前正在進行添加更多綁定 支持的工作。本文將對使用三種完全受支持的數據綁定框架的示例代碼進行比較,並討論每 個框架與 Axis2 一起使用的一些優缺點。

清單 4 中所示的示例代碼(也包括在下載部分的示例下載中)用於圖書館服務,該服務 維護按主題類型整理的書籍集合。在此服務上定義了多個操作,包括:

getBook

getTypes

addBook

getBooksByType

清單 4 提供了此服務的 WSDL 的部分內容,僅僅顯示了 getBook 操作中涉及的部分。

清單 4. 圖書館服務的 WSDL<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
  xmlns:wns="http://ws.sosnoski.com/library/wsdl"
  xmlns:tns="http://ws.sosnoski.com/library/types"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
 <wsdl:types>

  <schema elementFormDefault="qualified"
    targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns="http://www.w3.org/2001/XMLSchema">

   <import namespace="http://ws.sosnoski.com/library/types"/>

   <element name="getBook">
    <complexType>
     <sequence>
      <element name="isbn" type="string"/>
     </sequence>
    </complexType>
   </element>

   <element name="getBookResponse">
    <complexType>
     <sequence>
      <element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
     </sequence>
    </complexType>
   </element>
   ...

  </schema>

  <schema elementFormDefault="qualified"
    targetNamespace="http://ws.sosnoski.com/library/types"
    xmlns="http://www.w3.org/2001/XMLSchema">

   <complexType name="BookInformation">
    <sequence>
     <element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
     <element name="title" type="string"/>
    </sequence>
    <attribute name="type" use="required" type="string"/>
    <attribute name="isbn" use="required" type="string"/>
   </complexType>
   ...

  </schema>
 </wsdl:types>
 <wsdl:message name="getBookRequest">
  <wsdl:part element="wns:getBook" name="parameters"/>
 </wsdl:message>
 <wsdl:message name="getBookResponse">
  <wsdl:part element="wns:getBookResponse" name="parameters"/>
 </wsdl:message>
 ...

 <wsdl:portType name="Library">
  <wsdl:operation name="getBook">
   <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
   <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
  </wsdl:operation>
  ...
 </wsdl:portType>
 <wsdl:binding name="LibrarySoapBinding" type="wns:Library">
  <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="getBook">

   <wsdlsoap:operation soapAction="urn:getBook"/>

   <wsdl:input name="getBookRequest">
    <wsdlsoap:body use="literal"/>
   </wsdl:input>

   <wsdl:output name="getBookResponse">
    <wsdlsoap:body use="literal"/>vi
   </wsdl:output>

  </wsdl:operation>
  ...
 </wsdl:binding>
 ...
</wsdl:definitions>

實際的服務實現代碼非常簡單,即采用硬編碼書籍清單填充圖書館實例。客戶機代碼按照 以下順序執行一系列查詢:

一個 getBook

一個 getTypes

兩個 addBook,第二個返回嘗試添加重復書籍 ID 的 SOAP 錯誤

一個 getBooksByType

示例間的實現細節有所差別,因為每個示例使用適合於其數據綁定的數據對象。除非專門 說明,否則所顯示的所有代碼對 Axis2 1.1.1 和 1.2 都完全一樣。Axis2 1.3 版(撰寫本 文時正在進行開發)要求對代碼進行一些小的更改,因為其中與服務錯誤對應的生成異常類 的命令發生了變化。提供了兩個版本的代碼供下載(請參見下載部分)。

在本文中,我們將僅討論客戶機代碼,不過提供的下載(請參見下載部分)包括了所有示 例的客戶機和服務器代碼以及 Ant 構建文件。接下來,讓我們分析三個數據綁定框架對應的 客戶機代碼,了解每種方法的優缺點。

Axis2 數據綁定

ADB 是 Axis2 的數據綁定擴展。與其他數據綁定框架不同,ADB 代碼僅可用於 Axis2 Web 服務。這個限制是 ADB 的一大局限,但也帶來了一些好處。由於 ADB 與 Axis2 進行了 集成,因此其代碼可針對 Axis2 要求進行優化。這方面的一個例子就是,ADB 構建於位於 Axis2 核心的 AXis 對象模型(AXis Object Model,AXIOM)文檔模型(我們已在本系列的 前一篇文章中對此進行了討論)之上。ADB 還提供了一些目前其他數據綁定框架所沒有的增 強功能,包括自動附件處理。WSDL2Java 提供了對 ADB 代碼生成的全面支持,其中包括生成 與 XML 模式組件對應的數據模型類。

ADB 模式支持具有一定的局限。在當前的 Axis2 1.2 版本中,這些局限包括模式功能, 如使用 maxOccurs="unbounded" 的組合器、使用 attributeFormDefault="qualified" 的模式定義和其他一些類似的變體。但 Axis2 1.2 ADB 模式支持比 Axis2 1.1 版要好得多,而且此支持 Axis2 框架的每個發布版 本將逐步改進,直到支持所有主要模式功能為止。

ADB 代碼生成的基本形式是使用直接模型,其中包含與每個操作使用的輸入與輸出消息對 應的獨立類。清單 5 顯示了使用此基本 ADB 代碼生成模式的示例應用程序的客戶機代碼中 最有意義的部分。此客戶機代碼說明了與 ADB 生成的類的交互,如用作 getBook() 方法調 用的參數的 GetBookDocument 和 GetBookDocument.GetBook 類以及用於從此調用獲取返回 結果的 GetBookResponseDocument 和 BookInformation 類。

清單 5. ADB 客戶機代碼

// create the client stub
AdbLibraryStub stub = new AdbLibraryStub(target);
// retrieve a book directly
String isbn = "0061020052";
GetBook gb = new GetBook();
gb.setIsbn(isbn);
GetBookResponse gbr = stub.getBook(gb);
BookInformation book = gbr.getGetBookReturn();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
// retrieve the list of types defined
GetTypesResponse gtr = stub.getTypes(new GetTypes());
TypeInformation[] types = gtr.getGetTypesReturn();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBook ab = new AddBook();
  ab.setType("scifi");
  ab.setAuthor(new String[] { "Cook, Glen" });
  ab.setIsbn(isbn);
  ab.setTitle(title);
  stub.addBook(ab);
  System.out.println("Added '" + title + '\'');
  ab.setTitle("This Should Not Work");
  stub.addBook(ab);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
// create a callback instance
CallbackHandler cb = new CallbackHandler();
// retrieve all books of a type asynchronously
GetBooksByType gbbt = new GetBooksByType();
gbbt.setType("scifi");
stub.startgetBooksByType(gbbt, cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  BookInformation[] books = cb.m_response.getGetBooksByTypeReturn();
  ...

清單 5 使用利用 -u WSDL2Java 選項生成的代碼,該代碼特定於 ADB。通過使用此選項 ,將為每個消息和數據模型類生成獨立的 Java 源文件;如果未 使用此選項,ADB 代碼生成 操作會將所有這些類作為生成的存根的靜態內部類生成。使用獨立的類要方便得多,因此, 如果使用 ADB,則應該使用 -u 選項。

代碼生成的直接形式會導致為每個操作的輸入和輸出生成大量的獨立類(不會受到 -u 選 項的影響,此選項僅僅以不同的方式組織相同的類而已)。這些生成的消息類經常包含極少 (對於 GetTypes 類,甚至沒有)有用數據,但生成的消息簽名需要這些類。幸運的是,還 有適用於很多常見服務的備用代碼生成形式。

ADB 取消包裝

Web 服務經常以方法調用形式基於現有編程 API 開發,在此情況下,將現有 API 嵌入到 Web 服務內的做法將非常方便。這種做法非常簡單;為服務定義的操作(如果要深究的話, 從技術上是為端口類型定義的)實際上等效於接口定義中的方法調用。唯一的主要區別在於 ,服務將輸入和輸出定義為 XML 消息,而不是調用參數和返回值。因此,為了在 Web 服務 定義中嵌入現有 API,您只需要約定如何將調用參數和返回值表示為 XML 消息結構即可。

幸運的是,Microsoft® 早期就確立了此領域的一個約定,為其他人節約了建立自己 約定的時間。此約定稱為 wrapped document/literal,是 .NET 在將方法調用作為 Web 服 務操作公開時使用的缺省表示形式。實際上,此包裝方法規定每個輸入消息都是僅包含子元 素序列的 XML 元素,而每個輸出消息都是具有單個子元素的 XML 元素。除了完全 .NET 互 操作性外,Microsoft 實現還有一些其他並不重要的技術細節,但用於圖書館示例(請參見 清單 4 中給出的部分代碼)的消息並不是為了這些細節而采用此方法。

WSDL2Java 支持在 ADB 代碼生成中對此類 wrapped doc/lit 服務進行取消包裝操作。當 對合適的 WSDL 服務定義使用取消包裝操作時,生成的客戶機存根(以及服務器代碼框架) 將更為簡單和直接。清單 6 顯示了與清單 5 等效的客戶機應用程序代碼,不過其中向 WSDL2Java 傳遞了 -uw 參數,以生成取消包裝接口。清單 5 中增加的復雜性層次的消息類 幾乎都從清單 6 中去掉了(除了 GetTypes 類),服務方法直接接受參數和返回值,而不是 嵌入在消息類中。實際上,ADB 仍然生成消息類,並在生成的代碼中使用這些類,但代碼通 常會忽略這些類,而直接使用數據。

清單 6. ADB 取消包裝客戶機代碼

// create the client stub
AdbUnwrapLibraryStub stub = new AdbUnwrapLibraryStub(target);
// retrieve a book directly
String isbn = "0061020052";
BookInformation book = stub.getBook(isbn);
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
// retrieve the list of types defined
TypeInformation[] types = stub.getTypes(new GetTypes());
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  stub.addBook("scifi", isbn, new String[] { "Cook, Glen" }, title);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  stub.addBook("xml", isbn, new String[] { "Nobody, Ima" }, title);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
// retrieve all books of a type asynchronously
stub.startgetBooksByType("scifi", cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_books != null) {
  BookInformation[] books = cb.m_books;
  ...

ADB 取消包裝支持的主要問題在於其尚不完全穩定。清單 6 中的代碼適合與 Axis2 1.2 一起使用,其中包含了相對於 Axis2 1.1.1 中使用的 ADB 取消包裝進行的幾項大改進。但 WSDL2Java 工具要求修改與其他示例使用的 WSDL 文檔的結構,以便在此示例中使用,即將 數據類的內聯模式移動到獨立的模式文檔中。最重要的是,清單 6 中所示的代碼並不能全部 都正常工作;代碼的最後一部分(即使用異步操作的部分)在運行時出現錯誤,由於生成的 ADB 客戶機存根代碼中的錯誤引發了類強制轉換異常。

Axis2 1.2 的後續版本發布時,ADB 取消包裝問題應該得到了極大的消除。但 ADB 並不 是讓 Axis2 支持取消包裝的唯一數據綁定框架。JiBX 也提供取消包裝支持,JiBX 版本從 Axis2 1.1.1 發布以來就穩定了。可以在本文稍後(討論了 Axsi 2 數據綁定主要選項後) 了解 JiBX 客戶機代碼。

XMLBeans

XMLBeans 是包含數據綁定層的通用 XML 處理框架。其源自一個 BEA Systems 項目,後 來提交給了 Apache Foundation。XMLBeans 是 Axis2 支持的第一種數據綁定形式,並將繼 續作為與 Axis2 一起使用的熱門選項(特別是使用復雜模式定義時)。

清單 7 顯示了示例應用程序的 XMLBeans 客戶機代碼中最有意義的部分。對於基本(非 取消包裝)ADB 代碼,每個操作的輸入和輸出都有一個對應的獨立類。但 XMLBeans 與 ADB 並不相同,其中具有針對包含輸入或輸出類的文檔添加的類(例如,除了 GetBook 類外,還 有 GetBookDocument)。其直接效果就是在使用 XMLBeans 代替 ADB 時會添加一個對象創建 層。Axis2 中沒有為 XMLBeans 提供取消包裝支持,因此沒有辦法避免這個添加的對象層。 所得到的結果是 XMLBeans 生成的類比其他數據綁定框架的對等項使用更為復雜。

清單 7. XMLBeans 客戶機代碼

// create the client stub
XmlbeansLibraryStub stub = new XmlbeansLibraryStub(target);
// retrieve a book directly
String isbn = "0061020052";
GetBookDocument gbd = GetBookDocument.Factory.newInstance();
GetBookDocument.GetBook gb = gbd.addNewGetBook();
gb.setIsbn(isbn);
gbd.setGetBook(gb);
GetBookResponseDocument gbrd = stub.getBook(gbd);
BookInformation book = gbrd.getGetBookResponse().getGetBookReturn();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
// retrieve the list of types defined
GetTypesDocument gtd = GetTypesDocument.Factory.newInstance();
gtd.addNewGetTypes();
GetTypesResponseDocument gtrd = stub.getTypes(gtd);
TypeInformation[] types =
  gtrd.getGetTypesResponse().getGetTypesReturnArray();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBookDocument abd = AddBookDocument.Factory.newInstance();
  AddBookDocument.AddBook ab = abd.addNewAddBook();
  ab.setAuthorArray(new String[] { "Cook, Glen" });
  ab.setIsbn(isbn);
  ab.setTitle(title);
  ab.setType("scifi");
  stub.addBook(abd);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  ab.setTitle(title);
  stub.addBook(abd);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getAddDuplicate().getBook().getTitle() +
    '\'');
}
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
// retrieve all books of a type asynchronously
GetBooksByTypeDocument gbtd =
  GetBooksByTypeDocument.Factory.newInstance();
gbtd.addNewGetBooksByType().setType("scifi");
stub.startgetBooksByType(gbtd, cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  BookInformation[] books =
    cb.m_response.getGetBooksByTypeResponse().getGetBooksByTypeReturnArray();
  ...

清單 7 中的客戶和對應的服務器代碼可在 Axis2 1.1.1 下正常執行,但由於1.2 版中針 對 XMLBeans 的錯誤處理代碼生成存在問題,會在添加重復書籍 ID 時遇到意外異常,從而 失敗。這個問題應該已經在下一個 Axis2 版本中得到解決。

盡管 XMLBeans 聲稱 100% 支持 XML 模式,但這個說法的准確性有待商榷。對於幾乎任 何模式構造,XMLBeans 都生成了一組能用於讀寫匹配此模式的文檔。但與本文討論的其他數 據綁定框架不同的是,XMLBeans 缺省情況下並不進行任何工作來執行模式。例如,如果將清 單 7 代碼中添加的設置書籍標題的代碼行注釋掉,XMLBeans 仍然能正常讀寫缺少所需的 <title> 元素(因而無效)的文檔。清單 8 顯示了此代碼更改,並給出了發送到服務 器進行添加請求的 XML 以及檢索書籍時從服務器返回的 XML。對於檢索響應,XML 文檔包括 <title> 元素,但使用了 xsi:nil="true" 屬性,這在模式中是不允許的 ,因此同樣也無效。

清單 8. XMLBeans 客戶機和無效文檔

AddBookDocument abd = AddBookDocument.Factory.newInstance();
   AddBookDocument.AddBook ab = abd.addNewAddBook();
   ab.addAuthor("Cook, Glen");
   ab.setIsbn(isbn);
   ab.setType("scifi");
//      ab.setTitle(title);
   System.out.println("Validate returned " + abd.validate());
   stub.addBook(abd);
   ...
 <addBook xmlns="http://ws.sosnoski.com/library/wsdl">
  <type>scifi</type>
  <isbn>0445203498</isbn>
  <author>Cook, Glen</author>
 </addBook>

 <getBooksByTypeResponse xmlns="http://ws.sosnoski.com/library/wsdl">
  ...
  <getBooksByTypeReturn isbn="0445203498" type="scifi">
  <author xmlns="http://ws.sosnoski.com/library/types">Cook, Glen</author>
  <title xmlns="http://ws.sosnoski.com/library/types" xmlns:xsi="http://www.w3.org/2001
   /XMLSchema-instance" xsi:nil="true"/>
  </getBooksByTypeReturn>
 </getBooksByTypeResponse>

這是未設置所需值的一個簡單示例。對於更為復雜的模式,XMLBeans 生成的 API 可能會 隱藏更多的缺陷。XMLBeans 用戶郵件列表的最近一次討論曾談及這樣的情況,即必須將值添 加到兩個不同的列表,以更改生成正確輸出的順序。因此 XMLBeans 要求開發人員既要了解 模式,還要了解生成的代碼如何與模式相關,以確保應用程序代碼構建有效的 XML 文檔。數 據綁定框架的一個主要好處是,通常能夠對開發人員隱藏模式的這些細節,而 XMLBeans 顯 然在這方面做得並不好。

可以通過調用生成類中包括的 validate() 方法來避免 XMLBeans 處理和生成無效 XML 文檔的問題。如果使用 XMLBeans,則應至少在測試和開發期間使用此方法來檢查所有文檔。 不過,驗證對性能具有很大的影響(正如您在下一篇文章中將看到的,即使不對每個文檔調 用 validate(),XMLBeans 也已經非常慢了),因此很多應用程序都應該在生產部署中避免 驗證開銷。就結果信息而言,驗證也具有相當的局限性。為了避免導致驗證錯誤,應該對出 現錯誤的文檔運行獨立的模式驗證。

JiBX

JiBX(我自己開發的)是主要側重使用現有 Java 類(而不是從模式進行代碼生成)的數 據綁定框架。對於 JiBX,要首先創建綁定定義,以定義 Java 數據對象與 XML 之間如何轉 換,然後使用可通過添加實現轉換的方法(作為字節碼)來增強數據類文件的工具對該綁定 進行編譯。JiBX 運行時框架將隨後使用這些添加的方法來在數據和 XML 之間進行轉換。

JiBX 方法提供了一些獨有的優勢,也有自己獨有的缺點。就好的一面而言,JIBX 可讓您 在 Web 服務接口添加到現有服務代碼的情況下直接使用現有類。Jibx2Wsdl 工具特別適合這 種用途,因為它會生成所需的所有內容,從而方便地將現有代碼作為 Axis2 服務部署。可以 在使用單個數據模型的情況下為相同的類定義不同的綁定,以同時用於文檔的不同 XML 版本 。通過修改綁定,甚至通常能在重構數據類的情況下保持相同的 XML 表示形式。

清單 9 顯示了 JiBX 客戶機代碼中有意義的部分,其中使用了匹配消息元素的類。此代 碼與清單 5 中所示的 ADB 對等項類似,因此這裡就不詳細討論了。唯一值得注意的差異是 ,由於數據類和消息類都在用戶的控制之下,因此可以方便地向通過 JiBX 使用的類添加常 規構造函數(如 AddBookRequest 的情況)和其他支持方法。

清單 9. JIBX 客戶機代碼

// create the server instance
JibxLibraryStub stub = new JibxLibraryStub(target);
// retrieve a book directly
String isbn = "0061020052";
GetBookResponse bresp = stub.getBook(new GetBookRequest(isbn));
Book book = bresp.getBook();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
isbn = "9999999999";
bresp = stub.getBook(new GetBookRequest(isbn));
book = bresp.getBook();
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
// retrieve the list of types defined
GetTypesResponse tresp = stub.getTypes(new GetTypesRequest());
Type[] types = tresp.getTypes();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  AddBookRequest abr = new AddBookRequest("scifi", isbn, title,
    new String[] { "Cook, Glen" });
  stub.addBook(abr);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  abr = new AddBookRequest("scifi", isbn, title,
    new String[] { "Nobody, Ima" });
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
  "' with ISBN '" + isbn + "' - matches existing title '" +
  e.getFaultMessage().getBook().getTitle() + '\'');
}
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
// retrieve all books of a type asynchronously
stub.startgetBooksByType(new GetBooksByTypeRequest("scifi"), cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_response != null) {
  Book[] books = cb.m_response.getBooks();

清單 10 顯示了對等的 JiBX 取消包裝代碼。和 ADB 取消包裝代碼類似,理解和使用服 務調用的取消包裝形式比使用直接代碼簡單得多。JiBX 和 ADB 版本唯一重要的區別在於, 對於 JiBX,並不需要在不傳遞值時創建對象,而 getTypes() 調用的 ADB 版本則要求進行 此工作。JiBX 取消包裝支持也比 ADB 版本更為穩定一些,因為從 Axis2 1.1.1 起就提供了 全面支持。

清單 10. JIBX 取消包裝客戶機代碼

// create the server instance
JibxUnwrapLibraryStub stub = new JibxUnwrapLibraryStub(target);
// retrieve a book directly
String isbn = "0061020052";
Book book = stub.getBook(isbn);
if (book == null) {
  System.out.println("No book found with ISBN '" + isbn + '\'');
} else {
  System.out.println("Retrieved '" + book.getTitle() + '\'');
}
// retrieve the list of types defined
Type[] types = stub.getTypes();
System.out.println("Retrieved " + types.length + " types:");
for (int i = 0; i < types.length; i++) {
  System.out.println(" '" + types[i].getName() + "' with " +
    types[i].getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
  stub.addBook("scifi", isbn, new String[] { "Cook, Glen" }, title);
  System.out.println("Added '" + title + '\'');
  title = "This Should Not Work";
  stub.addBook("xml", isbn, new String[] { "Nobody, Ima" }, title);
  System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFaultException e) {
  System.out.println("Failed adding '" + title +
    "' with ISBN '" + isbn + "' - matches existing title '" +
    e.getFaultMessage().getBook().getTitle() + '\'');
}
// create a callback instance
BooksByTypeCallback cb = new BooksByTypeCallback();
// retrieve all books of a type asynchronously
stub.startgetBooksByType("scifi", cb);
long start = System.currentTimeMillis();
synchronized (cb) {
  while (!cb.m_done) {
    try {
      cb.wait(100L);
    } catch (Exception e) {}
  }
}
System.out.println("Asynchronous operation took " +
  (System.currentTimeMillis()-start) + " millis");
if (cb.m_books != null) {
  Book[] books = cb.m_books;

JiBX 取消包裝支持在使用的類方面也與 ADB 有差別。當使用 ADB 取消包裝時,所有消 息元素的類仍然在後台生成和使用。對於 JiBX,使用直接處理時必須將類定義為與消息元素 對應,如清單 9 中所示;對於取消包裝處理,只有作為值傳遞的類需要在綁定定義中加以定 義和包括。無論采用哪種方法,JiBX 綁定定義都需要在運行 Axis2 WSDL2Java 工具前創建 ,而且需要使用 -Ebindingfile 命令行參數傳入。

JiBX 綁定方法最大的缺陷(至少從 Web 服務方面可以這樣說)可能就是,JiBX 目前對 從 XML 模式定義工作的支持非常微弱。不過,即使是 Xsd2Jibx 工具提供的這個微弱支持, 也尚未集成到 Axis2 WSDL2Java 代碼生成中。這意味著需要在運行 WSDL2Java 生成 Axis2 鏈接代碼前創建 Java 數據類和綁定定義。JiBX 所需的字節碼增強步驟在某些環境中也可能 出現問題,因為通常需要在應用程序構建時進行此工作,可在類中得到沒有源代碼可用的代 碼。

正如前面所提到的,JiBX 數據綁定提供了一些獨特的好處。就 Axis2 使用而言,JiBX 也提供了優於其他框架的優勢,即支持能夠插入到 Axis2 版本中糾正發布之後發現的錯誤的 錯誤修復程序版本(有關詳細信息,請參見參考資料部分的“獲得相關產品和技術 ”)。對於其他框架,獲取錯誤修復程序的唯一方式就是遷移到 Axis2 的更高版本, 而這樣通常會帶來其他問題。預計將來 JiBX 將提供穩定的從模式生成代碼和綁定的支持。 到提供此功能的那一天,就可以將 JiBX 視為 Axis2 的優秀全能數據綁定備選方案了。到那 時,使用現有 Java 代碼的做法將是最佳的,而 Jibx2Wsdl 工具恰恰在這方面提供了出色的 支持。

總結

Axis2 目前提供對三種不同數據綁定框架的全面支持:

ADB 專門為 Axis2 設計,僅能用於 Axis2 環境中。對於 Axis2 1.3 版,提供了對從模 式生成代碼的良好支持(並在不斷改進)。另外還支持常規取消包裝服務方法和自動附件處 理,從而使其成為了從現有 WSDL 服務定義著手時的首要選項。

XMLBeans 提供對在生成的 Java 代碼中建模模式結構的最全面支持。但其會創建給定模 式的最為復雜的 Java 模型,並不支持使用取消包裝服務方法來實現更為簡單的接口。缺省 情況下,甚至不會對輸入或輸出 XML 文檔執行基本模式結構規則,從而很可能會創建無效的 XML 文檔或接受此類文檔進行處理。

JiBX 是唯一支持使用現有 Java 類的備選方案。另外還提供了對取消包裝服務方法出色 的支持。但集成到 Axis2 中的 JiBX 支持並不處理從模式生成代碼的情況,甚至 JiBX 工具 所提供的從模式生成代碼的獨立支持目前還僅限於模式支持。

Axis2 提供了這些數據綁定框架間的選擇,這非常好,因為並沒有那一個選項能滿足所有 需求。將來 Axis2 還將提供對 JAXB 2.0 的全面支持,我將在本系列關於相關 JAX-WS 2.0 的後續文章中對此進行討論。了解每個框架的優缺點可幫助您根據自己的需求作出最適當的 選擇,並在投入生產前了解潛在的問題區域。在本系列的下一篇文章中,我們將了解 Axis2 數據綁定框架比較的另一個方面:性能。

來源:http://www.ibm.com/developerworks/cn/webservices/ws-java3/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved