描述 Web 服務:WSDL

Web 服務描述語言(Web Services Description Language,WSDL)是一種基於 XML 的格式,它正在成爲一種業界標準,用於以與實現無關的方式描述 Web 服務。將創建和使用 Web 服務的許多不同的方法結合起來,是一種常見的思路。本教程將讓您牢固掌握 WSDL 的用途和機制,以及在使 Web 服務變得可互操作的工作中,它如何起到至關重要的作用。

本教程包括了幾個實驗,它們使用 IBM WebSphere SDK for Web Services V5(WSDK)僅從一個 WSDL 文件開始創建 Web 服務和客戶機。我們將向您說明 WSDK 中可用的高級工具如何能幫助您在使用 WSDL 時提高 Web 服務的開發速度。

學習本教程所必備的知識

在本教程中,假設讀者已經順利學完了教程:“Web 服務和 WSDK 簡介(Introduction to Web services and the WSDK)”和“通過 Java 類創建 Web 服務(Creating a Web service from a Java class)”,或者讀者擁有同等水平的 Web 服務和 WSDK 知識。這意味着,您具備了 Web 服務的應用知識,並且可以使用 IBM WSDK 執行基本的操作,譬如啓動和停止服務器以及部署 Web 服務。本教程還假定您已具備了 Java 編程語言和 XML 的應用知識。J2EE 和 Ant 的知識會有所幫助,但不是必需的。

所有的示例應用程序將部署在同 WSDK 一起提供的嵌入式 IBM WebSphere Application Server 上。提供的 Ant 構建腳本可以使構建和部署這些示例應用程序更方便和更可靠。在本教程結尾處的參考資料一章中,提供了有關 Ant、Java 技術、XML、IBM WebSphere 和 J2EE 方面的入門參考材料的鏈接。

本教程包括的內容

本教程介紹了 WSDL 和 WSDK 所提供的工具,這些工具使用 WSDL 來開發基於 Web 服務的系統。其中將包含以下主題、工具和技術:

主題

  • WSDL 回顧。
  • WSDL 簡介。
  • WSDL 的 <types>、<message>、<portType>、<binding> 和 <service> 元素。
  • XML Schema 概述。
  • 消息傳遞和編碼樣式。

工具

  • WSDL2WebService。

技術

  • 通過 WSDL 文件創建 Web 服務實現。
  • 通過 WSDL 文件創建 Web 服務客戶機。

本教程中您所需的工具

最起碼您需要一個簡單的文本編輯器和一個 Java SDK 1.3.1(WSDK 中包含了它)或者更高版本,用於編譯示例。您還將需要 IBM WebSphere SDK for Web Services(WSDK)。可以下載 IBM WSDK 並獲取有關它的信息。您也可以索取免費的 CD,這些 CD 中包含 WSDK 和其它 IBM Web 服務現成產品(譬如來自 developerWorks Speed-start Web services 計劃的 WebSphere Studio)。WSDK 提供了它自己的 Java SDK 和一些用於啓動、停止和管理與 WSDK 一同提供的嵌入式 WebSphere Application Server 的批處理文件和程序。

雖然以手工方式構建和部署應用程序可能會有趣些,但教程中的所有示例都包含了 Ant 構建腳本,可以用這些腳本來構建和部署我們將創建的 Web 服務。在 Apache 的 Ant 主頁上可以找到 Ant。

因爲有許多 JAR 文件要管理,所以建議您使用集成開發環境(Interactive Development Environment,IDE)。我們使用 Eclipse 來構建樣本應用程序,Eclipse 是一個免費使用的 IDE。雖然 Eclipse 和 WebSphere Studio Application Developer(Application Developer)都不是必需的,但可以分別在 Eclipse 網站和 IBM WebSphere 開發人員專區上找到它們。

WSDL 簡介

簡介

Web 服務的引入已經在許多技術圈中引起了一陣興奮。這種興奮的緣由有許多;其中有兩個原因便是對互操作性和更快的上市時間的承諾。互操作性部分是通過使用常見的開放協議(比如 HTTP 和 SOAP)實現的。但是,這還不足以使服務器進程的實現對客戶機完全透明。

客戶機還必須知道 Web 服務的數據類型、參數、返回類型、位置和傳輸細節。還需要元協議,該協議可以用不特定於供應商和實現的方式描述這些基本要素,這樣客戶機和服務器就可能完全分離了。

Web 服務描述語言(WSDL)是基於 XML 的文件格式,它不但描述了這些 Web 服務接口的細節,而且描述了抽象接口是如何與給定的傳輸協議(HTTP 和 SMTP 等等)和編碼(SOAP 等等)結合在一起的。但是 WSDL 的優點遠不止這些來自互操作性的優點。WSDL 是一個 W3C 紀要(W3C Note)。通過綁定到規範,供應商不但能夠創建使用 WSDL 生成客戶機運行時代碼(這些代碼與 Web 服務進行交互)的工具,而且還可以創建使用 WSDL 生成服務器端模板代碼的工具。

而 WSDL 不但有助於完全不同的進程進行互操作,還有助於縮短開發支持 Web 服務的客戶機和服務器所需的時間。WSDK 附帶了WSDL2WebService 工具,該工具使用 WSDL,通過生成用於 Web 服務的客戶機運行時代碼和服務器模板代碼來提供幫助,使開發人員的項目能夠啓動並運行。

我們將更詳細地討論 WSDL2WebService 工具,在此之前首先研究一下 WSDL 的體系結構。

WSDL 體系結構

WSDL 提供了一種語法,用於將服務描述成一組交換消息的端點。WSDL 文檔充當一個或多個服務的(XML)描述,這種描述是與語言和平臺無關的。WSDL 文檔描述了這些服務、如何訪問它們以及期望的響應類型(如果有的話)。WSDL 描述了服務的類型、消息、操作、portType、位置和協議綁定。您使用 WSDL 將 Web 服務描述成一組對消息進行操作的端點。WSDL 可以描述面向文檔的信息,也可以描述面向過程的信息。在介紹特定於 WSDL 的元素之前,讓我們先介紹 WSDL 試圖描述的一些概念。

portType:portType 描述 Web 服務所提供的操作。它類似於一個 Java 接口,因爲它描述了一組操作。它將消息元素合併成操作。

message 和 types:message 是一個數據元素。操作用它來傳送該操作的數據。消息通過列出所交換的數據類型來描述客戶機和服務之間的通信。types 元素中描述了類型,通常用 XML Schema 完成描述。types 類似於 Java 類和基本類型。

operation、message 和 fault:operation 就像一個 Java 方法。它包含了傳入消息、傳出消息和出錯消息。您可以將操作的傳入消息當作是 Java 編程語言中方法的參數。可以將操作的傳出消息當作 Java 編程語言中方法的返回類型。可以將出錯消息當作 Java 異常。

binding:binding 將 portType 綁定到特定的協議(例如 SOAP 1.1、HTTP GET/POST 或 MIME)。

service:service 定義了某個特定綁定(binding)的連接信息。服務可以有一個或多個端口,每個端口都定義一個不同的連接方法(例如 HTTP / SMTP 等等)。

總而言之,一個 WSDL 協議實例描述五項內容:

  1. types,Web 服務接口的數據類型(其參數和返回類型)。
  2. message,將數據類型變量分組以進行網絡傳輸。
  3. portType,將消息分組成邏輯操作。
  4. binding,描述如何將 portType 映射成傳輸/消息傳遞協議。
  5. service,列出某個特定綁定的連接信息。

在接下來的幾頁中會用示例更加詳細地描述上面這些項中的每一項。

<types> 元素

<types> WSDL 元素允許您指定數據類型,無論這些類型多簡單或多複雜,這些類型是 WSDL 文件所描述的 Web 服務接口必需的。WSDL 紀要對於定義類型所採用的協議沒有提出任何要求,儘管它支持將 XML Schema 用於其規範的類型協議。XML Schema 很常見,而且是開放的,因此在 WSDL 中使用它使得 Web 服務定義不受到特定於編程語言的類型的約束,也不受到特定於供應商的類型的約束。

下面這個示例的 <types> 元素使用 XML Schema 來定義七種數據類型。正如您所看到的,XML Schema 非常靈活,允許您用一種開放和常見的方式定義具體針對您的問題領域的類型。現在可以在 WSDL 中引用這些類型來進一步描述 Web 服務接口。

                    <wsdl:definitions
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://dvd.com"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:tns="http://dvd.com">

 ...

 <wsdl:types>
  <xsd:schema>

   <xsd:simpleType name="EmailAddressType">
    <xsd:restriction base="xsd:string">
     <xsd:pattern value=".+@.+"/>
    </xsd:restriction>
   </xsd:simpleType>

   <xsd:simpleType name="PriceType">
    <xsd:restriction base="xsd:decimal">
     <xsd:totalDigits value="6"/>
     <xsd:fractionDigits value="2"/>
    </xsd:restriction>
   </xsd:simpleType>

   <xsd:simpleType name="SerialNumberType">
    <xsd:restriction base="xsd:string">
     <xsd:pattern value="[0-9a-zA-Z]{3}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{3}" />
    </xsd:restriction>
   </xsd:simpleType>

   <xsd:complexType name="DVDAbstractType">
    <xsd:sequence>
     <xsd:element name="title" type="xsd:string"/>
     <xsd:element name="artist" type="xsd:string"/>
     <xsd:element name="releaseDate" type="xsd:date"/>
    </xsd:sequence>
   </xsd:complexType>

   <xsd:complexType name="DVDPurchaseType">
    <xsd:complexContent>
     <xsd:extension base="tns:DVDAbstractType">
      <xsd:sequence>
       <xsd:element name="cost" type="tns:PriceType"/>
      </xsd:sequence>
     </xsd:extension>
    </xsd:complexContent>
   </xsd:complexType>

   <xsd:complexType name="DVDRentalType">
    <xsd:complexContent>
     <xsd:extension base="tns:DVDAbstractType">
      <xsd:sequence>
       <xsd:element name="rentalCost" type="tns:PriceType"/>
       <xsd:element name="returnDate" type="xsd:date" />
      </xsd:sequence>
     </xsd:extension>
    </xsd:complexContent>
   </xsd:complexType>

   <xsd:complexType name="ResponseConfirmationType">
    <xsd:sequence>
     <xsd:element name="confirmationNumber" type="tns:SerialNumberType" />
     <xsd:element name="message" type="xsd:string" />
    </xsd:sequence>
   </xsd:complexType>

  </xsd:schema>
 </wsdl:types>

 ...

</wsdl:definitions>

下一頁大致地描述了 XML Schema 的類型系統。

XML Schema

Web 服務全是關於進程間的通信;它可使完全不同的計算機進行通信。將 WSDL 和 WSDK 聯合起來使用,使您能夠更輕鬆地將 Web 服務集成到目前的 IT 基礎設施中,因爲 WSDK 的 WSDL2WebService 工具可以爲開發人員生成客戶機運行時代碼(如果需要的話)和服務器代碼。

當您研究 Web 服務的互操作特徵時,類型問題就出現了。例如,如果一臺服務器(用某種語言/技術編寫而成)想要把一個 numeric 發送給一臺客戶機(用另一種語言/技術編寫而成),那麼該服務器該如何告訴客戶機已發送數據類型的含義呢?這兩個進程對“numeric”的含義可能有不同的定義。客戶機如何得知它讀入的那幾個字節的一串字符不能被解釋成字符串或序列化對象,而要解析成一個數字呢?當您研究一個複雜類型(例如 DVD 和 Address 等等)時這個問題就更加明顯了。在複雜類型中不僅需要定義所包含的簡單類型,還需要定義包含這些簡單類型的類型。

XML Schema 爲 Web 服務提供了一種獲得業界認可的方式,用來定義複雜數據類型以及預先確定好的簡單數據類型(例如 string、date 和 numeric 等等)。遵循 XML Schema 規則的用戶定義的 XML 協議的描述通常位於 WSDL 文件中。

讓我們研究一下一些簡單的 XML Schema 示例。

<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>

<!-- Other stuff -->
      <xsd:element name='name' type='xsd:string'/>
      <xsd:element name='age' type='xsd:positiveInteger'/>

</xsd:schema>

在上面的示例中,類型“string”通過前綴“xsd”與名稱空間“http://www.w3.org/2001/XMLSchema”相關聯。解析器就是通過這種方法知道類型“string”可在 XML Schema 規範中找到的。這些類型很容易理解,下面也列出了這些類型。

複雜類型是簡單類型和/或其它複雜類型的特性的命名聚集。例如,研究一下類型 DVD,該類型的特性有:title、artist、releaseDate、listPrice 和 price。DVD 實例的 XML 表示可能類似於這樣:

                    <DVD ID="1232232">
 <title>Greatest Hits of the 80s</title>
 <artist>To Be AnnouncedVarious</artist>
 <releaseDate>1980-01-01</releaseDate>
 <listPrice>12.00</listPrice>
 <price>3.00</price>
</DVD>

XML Schema 給我們提供了一種描述該 XML 文檔所遵循的協議或模式的方式。和基於 DTD 的描述不同的是,XML Schema 是基於 XML 的,幾乎是完全可擴展的。上述實例的 XML Schema 文檔可能是:

                    <xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
 <xsd:element name="DVD">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="title" type="xsd:string" minOccurs="1" maxOccurs="1" />
    <xsd:element name="artist" type="xsd:string" minOccurs="1" maxOccurs="1" />
    <xsd:element name="releaseDate" xsd:type="date" minOccurs="1" maxOccurs="1" />
    <xsd:element name="listPrice" type="xsd:string" minOccurs="1" maxOccurs="1" />
    <xsd:element name="price" type="xsd:string" minOccurs="1" maxOccurs="1" />
   </xsd:sequence>
  <xsd:attribute name="ID" type="xsd:string"/>
  </xsd:complexType>
 </xsd:element>
</xsd:schema>
  • schema 元素是 XML Schema 文檔的根元素。XML 協議的所有元素類型和屬性類型的定義都在其中。
  • element 元素允許我們定義元素的類型。
  • complexType 元素定義了一個複雜類型。如果一個元素有屬性和/或子元素,那麼就認爲該元素的類型是複雜類型。DVD 類型既有屬性又有子元素。元素的子元素可以遵循三種模型:any、choice 和 sequence。
    1. 類型 any 意味着任何嵌套內容都可以以任何順序顯示成父節點的子節點。
    2. 類型 choice 意味着只可以使用一組可能的子元素中的一個。
    3. 最後,sequence 意味着子元素在父元素中的顯示順序必須和它們在 sequence 元素中的順序相同。
  • 在上面的案例中,由於使用了 sequence,因此 DVD 的子元素必須按這樣的順序顯示:title、artist、releaseDate、listPrice,然後是 price。attribute 元素指出在該協議中 DVD 元素有一個 ID 屬性。

雖然使用複雜類型來驗證並記錄 SOAP 流所包含的內容,但是 SOAP 流本身並沒有包含任何用複雜類型定義的 XML Schema 數據。只有想要在 WSDL 文件中讀取或創建自己的類型的時候,纔有必要知道如何用 XML Schema 定義複雜類型。

實際上,您不必真正徹底地瞭解 Web 服務中的類型和類型定義的主題纔去編寫 Web 服務系統。大多數開發人員所利用的存根/調用 API 和框架/助手代碼層負責有關對客戶機和服務器之間的數據進行序列化和反序列化的低級細節。這使得整個類型轉換過程對於開發人員是透明的。但是,如果您希望讀懂 WSDL 文件,或者從頭開始編寫它們,那麼熟知 XML Schema 是必要的。

XML Schema 建議書定義了許多簡單的數據類型(請參閱下表)。“簡單”指的是標量/名-值對。例如 12334、“hello”、123.322 和 12:12:2005 等等。它們有一個共同點 - 很容易將它們描述成字符串 - 也就是說,它們都沒有複雜結構。但是,其中有些確實具有結構。例如日期(date)類型具有把年月日分開的分隔符。簡單類型有擴展/限制語法,它允許您使用 XML Schema 簡單類型作爲基類型來創建/定義您自己的簡單類型。請查詢 XML Schema 規範以獲取更多信息。

string 任意的字符序列。
normalizedString 任何字符序列,其中允許的空白字符只有空格。
token 任何字符序列,其中允許的空白都是單個的空格(也就是說,兩個或更多空格連在一起是非法的)。
Name 和 XML 名稱相同。
QName 具有名稱空間前綴部分、分號和自身名稱部分的 XML 名稱。
NMTOKEN 和 XML NMTOKEN 相同。
NMTOKENS 和 XML NMTOKEN 相同。
byte -128 到 127 之間的整數(包括 -128 和 127)。
unsignedByte 0 到 255 之間的整數(包括 0 和 255)。
base64Binary 任意長度的用 Base64 編碼的數據流。
hexBinary 任意長度的用十六進制編碼的數據流。
int -2147483648 到 2147483647 之間的整數(包括 -2147483648 和 2147483647)。
unsignedInt 0 到 4294967295 之間的整數(包括 0 和 4294967295)。
integer 任意整數。
positiveInteger 1 到正無窮大之間的整數(包括 1)。
negativeInteger 負無窮大到 -1 之間的整數(包括 -1)。
nonNegativeInteger 0 到正無窮大之間的整數(包括 0)。
nonPositiveInteger 負無窮大到 0 之間的整數(包括 0)。
long -9223372036854775808 到 9223372036854775807 之間的整數(包括 -9223372036854775808 和 9223372036854775807)。
unsignedLong 0 到 18446744073709551615 之間的整數(包括 0 和 18446744073709551615)。
short -32768 到 32767 之間的整數(包括 -32768 和 32767)。
unsignedShort 0 到 65535 之間的整數(包括 0 和 65535)。
decimal 任意帶小數點的數。
float 任何 IEEE 單精度 32 位浮點類型 [IEEE 754-1985] 值。
double 任何 IEEE 單精度 64 位浮點類型 [IEEE 754-1985] 值。
boolean 值爲 true 或 false
time 一天中的某個時刻,格式爲 hh:mm:ss.sss
dateTime 某一時刻,格式爲 CCYY-MM-DDThh:mm:ss
duration PnYnMnDTnHnMnS 格式的時間段,其中“T”是日期/時間的分隔符。允許在前面加上可選的“-”表示已經過去的時期。
date CCYY-MM-DD 格式的某一時刻。
anyURI 任何符合 URI 規則的字符串。

<message> 元素

<message> 元素將數據(數據類型在 <types> 元素中進行定義)分組成一個用於邏輯網絡傳輸的特徵符,並將數據綁定到一個名稱上。該名稱用來在操作定義的上下文中引用 <message>。<message> 中的每個數據實例都在 <part> 元素中進行了聲明。請參閱下面的圖和示例。

                    <wsdl:definitions
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 ...>

  ...

 <wsdl:message name="Subscribe">
  <wsdl:part name="email" type="tns:EmailAddressType"/>
  <wsdl:part name="until" type="xsd:date"/>
 </wsdl:message>

 <wsdl:message name="PurchaseResponse">
  <wsdl:part name="paymentResponse" type="tns:ResponseConfirmationType" />
  <wsdl:part name="purchaseDate" type="xsd:date" />
 </wsdl:message>

 <wsdl:message name="PurchaseRequest">
  <wsdl:part name="purchaseRequest" type="tns:DVDPurchaseType" />
 </wsdl:message>

 <wsdl:message name="RentalRequest">
  <wsdl:part name="rentalRequest" type="tns:DVDRentalType" />
 </wsdl:message>

 <wsdl:message name="PaymentFault">
  <wsdl:part name="errorCode" type="tns:SerialNumberType"/>
 </wsdl:message>

 ...

</wsdl:definitions>

<portType> 元素

<portType> 元素在 WSDL 中相當於 Java 中的接口。它將對 <message> 元素的引用分組成邏輯操作,一個進程可以對另一個進程執行這些操作,並將它們綁定到一個名稱。<operation> 可以包含 <input>、<output> 和 <fault> 元素。對於所有這三個元素,message 屬性引用 WSDL 文件中所定義的 <message> 元素。<input> 元素聲明客戶機向 Web 服務請求傳輸的需求。<output> 聲明 Web 服務響應的內容。<fault> 元素描述當 Web 服務設法響應客戶機的請求時所發生的任何消息級異常。請參閱下面的圖和示例。

                    <wsdl:definitions
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 ...>

...

 <wsdl:portType name="DVDRenting">
  <wsdl:operation name="SubscribeToSpecialsAlertList">
   <wsdl:input message="tns:Subscribe" />
  </wsdl:operation>

  <wsdl:operation name="RentDVD">
   <wsdl:input message="tns:RentalRequest" />
   <wsdl:output message="tns:PurchaseResponse" />
   <wsdl:fault name="tns:RentFault" message="tns:PaymentFault" />
  </wsdl:operation>
 </wsdl:portType>

 <wsdl:portType name="DVDPurchasing">
  <wsdl:operation name="BuyDVD">
   <wsdl:input message="tns:PurchaseRequest" />
   <wsdl:output message="tns:PurchaseResponse" />
   <wsdl:fault name="tns:PurchaseFault" message="tns:PaymentFault" />
  </wsdl:operation>
 </wsdl:portType>

 ...

</wsdl:definitions>

<binding> 元素

就某個具體的傳輸或消息傳遞協議而言(即 SOAP、SMTP 和 HTTP 等等),到目前爲止所列出的 WSDL 元素都是抽象的。使用任何協議的組織都可以實現上面所述的 Web 服務接口。WSDL <binding> 元素的內容將這些抽象的鉤接點與 Web 協議束縛起來。binding 元素既有 name 屬性(用來在 WSDL 文檔內標識該綁定),又有 type 屬性(用來引用該元素描述綁定時所針對的 portType)。它還有 <operation> 元素,與它所綁定的 <portType> 中的每個 <operation> 元素對應。在 <operation> 元素又有 <input>/<output>/<fault> 元素,與相應的 <operation> 元素中所定義的元素相對應。描述綁定的元素嵌套在 <binding> 元素的這些子元素之中,這是爲了將消息傳遞協議的詳細內容鏈接到目標 portType 中所提到的通則上。示例如下。

                    <wsdl:definitions>

 ...

 <wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">

  <wsdl:operation name="tns:SubscribeToSpecialsAlertList">
   <wsdl:input>

   </wsdl:input>
  </wsdl:operation>

  <wsdl:operation name="tns:RentDVD">
   <wsdl:input>

   </wsdl:input>
   <wsdl:output>

   </wsdl:output>
   <wsdl:fault>

   </wsdl:fault>
  </wsdl:operation>

 </wsdl:binding>

 ...

</wsdl:definitions>

W3C 推薦了三個 Web 服務的綁定:HTTP 上的 SOAP(SOAP over HTTP)、HTTP GET/POST 和 SOAP/MIME。我們將研究 HTTP 上的 SOAP,因爲它似乎是最流行的。在嘗試爲 DVDRenting 協議創建 SOAP 綁定的過程中可能會出現一些問題:

  • 應該把 RentDVD 的輸入值放在什麼地方?放在 SOAP 頭(Header)中嗎?
  • SubscribeToSpecialsAlertList 輸入值中的數據需要遵守 SOAP 編碼規則嗎?換言之它可以是任意的 XML 文件(請參閱編碼樣式)嗎?
  • 在需要通知故障消息時,應該將 RentDVD 出錯放在 SOAP 消息中的哪個位置?

這些問題由 SOAP 綁定來回答:

                    <wsdl:definitions>

 ...

 <wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="SubscribeToSpecialsAlertList">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
  </wsdl:operation>

  <wsdl:operation name="RentDVD">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
   <wsdl:output>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:output>
   <wsdl:fault name="tns:RentFault">
    <soap:fault namespace="http://dvd.com" use="literal"/>
   </wsdl:fault>
  </wsdl:operation>

 </wsdl:binding>

 ...

</wsdl:definitions>

<soap:binding> 元素表示 soap 通信的消息傳遞樣式(rpc)(請參閱“消息傳遞樣式”)和期望使用的傳輸協議(HTTP)。<soap:operation> 元素的 soapAction 屬性被轉換成 HTTP 頭,該頭聲明瞭將被髮送的 HTTP 請求的目的。在 SOAP 1.1 中引入了這個頭,以允許防火牆和其它預處理節點對 SOAP 請求進行過濾。在 SOAP 1.2 中,不贊成使用這個頭,而贊成將請求中的請求目的聲明爲一個參數,稱作“action”。因而通常就讓這個頭空着。<soap:body> 和 <soap:fault> 元素表示消息數據應當放到所生成的 SOAP <Envelope> 中的什麼地方。在上面的案例中,除了 RentDVD 出錯數據應當包含在 SOAP <Fault> 元素中之外,其它所有消息都將包含在常規的 SOAP <Body> 元素中。

<service> 元素

最後,<service> WSDL 元素將某個具體的綁定與網絡上的一個或多個進程相關聯,這些進程可以根據綁定所實現的 portType 來處理請求。對於 HTTP 上的 SOAP,這就是指向那個進程的 URL。

                    <wsdl:service name="DVDRentalServices">
 <wsdl:documentation>Here are some endpoints that support
 the DVDRental SOAP HTTPbinding</wsdl:documentation>
 <wsdl:port name="LPCDVDRentalService" binding="tns:SoapDVDRenting">
  <soap:address location="http://localhost:6080/soap/servlet/rpcrouter" />
 </wsdl:port>
 <wsdl:port name="ClassicsDVDRentals" binding="tns:SoapDVDRenting">
  <soap:address location="http://www.classicsdvdrentals.com/soap/servlet/rpcrouter" />
 </wsdl:port>
</wsdl:service>

關於消息傳遞樣式

SOAP <Body> 元素可以描述的消息有兩種:描述遠程過程調用(rpc)的 XML 文檔或任意 XML 文檔(document)。

在 RPC 樣式的消息傳遞情況下,兩個進程之間發生請求-響應類型的通信是很常見的。也就是說,客戶機進程調用服務器進程上的一個過程,將該過程所需的所有參數傳遞給它,服務器進程將它擁有的所有返回值發送回客戶機進程。該過程的名稱、參數以及返回類型都在 SOAP <Body> 元素中以 XML 進行描述。

文檔樣式的消息傳遞表示 SOAP <Body> 元素的內容是任意的 XML 文檔。儘管可以在請求-響應類型的通信方案中使用文檔樣式的消息傳遞,但是在異步通信中使用它非常理想,因爲這個自包含的 XML 文檔可以放入隊列等待處理。

在下面的示例中,消息傳遞樣式是通過使用 <soap:binding> 元素的 style 屬性來聲明的。在本例中,樣式是 rpc。而 style 屬性另一個可能的值是 document

                    <wsdl:definitions>

 ... 

 <wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

  <wsdl:operation name="tns:SubscribeToSpecialsAlertList">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
  </wsdl:operation>

  <wsdl:operation name="tns:RentDVD">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
   <wsdl:output>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:output>
   <wsdl:fault>
    <soap:fault namespace="http://dvd.com" use="literal"/>
   </wsdl:fault>
  </wsdl:operation>

 </wsdl:binding>

 ...

<wsdl:definitions>

關於編碼

通過線路發送的 XML 流(在 SOAP 消息主體中)可以使用以下兩種編碼之一:encoded 或 literal。要理解這兩者之間的差異,您必須先理解在進程中處理 XML 流可能採用的不同方式。

所有格式良好的 XML 文檔都可以被數據編出到一個 DOM 樹中。一旦創建了樹,線程可以遍歷 DOM 樹並遞歸地讀取/修改它。按照開發人員的需要,可以對 DOM 樹中類型的引用進行數據類型轉換,也可以轉換 DOM 樹中的數據(例如,將字符串“10”轉換成整數 10)。任意 XML 文檔中數據所預期的數據類型,都可以在相關的 XML Schema 文檔中(更籠統地說,是在 DTD 中)定義。編碼樣式 literal 表示可以直接將 SOAP <Body> 元素內容的值轉換成 DOM 樹,而不必擔心它們確切的編程類型。如果需要知道它們預期的類型,可以通過引用 XML 文檔的模式來發現。

SOAP 1.1 建議書定義了一組編碼規則,它們描述應當如何將數據序列化成 XML 流,以及如何將數據反序列化成編程類型。編碼樣式 encoded表明 SOAP 傳輸的消息內容應遵循這些序列化/反序列化規則。

literal 編碼的優點是沒有對正在傳輸的 XML 文檔作任何限制。它的編碼依賴於模式,因此是完全可擴展的。由於這個原因,literal 編碼逐漸成爲傳輸 SOAP <Body> 內容比較常用的方式。

最後,如果客戶機和服務器的開發人員都在使用供應商提供的工具來生成助手代碼,那麼,從這兩類開發人員的角度看,所選擇的編碼樣式應該不是可辨別的。

在下面的示例中,所有的 SOAP 消息都是用 literal 編碼的,不需要遵循 SOAP 編碼規則。編碼是通過使用 <soap:body> 和 <soap:fault> 元素的“use”屬性來聲明的。“use”屬性的另一個可能的值是 encoded

                    <wsdl:definitions>

 ... 

 <wsdl:binding name="SoapDVDRenting" type="tns:DVDRenting">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

  <wsdl:operation name="tns:SubscribeToSpecialsAlertList">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
  </wsdl:operation>

  <wsdl:operation name="tns:RentDVD">
   <soap:operation soapAction=""/>
   <wsdl:input>
    <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:input>
   <wsdl:output>
     <soap:body namespace="http://dvd.com" use="literal"/>
   </wsdl:output>               
   <wsdl:fault>
    <soap:fault namespace="http://dvd.com" use="literal"/>
   </wsdl:fault>
  </wsdl:operation>     

 </wsdl:binding>

 ...
        
<wsdl:definitions>

WSDL2WebService

簡介

WSDK 附帶了 WSDL2WebService(位於 %WSDK_HOME%\bin 目錄),用來幫助您通過 WSDL 構建 Web 服務及其客戶機運行時代碼。WSDL2WebService 工具將使用 WSDL 文件生成創建 Web 服務所需的全部服務器代碼,該 Web 服務是在給定的 WSDL 文件中定義的。它將爲接口中聲明的類型生成類,還將生成與客戶機交互的接口。這樣,開發人員只需負責提供要作爲 Web 服務公開的業務邏輯即可。開發人員要實現的服務器模板代碼有兩種可能的形式:BEAN 或 EJB,這取決於 type 標誌的設置。如果是 BEAN,開發人員只需要在一個簡單的 Java 源文件中實現方法定義。完成的 Web 服務隨即被部署爲 Web 應用程序。如果類型是 EJB,那就需要開發人員在一個無狀態會話 Bean 中實現業務方法定義。

WSDL2WebService 工具在許多情況下都會發現它自己是有用的。例如:

  • 您所在的行業已經通過 WSDL 定義了一組類型和接口,而您的公司希望自己實現它們。
    • 在這種方案下,您可以獲取該行業已定義的 WSDL 文件,並將其傳遞給 WSDL2WebService 工具。它會生成所需的助手代碼和模板,而您需要做的只是在其中實現業務方法。被配置成與 WSDL 文件中定義的服務器接口通信的所有客戶機隨後將能與您的進程通信了!
  • 您正在設計新的服務器端組件,並且您想把它公開爲 Web 服務。
    • 在這種情況下,您可以使用 WSDL 定義組件的接口,然後使用 WSDL2WebService 工具生成所需的(您的組件邏輯可以實現的)類型和模板。
  • 您的組織有兩個進程,您希望它們能夠相互通信。
    • 創建定義接口的 WSDL 文件,該接口由服務器進程公開。WSDL2WebService 不僅將生成服務器助手代碼和模板,還將生成客戶機進程與服務器進程通信所需的客戶機運行時代碼。

創建 Web 服務助手代碼

使用 WSDL2WebService 創建可部署的 Web 服務的過程由兩步組成。首先,WSDL2WebService 需要一個 WSDL 文件,它將通過該文件生成助手代碼和模板代碼:

WSDL2WebService -type <service type> -verbose -createService 
<service name> -project <project dir>  <wsdl file>

例如,

WSDL2WebService -type EJB -verbose -createService myservice  
-project .\myserviceroot MyService.wsdl

正如前面提到的,<code>WSDL2WebService</code> 工具可以生成基於 Bean 或基於 EJB 的服務器組件的模板。可以將 <code>type</code> 標誌設置成 <code>EJB</code>(象在示例中那樣)或 <code>BEAN</code>。如果它的值是 <code>EJB</code>,那麼將生成一個遠程模板、一個 home 模板和一個 bean 模板,這些模板的名稱將根據 WSDL 文件中的目標綁定名稱而定。例如,如果在 <code>MyService.wsdl</code> 中,目標綁定與下面類似:

...
<wsdl:binding name="MyServiceBinding" type="tns:MyServicePortType">
        ...
</wsdl:binding>
...

對所生成的 EJB 類命名如下:

  • home ― MyServicePortTypeHome.java
  • 遠程 ― MyServicePortType.java
  • EJB 類 ― MyServiceBindingImpl.java

verbose 標誌告訴 WSDL2WebService 以詳細方式運行。這是可選的。

createService 標誌的值是 Web 服務的根名稱。

project 標誌值告訴 WSDL2WebService 在哪裏構建 Web 服務。

WSDL2WebService 工具在缺省情況下將生成客戶機運行時代碼。可選的 server-side-only 標誌將關閉這一自動行爲,導致只生成服務器所需的代碼。

提供實現

WSDL2WebService 創建了模板 ― 提供給服務器端開發人員的快捷方法。該模板 Java 文件的名稱與目標綁定名稱相同。例如,爲下列 WSDL 文件生成的模板文件應該是 LPCDVDRentalServiceSoapBindingImpl.java(以下是其部分摘錄)

<wsdl:service name="DVDRentalServices">
 <wsdl:documentation>Here are some endpoints that support the DVDRental SOAP
  HTTPbinding</wsdl:documentation>           
 <wsdl:port binding="tns:SoapDVDRenting" name="LPCDVDRentalService">
  <soap:address location="http://localhost:6080/soap/servlet/rpcrouter" /> 
 </wsdl:port>
</wsdl:service>

LPCDVDRentalServiceSoapBindingImpl.java 會有空的業務方法,其特徵符對應於與綁定相關聯的操作,針對該綁定實現了 Web 服務。開發人員負責“填充”這些方法。這通常是相當簡單的步驟,因爲創建 Web 服務的整個目的無非是公開現有服務器端功能或創建一些全新的事物,這兩種目的出現的頻率基本相同。以下代碼是通過 WSDL2WebService 工具生成的模板代碼的示例。

package com.dvd;

public class SoapDVDRentingImpl implements com.dvd.DVDRenting{
    public void subscribeToSpecialsAlertList(
    com.dvd.EmailAddressType email, java.util.Date until) 
      throws java.rmi.RemoteException {
    }

    public void rentDVD(com.dvd.DVDRentalType rentalRequest, 
        com.dvd.holders.ResponseConfirmationTypeHolder paymentResponse, 
        org.apache.axis.holders.DateHolder purchaseDate) 
          throws java.rmi.RemoteException, com.dvd.PaymentFault {
        paymentResponse.value = new com.dvd.ResponseConfirmationType();
        purchaseDate.value = new java.util.Date();
    }
}

我們省略了 subscribeToSpecialsAlertList(...) 和 rentDVD(...) 的實現。Java 類型com.dvd.EmailAddressType.com.dvd.DVDRentalType 和 com.dvd.holders.ResponseConfirmationTypeHolder 由 WSDL2WebService 工具生成。它們對應於源 WSDL 文件中定義的模式類型。請注意,不需要知道在“幕後”使用的傳輸或消息傳遞協議。處理數據的序列化和反序列化的是與 WSDK 一起提供的運行時代碼 JAR 文件和由 WSDL2WebService 通過源 WSDL 生成的代碼。

創建可部署的 EAR 文件

將 Web 服務生成爲 EAR 文件並部署它,這是使用 WSDL2WebService 工具的第二個步驟。下面顯示了用來從 Web 服務創建 EAR 文件並部署該文件的命令:

WSDL2WebService -deploy -verbose -createEar <EAR name> -project<project dir>

例如,

WSDL2WebService -deploy -verbose -createEar myservice.ear  -project .\myserviceroot

執行該命令後,在指定的目錄中創建了 EAR 文件(在上例中,在當前目錄中創建了 myservice.ear),並將其部署到與 WSDK 一起提供的WebSphere Application Server Express。爲了使部署(deploy)標誌起作用,必須啓動服務器。雖然此時會部署這個 EAR 文件,但不會啓動該文件。爲了使客戶機與該 Web 服務交互,必須啓動該 EAR 文件。要啓動 Web 應用程序,打開命令提示符,然後轉至%WSDK_HOME%\bin。命令 appserver appstatus 將列出所有已部署的應用程序及其狀態。要啓動您的 Web 服務,從列表中找出它的名稱,然後輸入命令 appserver startapp <ws name>,其中 <ws name> 是該 Web 服務的名稱。

例如,如果下面是執行 appserver appstatus 命令後的輸出:

Installed applications

Sample1WebService (disabled/stopped)
Sample2EJBClient (enabled/running)
Sample2WebService (disabled/stopped)
Sample4WebService (disabled/stopped)
Sample5WebService (disabled/stopped)
Sample6WebService (disabled/stopped)
Sample7WebService (disabled/stopped)
Sample8WebService (disabled/stopped)
UDDIRegistry (enabled/running)
myservice (enabled/stopped)

要啓動名稱爲 myservice 的 Web 服務,可以在命令行上輸入:

appserver startapp myservice

實驗 1:構建和部署 Web 服務

實驗目的和概述

本實驗的目的是向您提供使用 IBM WSDK 和 WSDL2WebService 工具構建、部署和測試 Java Web 服務的實踐經驗。本實驗還將演示 WSDK 使您從 WSDL Web 服務定義創建客戶機和服務器代碼的工作變得多麼簡單。

該實驗有四個非常簡單的步驟:

  1. 使用 WSDL2WebService 通過 WSDL 文件生成客戶機和服務器的助手代碼。
  2. 在生成的服務器模板中實現業務方法。
  3. 編譯 Web 服務源代碼,然後使用 WSDL2WebService 將其打包成 EAR 文件並部署它。
  4. 創建一個客戶機以測試該服務。

通過 WSDL 文件生成代碼

在本章中,您將生成 Web 服務及其客戶機的代碼。所生成的代碼當然會缺少其業務邏輯,但我們會在下一頁中添加它。所生成的代碼將基於下面這個簡單的 WSDL。它看起來應該比較熟悉。因爲它描述了在教程通過 Java 類創建 Web 服務(Creating a Web service from a Java class)的實驗 1 中創建的 rpc Web 服務。

                    <?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions 
   targetNamespace="http://server.simplewsdl.dvdonline.lpc" 
   xmlns="http://schemas.xmlsoap.org/wsdl/" 
   xmlns:apachesoap="http://xml.apache.org/xml-soap" 
   xmlns:impl="http://server.simplewsdl.dvdonline.lpc" 
   xmlns:intf="http://server.simplewsdl.dvdonline.lpc" 
   xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <wsdl:types/>

 <wsdl:message name="getDVDResponse">
  <wsdl:part name="getDVDReturn" type="xsd:string"/>
 </wsdl:message>

 <wsdl:message name="getDVDRequest">
 </wsdl:message>

 <wsdl:portType name="DVDOnlineStore_SEI">
  <wsdl:operation name="getDVD">
   <wsdl:input message="intf:getDVDRequest" name="getDVDRequest"/>
   <wsdl:output message="intf:getDVDResponse" name="getDVDResponse"/>
  </wsdl:operation>
 </wsdl:portType>

 <wsdl:binding name="DVDOnlineStoreSoapBinding" type="intf:DVDOnlineStore_SEI">    
  <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="getDVD">
   <wsdlsoap:operation soapAction=""/>
   <wsdl:input name="getDVDRequest">
    <wsdlsoap:body namespace="http://server.simplewsdl.dvdonline.lpc" use="literal"/>
   </wsdl:input>
   <wsdl:output name="getDVDResponse">
    <wsdlsoap:body namespace="http://server.simplewsdl.dvdonline.lpc" use="literal"/>
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>

 <wsdl:service name="DVDOnlineStore_SEIService">
  <wsdl:port binding="intf:DVDOnlineStoreSoapBinding" name="DVDOnlineStore">
   <wsdlsoap:address location="http://localhost:6080/simpleWSDL/services/DVDOnlineStore"/>
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

要生成助手代碼,請執行以下步驟:

  1. 在 %WSDK_HOME%\apps 下創建名爲 simpleWSDL 的目錄。
  2. 將以上的 WSDL 複製到新創建的 simpleWSDL 目錄下名爲 DVDOnlineStoreSimple.wsdl 的文件中。
  3. 在命令行中,轉至新創建的 simpleWSDL 目錄,然後輸入:
    %WSDK_HOME%\bin\WSDL2WebService -createService simpleWSDL -project root DVDOnlineStoreSimple.wsdl
    運行該命令的結果是創建了一個名爲 root 的目錄。在該目錄下,是爲客戶機和服務器生成的全部代碼。客戶機代碼在root\simpleWSDL\client-side 目錄下。服務器代碼在 root\simpleWSDL\lpc 目錄下。另外還在 root\simpleWSDL 下創建了一個 compile.bat,您以後可以用它來編譯源代碼。

實現模板服務器代碼

WSDL2WebService 創建名爲%WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL\lpc\dvdonline\simplewsdl\server\DVDOnlineStoreSoapBindingImpl.java 的文件。這個文件是一個模板,該模板的業務方法特徵符與在 WSDL 文件中定義的特徵符匹配。在本例中,只有一個方法,因爲在 WSDL 文件中只聲明瞭一個操作。下面的代碼清單是這個 DVDOnlineStoreSoapBindingImpl.java,其中有 getDVD() 方法的建議實現:

                    package lpc.dvdonline.complexwsdl.server;

import lpc.dvdonline.complexwsdl.service.DVD;

public class DVDOnlineStoreSoapBindingImpl 
   implements lpc.dvdonline.complexwsdl.server.DVDOnlineStore_SEI{
 public lpc.dvdonline.complexwsdl.service.DVD getDVD() 
     throws java.rmi.RemoteException {

  DVD dvd = new DVD();
  dvd.setRank(10);
  dvd.setTitle("Fight Club");
  dvd.setCategory("Weird but good");
  return dvd;
 }
}

要編譯該源代碼以及生成的其它服務器類,轉至 %WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL,然後執行:

compile.bat

打包、部署和啓動服務

要把這個簡單的類變成 Web 服務還要做很多工作。幸運的是,WSDK 有幫助您做這些工作的工具。要打包並部署您的服務,只需:

1. 啓動應用程序服務器

  • 您可以從開始菜單啓動應用程序服務器。WSDK 安裝程序在 WSDK 程序組中創建名爲 Start Server 的快捷方式,您可以用它來啓動服務器。
  • 您可以從命令行啓動 WebSphere Application Server。它位於 bin 目錄中,可以這樣調用它:
    %WSDK_HOME%\bin\appserver start

啓動應用程序服務器。當應用程序服務器裝入時,您可以看到幾條狀態消息。尋找消息“Server server1 open for e-business”。

2. 運行 WSDL2WebService 實用程序

  • 在第一步中,該工具生成了服務器代碼。現在,它將打包並部署 Web 服務應用程序。
  • 注:在調用該工具之前,請確保 WSDK 的 bin 目錄(%WSDK_HOME%\bin)在您的路徑中。
  • 要運行它,可打開一個命令提示符,轉至 %WSDK_HOME%\apps\simpleWSDL 目錄,然後用帶有 deploy 和 createEar 標誌的命令調用WSDL2WebService 實用程序:
    WSDL2WebService -deploy -createEar simpleWSDL.ear -project root

用於 WSDL2WebService 的參數如下:

  • deploy 標誌使得產生的 EAR 文件(包括類文件和部署描述符)將被部署到應用程序服務器。
  • createEar 標誌定義 EAR 文件的名稱,並定義 Web 服務通信的 Web 環境。
  • project 標誌告訴工具 Web 服務類文件和 xml 文件的根目錄在哪裏。

您無需對 simpleWSDL.ear 進行任何操作,因爲這裏使用了 deploy 標誌。應用程序已得到部署,它的一個已展開格式的副本被放在appserver 目錄下。

3. 檢查結果

  • 通過檢查服務器日誌文件中是否有錯誤以及通過使用 appserver appstatus 命令,來驗證服務已得到部署。appserver appstatus 命令列出 WebSphere Application Server 中已安裝的企業應用程序,以及它們的運行 status (running | stopped)。如下調用 appserver appstatus
    appserver appstatus
    在已安裝企業應用程序的列表中查找 simpleWSDL 應用程序名稱。

4. 啓動 Web 服務

  • 使用 appserver startapp 命令啓動 Web 服務,如下所示:
    appserver startapp simpleWSDL
    該命令告訴 WebSphere 啓動與企業應用程序 simpleWSDL 相關的 Web 服務。

創建 simpleWSDL 的服務客戶機

既然有了服務,就需要客戶機。創建客戶機非常簡單。只需要幾行代碼。

  1. 在 %WSDK_HOME%\apps\simpleWSDL 目錄中,創建名爲 requester 的目錄。該目錄將包含客戶機源代碼。
  2. 在 requester 目錄中,創建名爲 src 的目錄。
  3. 在 src 目錄中,創建一系列反映包結構 lpc.dvdonline.simplewsdl.client 的目錄。
  4. 在包 lpc\dvdonline\simplewsdl\client 目錄中,創建名爲 DVDOnlineStoreClient.java 的 Java 源文件。將以下代碼添加到源文件中:
    package lpc.dvdonline.simplewsdl.client;
    
    import lpc.dvdonline.simplewsdl.server.*;
    
    public class DVDOnlineStoreClient {
        
     public static void main( String[] args ) throws Exception{
      DVDOnlineStore_SEIService loc = new DVDOnlineStore_SEIServiceLocator();
      DVDOnlineStore_SEI port = (DVDOnlineStore_SEI)loc.getDVDOnlineStore();
      System.out.println( port.getDVD() );
     } //end main()
        
    } //end DVDOnlineStoreClient

    請注意,這個客戶機創建了一個服務。它使用特定於 IBM 的類型 ServiceLocator 來完成這一工作。它通過服務訪問端口(即 Web 服務的接口),然後調用 getDVD() 方法。
  5. 客戶機需要由 WSDL2WebService 生成的存根、接口和定位器類。將 %WSDK_HOME%\apps\simpleWSDL\root\simpleWSDL\client-side 目錄中的文件和目錄複製到 %WSDK_HOME%\apps\simpleWSDL\root\requester\src 目錄中。
  6. 此時就可以着手進行編譯了。和以前一樣編譯以上類,前提是類路徑中包含 %WSDK_HOME%\appserver\lib 中以下的 JAR 文件:
    • j2ee.jar
    • axis.jar
    • qname.jar
    • jaxrpc.jar
    • wsdl4j.jar
    • xerces.jar
    • commons-discovery.jar
    • saaj.jar
    • commons-logging-api.jar
    由於有許多 JAR 文件要管理,所以建議使用 Java IDE,如可免費獲得的 Eclipse IDE
  7. 使用 java 命令運行客戶機。確保類路徑上的 JAR 文件和用於編譯時類路徑上的 JAR 文件相同。 
    java -classpath ... lpc.dvdonline.simplewsdl.client.DVDOnlineStoreClient
  8. 如果您已開始使用 Eclipse IDE,則只需單擊工具欄上的小“奔跑者”圖標按鈕。

如果您看到“Fight Club”的響應消息,那麼您的 Web 服務已啓動並正在運行。您創建、部署並啓動了您的首個 Web 服務,接着您編寫了一個客戶機來調用該 Web 服務的方法。

實驗回顧

讓我們回顧一下您所做的工作,並回顧一下 WSDL2WebService 實用程序爲您做了什麼。

您獲得了一個 WSDL 文件,WSDL2WebService 通過該文件生成了服務器模板和客戶機代碼。此後,您實現了所生成的服務器模板代碼。然後,您使用提供的 compile.bat 編譯了所有的代碼。WSDL2WebService 將您的 Web 服務部署爲捆綁在 WAR 文件中的 J2EE Web 應用程序,而這個 WAR 文件又捆綁在 EAR 文件中。您使用靜態的、預先定義的存根類創建 Web 服務客戶機。

雖然您沒有親自動手定義接口,但 WSDL2WebService 爲您創建了一個,如下所示:

package lpc.dvdonline.simplewsdl.server;

public interface DVDOnlineStore_SEI extends java.rmi.Remote {
 public java.lang.String getDVD() throws java.rmi.RemoteException;
}

如果您進行過 RMI 開發,就知道這個接口看上去和爲 RMI 服務程序創建的接口類型完全相同。

WSDL2WebService 工具還生成一個服務類和一個存根類。服務類實現了 javax.xml.rpc.Service 接口。javax.xml.rpc.Service 是所生成的存根的工廠。對於這些生成文件的更深入的描述,請回頭參閱教程通過 Java 類創建 Web 服務(Creating a Web service from a Java class)

實驗 2:交換複雜對象

實驗概述

本實驗的目的是演示 IBM WSDK 交換複雜 SOAP 數據類型的能力,這些數據類型是根據 WSDL 文件中的定義用 WSDL2WebService 工具生成的。

雖然傳遞字符串(String)是您開始使用 Web 服務的好方法,但實際的應用程序通常要求客戶機和服務交換具有複雜數據結構的非標量類型的對象。在本實驗中,您將遵循上個實驗中相同的步驟,唯一不同的是您要開始處理的 WSDL 源文件中定義了一個複雜類型 ― DVD。服務器將把該 DVD 類型的實例傳遞給客戶機。

定義表示 DVD 對象的複雜 XML Schema 類型

正如本教程前面討論的,通過使用 XML Schema,您在 WSDL 文件中需要多複雜的類型,就可以完全自由地定義該類型。以下 WSDL 文件和您在上個實驗中處理的相同,唯一不同的是服務器向客戶機返回 DVD 對象,而不只是字符串。該 DVD 類型在 WSDL 中的 <types> 元素中定義,並且在 <message> 元素中被引用。

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions 
targetNamespace="http://server.complexwsdl.dvdonline.lpc" 
xmlns="http://schemas.xmlsoap.org/wsdl/" 
xmlns:apachesoap="http://xml.apache.org/xml-soap" 
xmlns:impl="http://server.complexwsdl.dvdonline.lpc" 
xmlns:intf="http://server.complexwsdl.dvdonline.lpc" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:tns2="http://service.complexwsdl.dvdonline.lpc" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <wsdl:types>
  <schema targetNamespace="http://service.complexwsdl.dvdonline.lpc"
   xmlns="http://www.w3.org/2001/XMLSchema">
   <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
   <complexType name="DVD">
    <sequence>
     <element name="category" nillable="true" type="xsd:string"/>
     <element name="rank" type="xsd:int"/>
     <element name="title" nillable="true" type="xsd:string"/>
    </sequence>
   </complexType>
   <element name="DVD" nillable="true" type="tns2:DVD"/>
  </schema>
 </wsdl:types>
 <wsdl:message name="getDVDRequest">
 </wsdl:message>
 <wsdl:message name="getDVDResponse">
  <wsdl:part name="getDVDReturn" type="tns2:DVD"/>
 </wsdl:message>
 <wsdl:portType name="DVDOnlineStore_SEI">
  <wsdl:operation name="getDVD">
   <wsdl:input message="impl:getDVDRequest" name="getDVDRequest"/>
   <wsdl:output message="impl:getDVDResponse" name="getDVDResponse"/>
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="DVDOnlineStoreSoapBinding" type="impl:DVDOnlineStore_SEI">
  <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="getDVD">
   <wsdlsoap:operation soapAction=""/>
   <wsdl:input name="getDVDRequest">
    <wsdlsoap:body namespace="http://server.complexwsdl.dvdonline.lpc" use="literal"/>
   </wsdl:input>
   <wsdl:output name="getDVDResponse">
    <wsdlsoap:body namespace="http://server.complexwsdl.dvdonline.lpc" use="literal"/>
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="DVDOnlineStore_SEIService">
  <wsdl:port binding="impl:DVDOnlineStoreSoapBinding" name="DVDOnlineStore">
   <wsdlsoap:address location="http://localhost:6080/compleWSDL/services/DVDOnlineStore"/>
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

要生成助手代碼,請執行以下步驟:

  1. 在 %WSDK_HOME%\apps 下創建名爲 complexWSDL 的目錄。
  2. 將上面的 WSDL 複製到新創建的 complexWSDL 目錄下名爲 DVDOnlineStoreComplex.wsdl 的文件中。
  3. 在命令行中,轉至新創建的 complexWSDL 目錄,然後輸入:
    %WSDK_HOME%\bin\WSDL2WebService -createService complexWSDL -project root DVDOnlineStoreComplex.wsdl
    運行該命令的結果是創建了一個名爲 root 的目錄。在該目錄下,是爲客戶機和服務器生成的全部代碼。客戶機代碼在root\complexWSDL\client-side 目錄下。服務器代碼在 root\complexWSDL\lpc 目錄下。另外還在 root\complexWSDL 下創建了一個 compile.bat,您稍後可以用它來編譯源代碼。

實現模板服務器代碼

WSDL2WebService 創建名爲%WSDK_HOME%\apps\complexWSDL\root\complexWSDL\lpc\dvdonline\complexwsdl\server\DVDOnlineStoreSoapBindingImpl.java 的文件。和上一個實驗不同的是,getDVD() 方法返回一個 DVD 實例。以下是 DVDOnlineStoreSoapBindingImpl.java 的代碼清單,其中有 getDVD()方法的建議實現:

package lpc.dvdonline.complexwsdl.server;

import lpc.dvdonline.complexwsdl.service.DVD;

public class DVDOnlineStoreSoapBindingImpl 
   implements lpc.dvdonline.complexwsdl.server.DVDOnlineStore_SEI{
 public lpc.dvdonline.complexwsdl.service.DVD getDVD() 
     throws java.rmi.RemoteException {
  DVD dvd = new DVD()
  dvd.setRank(10);
  dvd.setTitle("Fight Club");
  dvd.setCatagory("Weird but good");
  return dvd;
 }
}

要編譯該文件以及生成的其它服務器類,轉至 %WSDK_HOME%\apps\complexWSDL\root\complexWSDL,然後執行:

compile.bat

打包、部署和啓動服務

啓動並運行該 Web 服務的步驟和上一個實驗的步驟相同。我們再次列出這些步驟,但對名稱作了適當的改動。

1. 啓動應用程序服務器

  • 您可以從開始菜單啓動應用程序服務器。WSDK 安裝程序在 WSDK 程序組中創建名爲 Start Server 的快捷方式,您可以用它啓動服務器。
  • 您可以從命令行啓動 WebSphere Application Server。啓動命令位於 bin 目錄中,可以這樣調用它:
    %WSDK_HOME%\bin\appserver start

繼續然後啓動應用程序服務器。當應用程序服務器裝入時,您可以看到幾條狀態消息。尋找消息“Server server1 open for e-business”。

2. 運行 WSDL2WebService 實用程序

  • 在第一步中,該工具生成了服務器代碼。現在,它打包這個 Web 服務應用程序,並部署它。
  • 注:在調用該工具之前,請確保 WSDK 的 bin 目錄(%WSDK_HOME%\bin)在您的路徑中。
  • 要運行它,可打開一個命令提示符,轉至 %WSDK_HOME%\apps\complexWSDL 目錄,然後用帶有 deploy 和 createEar 標誌的命令調用WSDL2WebService 實用程序:
    WSDL2WebService -deploy -createEar complexWSDL.ear -project root

用於 WSDL2WebService 的參數和上一個實驗相同。

您無需對 simpleWSDL.ear 進行任何操作,因爲這裏使用了 deploy 標誌。應用程序已得到部署,它的一個已展開格式的副本被放置在appserver 目錄下。

3. 檢查結果

  • 通過檢查服務器日誌文件中是否有錯誤以及通過使用 appserver appstatus 命令,來驗證服務已得到部署。appserver appstatus 命令列出 WebSphere Application Server 中已安裝的企業應用程序,以及它們的運行 status (running | stopped)。如下調用 appserver appstatus
    appserver appstatus
    在已安裝企業應用程序的列表中查找 complexWSDL 應用程序的名稱。

4. 啓動 Web 服務

  • 使用 appserver startapp 命令啓動 Web 服務,如下所示:
    appserver startapp complexWSDL
    該命令告訴 WebSphere 啓動與企業應用程序 complexWSDL 相關聯的 Web 服務。

創建 complexWSDL 的服務客戶機

我們只需稍微修改一下上個實驗中的客戶機,以便使它與 DVD Web 服務通信。

  1. 在 %WSDK_HOME%\apps\complexWSDL 目錄中,創建名爲 requester 的目錄。該目錄將包含客戶機源代碼。
  2. 在 requester 目錄中,創建名爲 src 的目錄。
  3. 在 src 目錄中,創建一系列反映包結構 lpc.dvdonline.complexwsdl.client 的目錄。
  4. 在包 lpc\dvdonline\complexwsdl\client 目錄中,創建名爲 DVDOnlineStoreClient.java 的 java 源文件。將以下代碼添加到源文件中:
    package lpc.dvdonline.complexwsdl.client;
    
    import lpc.dvdonline.complexwsdl.server.*;
    
    public class DVDOnlineStoreClient {
     public static void main( String[] args ) throws Exception{
      DVDOnlineStore_SEIService loc = new DVDOnlineStore_SEIServiceLocator();
      DVDOnlineStore_SEI port = (DVDOnlineStore_SEI)loc.getDVDOnlineStore();
      DVD dvd = port.getDVD();        
      System.out.println(dvd.getTitle());
     } //end main()
    } //end DVDOnlineStoreClient

    請注意,這個客戶機與前面的一樣,唯一區別是返回給它的(由 Web 服務發送)不是字符串而是 DVD 對象。
  5. 客戶機需要由 WSDL2WebService 生成的存根、接口和定位器類。將 %WSDK_HOME%\apps\complexWSDL\root\dvdonline\client-side 目錄中的文件和目錄複製到 %WSDK_HOME%\apps\complexWSDL\root\requester\src 目錄中。
  6. 此時就可以着手進行編譯了。和前面一樣編譯以上類,前提是類路徑中包含 %WSDK_HOME%\appserver\lib 中的以下 JAR 文件:
    • j2ee.jar
    • axis.jar
    • qname.jar
    • jaxrpc.jar
    • wsdl4j.jar
    • xerces.jar
    • commons-discovery.jar
    • saaj.jar
    • commons-logging-api.jar
    由於有許多 JAR 文件要管理,所以建議使用 Java IDE,如可免費獲得的 Eclipse IDE
  7. 使用 java 命令運行客戶機。確保類路徑上的 JAR 文件和用於編譯時類路徑上的文件相同。 
    java -classpath ... lpc.dvdonline.complexwsdl.client.DVDOnlineStoreClient
  8. 如果您已開始使用 Eclipse IDE,則只需單擊工具欄上的小“奔跑者”圖標按鈕。

同樣,如果您看到“Fight Club”的響應消息,那麼您的 Web 服務就已啓動並運行了。您創建、部署並啓動一個使用複雜類型的 Web 服務,接着您編寫了一個客戶機來調用該 Web 服務的方法。正如您所見,主要差異在 WSDL 中。大多數複雜性都對您隱藏起來。

教程結束語

教程回顧

在本教程中,我們完成了以下工作:

  • 回顧 WSDL 並討論它之所以重要的原因。
  • 瞭解 WSDL 結構和 WSDL 元素。
  • 回顧 XML Schema,並瞭解如何將它和 WSDL 一起使用。
  • 使用 WSDL2WebService 通過 WSDL 文件創建基於 Web 服務的系統。
  • 創建、部署並使用通過 WSDL 文件創建的小型 Web 服務系統。

WSDL 乍一看似乎有些深奧,您可能認爲它是 Web 服務體系結構中無關緊要的部分。您確實無需直接使用 WSDL 就可以進行大量的 Web 服務開發 ― 只要您有適當的高級工具,但理解 WSDL 的結構和它的用途很重要。它是有關 Web 服務互操作性的關鍵部分。如果使用 UDDI 註冊中心的話,WSDL 也很重要。

我們讓您瞭解了 WSDL 是什麼,以及如何用 IBM WSDK 來使用它。如果您在 Web 服務方面的工作不斷增加的話,它會對您非常有幫助。

參考資料

學習

  • SOA and Web services 專區:提供了大量的文章,以及關於如何開發 Web 服務應用程序的初級、中級和高級教程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章