早期的 Apache Axis 建立在第一個面向 Web 服務的 Java 標准 JAX-RPC 的基礎之上。事實證明,這並不是一個很好的方法,因為 JAX-RPC 限制了 Axis 代碼的內部設計,而且造成了性能問題並缺乏靈活性。JAX-RPC 還對 Web 服務開發的方向作出了假設,而這在後來被證明是錯誤的。
在開始 Axis2 開發工作時,已經著手研究 JAX-RPC 的替代選擇,因此,Axis2 在設計時已經考慮到了足夠的靈活性,使其能夠在基礎框架之上實現對替代 Web 服務標准的支持。最新的 Axis2 版本同時實現了對 JAXB 2.x Java XML 數據綁定標准和替代了 JAX-RP 的 JAX-WS 2.x Java Web 服務標准的支持。本文將展示如何將 JAXB 和 JAX-WS 用於 Axis2 並找出 Axis2 對這些標准的當前支持中存在的一些限制。
Axis2 中的 JAXB
Axis2 實現了對 JAXB 2.x 的支持,將它作為數據綁定替代選擇的其中之一,您可以在使用 WSDL2Java 從 Web Services Description Language (WSDL) 服務定義中生成代碼時進行選擇。和大多數其他替代選擇一樣,使用 JAXB 2.x 從 WSDL 中生成的代碼創建了一組鏈接(linkage)類和一組數據模型類。這些鏈接類,包括一個客戶端 stub 和一個服務器端消息接收器,充當應用程序代碼和 Axis2 之間的接口。數據模型類表示實際的消息數據。
JAXB 2.x 使用數據模型類中的注釋來控制數據與 XML 之間的轉換方式。注釋方法允許您在無需修改源代碼或重新編譯類的情況下在運行時使用不同的 JAXB 實現。由 JAXB 實現負責從數據模型類訪問注釋信息並在執行 XML 轉換時應用這些注釋。
代碼下載(參見 下載)提供了一個演示在 Axis2 中使用 JAXB 的示例應用程序,位於 jaxb 目錄中。這個應用程序是本系列前一篇文章中的簡單的庫管理服務的另一個版本(包括 “Axis2 Data Binding” 中的數據綁定比較)。WSDL 服務定義定義了四個操作:
getBook 用來檢索由 International Standard Book Number (ISBN) 標識的某本特定圖書的細節信息
getBooksByType 用來檢索某一特定類型的所有圖書的細節信息
getTypes 用於查找可用的圖書類型
addBook 用於將新書添加到庫中
清單 1 顯示的是經過大量編輯的 WSDL,只包含了與 getBook 操作有關的部分:
清單 1. 庫服務 WSDL
<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"
schemaLocation="types.xsd"/>
<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>
</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"/>
</wsdl:output>
</wsdl:operation>
...
</wsdl:binding>
<wsdl:service name="jaxb-library">
<wsdl:port binding="wns:LibrarySoapBinding" name="library">
<wsdlsoap:address location="http://localhost:8080/axis2/services/jaxb-library"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Axis2 的 JAXB 支持應該進行擴展,以生成未封裝的操作方法(為了方便編程,封裝到消息中的值被轉換為方法參數 — 再一次建議您參考 “Java Web 服務:Axis2 Data Binding”,獲得有關封裝接口和未封裝接口的討論。但是使用 WSDL2Java 工具的未封裝支持,不管在當前的 Axis2 代碼中,還是最近幾個發行版中,都不適合這個例子。至少在目前,封裝操作方法是惟一可以將 JAXB 用於 Axis2 代碼生成的方法(但請立刻參考一下 JAX-WS 討論 了解另一種替代方法)。對於封裝操作接口,每個服務方法都對操作使用一個與輸入消息匹配的單一對象參數,並為操作返回一個與輸出消息匹配的對象。
附帶的代碼提供了服務和測試客戶機的實際實現,開始處理由運行的 WSDL2Java 生成的類。與本系列早期文章的樣例代碼相同,下載部分包括用於通過 Apache Ant 構建樣例的 build.properties 和 build.xml 文件(位於 jaxb 目錄)。您首先需要編輯 build.properties 文件,以將路徑設置為您的 Axis2 安裝(並修改其他設置,如果系統需要的話)。隨後可以在一個打開到 jaxb 目錄的控制台中輸入 ant 以運行 WSDL2Java,編譯附帶的和生成的代碼,並為服務器部署構建 AAR 文件。要進行嘗試,首先將生成的 AAR 文件部署到您的 Axis2 服務器安裝並在控制台中輸入 ant run。
客戶端 JAXB 使用
測試客戶機使用作為命令行參數傳遞進來的服務端點參數創建了一個服務 stub 實例,然後依次執行 5 個服務調用:
獲得有關某本書的細節。
獲得庫中的圖書類型。
向庫添加一本新書(如果該書已經存在的話,此操作將失敗,當客戶機在未重啟服務器的情況下運行超過一次時,也會出現同樣的錯誤)。
如果上一步驟成功,那麼嘗試使用相同 ISBN 添加另一本書(這個操作應當永遠都是失敗的)。
獲得有關某一特定類型的所有圖書的信息。
清單 2 展示了完整的測試客戶機代碼。可以看到用於每個操作的封裝器對象中的服務接口的封裝特性,比如調用 getTypes 操作所需的 GetTypes 對象(即使沒有為該操作提供輸入數據)和由調用返回的 GetTypesResponse 對象。
清單 2. JAXB 測試客戶機代碼
public class WebServiceClient
{
public static void main(String[] args) throws Exception {
// check for required command line parameters
if (args.length < 3) {
System.out.println("Usage:\n java " +
"com.sosnoski.ws.library.jaxb.WebServiceClient host port path");
System.exit(1);
}
// create the client stub
String target = "http://" + args[0] + ":" + args[1] + args[2];
System.out.println("Connecting to " + target);
JaxbLibraryStub stub = new JaxbLibraryStub(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());
List<TypeInformation> types = gtr.getGetTypesReturn();
System.out.println("Retrieved " + types.size() + " types:");
for (int i = 0; i < types.size(); i++) {
TypeInformation type = types.get(i);
System.out.println(" '" + type.getName() + "' with " +
type.getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
AddBook ab = new AddBook();
ab.setType("scifi");
ab.setIsbn(isbn);
ab.getAuthor().add("Cook, Glen");
ab.setTitle(title);
stub.addBook(ab);
System.out.println("Added '" + title + '\'');
title = "This Should Not Work";
ab.setTitle(title);
stub.addBook(ab);
System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFault e) {
System.out.println("Failed adding '" + title +
"' with ISBN '" + isbn + "' - matches existing title '" +
e.getFaultMessage().getBook().getTitle() + '\'');
}
// get all books of a type
GetBooksByType gbbt = new GetBooksByType();
gbbt.setType("scifi");
GetBooksByTypeResponse gbbtr = stub.getBooksByType(gbbt);
List<BookInformation> books = gbbtr.getGetBooksByTypeReturn();
System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
for (int i = 0; i < books.size(); i++) {
System.out.println(" '" + books.get(i).getTitle() + '\'');
}
}
}
如果將 清單 2 與 “Java Web Services: Axis2 Data Binding” 中的客戶機代碼示例加以比較,會發現它非常類似於 JiBX 和 Axis Data Binding (ADB) 封裝例子,主要區別在於 JAXB 封裝器類使用 Java 5 類型列表(typed lists)而不是數組(JiBX 數據綁定支持的另一個替換選擇,但是不受 ADB 支持)。
服務器端使用
庫服務的服務器端代碼包含兩個類,其中一個實際實現庫處理,另一個可適應 Axis2 所期望的服務接口。實際的實現代碼對於不同的數據綁定幾乎都是相同的,只需要根據生成的數據模型表示做一些微小的修改。清單 3 展示了更加有趣的服務接口類。和在客戶端一樣,封裝的接口要求應用程序代碼從收到的封裝器對象中提取數據,並構造將要發送的封裝器對象。
清單 3. JAXB 服務器代碼
public class JaxbLibraryImpl extends JaxbLibrarySkeleton
{
private final BookServer m_server;
public JaxbLibraryImpl() {
m_server = new BookServer();
}
public AddBookResponse addBook(AddBook req) throws AddDuplicateFault {
BookInformation prior = m_server.getBook(req.getIsbn());
if (prior == null) {
BookInformation book = new BookInformation();
book.getAuthor().addAll(req.getAuthor());
book.setIsbn(req.getIsbn());
book.setTitle(req.getTitle());
book.setType(req.getType());
AddBookResponse rsp = new AddBookResponse();
rsp.setAddBookReturn(m_server.addBook(book));
return rsp;
} else {
AddDuplicateFault e =
new AddDuplicateFault("Book already present with matching ISBN");
AddDuplicate ad = new AddDuplicate();
ad.setBook(prior);
e.setFaultMessage(ad);
throw e;
}
}
public GetBookResponse getBook(GetBook req) {
BookInformation book = m_server.getBook(req.getIsbn());
GetBookResponse rsp = new GetBookResponse();
rsp.setGetBookReturn(book);
return rsp;
}
public GetBooksByTypeResponse getBooksByType(GetBooksByType req) {
GetBooksByTypeResponse rsp = new GetBooksByTypeResponse();
rsp.getGetBooksByTypeReturn().addAll(m_server.getBooksByType(req.getType()));
return rsp;
}
public GetTypesResponse getTypes(GetTypes req) {
GetTypesResponse rsp = new GetTypesResponse();
rsp.getGetTypesReturn().addAll(m_server.getTypes());
return rsp;
}
}
“Java Web Services: Axis2 Data Binding” 並沒有展示針對不同數據綁定的服務器接口代碼,但是如果您比較 清單 3 和從上文下載的代碼,就會發現清單 3 非常接近於 ADB 和 JiBX 封裝器例子,同樣,惟一的區別在於使用了 Java 5 的類型類別而沒有使用數組。
JAXB 數據模型類
清單 4 展示了通過運行 WSDL2Java 生成的 JAXB 數據模型類(生成的大多數注釋已被刪除,只留下少量注釋作為例子)。生成的數據模型類對於客戶機和服務器都是相同的,即使是由項目構建單獨創建的。顯示的類用於 清單 2 的客戶機代碼和 清單 3 的服務器代碼中的 getBook 調用。每個類定義上的注釋(用粗體顯示)和大部分字段定義提供了配置信息,供 JAXB 用於控制對象與 XML 的轉換。
清單 4. JAXB 數據模型類
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"isbn"
})
@XmlRootElement(name = "getBook")
public class GetBook {
@XmlElement(required = true)
protected String isbn;
/**
* Gets the value of the isbn property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getIsbn() {
return isbn;
}
/**
* Sets the value of the isbn property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setIsbn(String value) {
this.isbn = value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BookInformation", propOrder = {
"author",
"title"
})
public class BookInformation {
protected List<String> author;
@XmlElement(required = true)
protected String title;
@XmlAttribute(required = true)
protected String type;
@XmlAttribute(required = true)
protected String isbn;
public List<String> getAuthor() {
if (author == null) {
author = new ArrayList<String>();
}
return this.author;
}
public String getTitle() {
return title;
}
public void setTitle(String value) {
this.title = value;
}
public String getType() {
return type;
}
public void setType(String value) {
this.type = value;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String value) {
this.isbn = value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"getBookReturn"
})
@XmlRootElement(name = "getBookResponse")
public class GetBookResponse {
protected BookInformation getBookReturn;
public BookInformation getGetBookReturn() {
return getBookReturn;
}
public void setGetBookReturn(BookInformation value) {
this.getBookReturn = value;
}
}
@XmlAccessorType 注釋在包或類級別使用,用於控制從類中訪問值的方式 — 包括訪問所有字段、訪問所有具有 get/set 訪問方法的屬性、只訪問公開字段和屬性,或僅根據某個注釋的指定進行訪問。@XmlType 注釋在匹配某個模式類型的類或 enum 定義中使用,以向 JAXB 提供模式類型的名稱和名稱空間(如果有的話)、類型表示中的值的順序,以及如何使用一個工廠方法構建類的實例(可選)。@XmlRootElement 注釋在匹配全局元素定義的類或 enum 定義中使用,用於提供全局元素的名稱和名稱空間。@XmlElement 和 @XmlAttribute 注釋在值中使用(以字段或 JavaBean 屬性方法的形式),提供元素或屬性名和其他特征)。
JAXB 使用的所有注釋都位於 javax.xml.bind.annotation 包中,除了這個簡單示例生成的代碼中使用的注釋外,還包括許多其他注釋。JAXB 支持從模式中生成代碼(本例就屬於此例),也支持從代碼中生成。某些注釋和選項(比如那些處理對象工廠和串行器/並行器方法的注釋和選項)只能用於從代碼中生成的情況。
Axis2 中的 JAXB 問題
WSDL2Java 調用 JAXB 參考實現中附帶的 XJC 綁定編譯器來生成數據模型代碼,因此在大多數方面數據模型代碼的生成是獨立於 Axis2 的。如果直接在 Web 服務使用的模式上面運行 JAXB XJC 綁定編譯器,您將生成相同的數據模型。不幸的是,WSDL2Java 和 XJC 之間的阻抗匹配並不總是正確的,這就會導致一些問題。
其中一個問題與在 WSDL 文檔中構建模式的方式有關。庫服務的初始 WSDL 使用單個文檔,該文檔合並了兩個獨立的模式,一個用於 WSDL 消息元素,另一個用於應用程序數據(圖書和類型信息)。如 WSDL 允許的那樣,消息元素模式通過名稱空間引用導入了應用程序數據模式。這個包含嵌入模式的 WSDL 可以很好地與使用 ADB 或 JiBX 數據綁定的 WSDL2Java 工作,但是對於 JAXB,它將在模式處理期間引發一個異常拋出。將應用程序數據模式分離到一個單獨的文件並在模式導入時指定文件名稱,這將允許 WSDL2Java 使用 JAXB 綁定正確地處理模式。
另一個問題是,XJC 提供了大量代碼生成選項以及許多定制來控制針對某一特定模式組件的代碼生成細節 — 但是 WSDL2Java 沒有提供任何方法來將這些選項或定制傳遞給 XJC,因此代碼生成將始終按照默認設置運行。如果需要使用任何代碼生成選項或定制,可能需要單獨運行 XJC 和 WSDL2Java。不幸的是,無法在 WSDL2Java 代碼生成中使用單獨生成的 JAXB 數據模型。如果需要使用定制的 JAXB 數據模型,最佳辦法可能就是運行 WSDL2Java 以生成其自己的 JAXB 代碼模型,然後再換入您單獨生成的數據模型類,並根據需要手動修改代碼來將所有內容聯合到一起。或者,可以像下一小節介紹的那樣使用 JAX-WS,這將使您完全跳過 WSDL2Java,但是存在一些明顯的限制。
在 Axis2 中使用 JAX-WS
雖然可以將 JAXB 作為 Axis2 中的另一種數據綁定替代選擇,但是它與 JAX-WS 之間的區別更加顯著。JAX-WS 是一種截然不同的 Web 服務定義方法,它全面取代了標准的 Axis2 服務器端和客戶端配置。您使用 JAX-WS 參考實現中附帶的 WsImport 工具從 WSDL 中生成 JAX-WS 代碼,而不是使用 WSDL2Java。甚至部署機制也不同於 Axis2 中通常使用的 AAR 文件方法。
代碼 下載 部分只提供了早先使用的同一樣例應用程序的不同版本,這個版本進行了修改,用於演示 JAX-WS 在 Axis2 中的使用。代碼位於下載中的 jaxws 目錄中,並且它帶有自己的 WSDL、build.properties 和 build.xml。這個 JAX-WS 版本的 WSDL 基本上與用於 JAXB 的 WSDL 相同,如 清單 1 所示。該 WSDL 的主要區別在於它對應用程序代碼使用了內嵌的模式,而這對於使用 JAXB 數據綁定的 WSDL2Java 來說是不可行的。
當使用 JAX-WS 的 WsImport 工具從 WSDL 創建代碼時,將獲得與使用 WSDL2Java 實現 JAXB 代碼生成時相同的 JAXB 數據模型和封裝器類。不同之處在於鏈接(linkage)代碼,對於 JAX-WS,鏈接代碼包含一個生成的服務接口和一個客戶端服務構建器類。接口類,如 清單 5(稍微進行了重新格式化,並且只保留了一個方法注釋)所示,定義了與 WSDL 中的操作匹配的方法。客戶機代碼和服務器代碼都使用這個接口。接口中的大量注釋提供了所有必需配置信息,幫助 JAX-WS 將服務接口與該服務的操作的接口方法關聯起來。
清單 5. JAX-WS 生成的服務接口
@WebService(name = "Library", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@XmlSeeAlso({
ObjectFactory.class
})
public interface Library
{
/**
*
* @param isbn
* @return
* returns com.sosnoski.ws.library.jaxws.BookInformation
*/
@WebMethod(action = "urn:getBook")
@WebResult(name = "getBookReturn",
targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@RequestWrapper(localName = "getBook",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetBook")
@ResponseWrapper(localName = "getBookResponse",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetBookResponse")
public BookInformation getBook(
@WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
String isbn);
@WebMethod(action = "urn:getBooksByType")
@WebResult(name = "getBooksByTypeReturn",
targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@RequestWrapper(localName = "getBooksByType",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetBooksByType")
@ResponseWrapper(localName = "getBooksByTypeResponse",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetBooksByTypeResponse")
public List<BookInformation> getBooksByType(
@WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
String type);
@WebMethod(action = "urn:getTypes")
@WebResult(name = "getTypesReturn",
targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@RequestWrapper(localName = "getTypes",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetTypes")
@ResponseWrapper(localName = "getTypesResponse",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.GetTypesResponse")
public List<TypeInformation> getTypes();
@WebMethod(action = "urn:addBook")
@WebResult(name = "addBookReturn",
targetNamespace = "http://ws.sosnoski.com/library/wsdl")
@RequestWrapper(localName = "addBook",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.AddBook")
@ResponseWrapper(localName = "addBookResponse",
targetNamespace = "http://ws.sosnoski.com/library/wsdl",
className = "com.sosnoski.ws.library.jaxws.AddBookResponse")
public boolean addBook(
@WebParam(name = "type", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
String type,
@WebParam(name = "isbn", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
String isbn,
@WebParam(name = "author",
targetNamespace = "http://ws.sosnoski.com/library/wsdl")
List<String> author,
@WebParam(name = "title", targetNamespace = "http://ws.sosnoski.com/library/wsdl")
String title)
throws AddDuplicateFault
;
}
WsImport 工具將附帶的 WSDL 識別為匹配 “封裝的” 約定,並自動生成一個未封裝的服務接口。您可以從 清單 5 中看到效果,在此過程中,方法將單獨的值作為輸入參數,並直接返回任何合適的類型,而不是使用一個封裝器對象層(但是仍然將生成封裝器對象,然後供 JAX-WS 運行時在幕後使用)。
附帶的代碼再一次給出了服務和測試客戶機的實際實現。要親自嘗試,您需要對附帶的 build.properties 文件進行編輯,以設置到 Axis2 安裝和 JAX-WS 參考實現安裝的路徑。完成編輯後,在打開到 jaxws 目錄的控制台中輸入 ant 以從 WSDL 運行 JAX-WS 代碼生成,編譯附帶的代碼,並為服務器部署構建一個 JAR 文件。要運行測試客戶機,將生成的 JAR 文件復制到 Axis2 服務器安裝的 WEB-INF/servicejars 目錄中,然後在控制台中輸入 ant run。
客戶端 JAX-WS 使用
清單 6 展示了完整的測試客戶機代碼。如果將其與 清單 2 比較,您將觀察到未封裝接口和已封裝接口之間的不同,其中未封裝接口具有更好的編程友好性。
清單 6. JAX-WS 測試客戶機代碼
public class WebServiceClient
{
public static void main(String[] args) throws Exception {
// check for required command line parameters
if (args.length < 3) {
System.out.println("Usage:\n java " +
"com.sosnoski.ws.library.jaxws.WebServiceClient host port path");
System.exit(1);
}
// create the client stub
JaxwsLibrary service = new JaxwsLibrary();
Library stub = service.getLibrary();
// set the actual endpoint address
String target = "http://" + args[0] + ":" + args[1] + args[2];
System.out.println("Connecting to " + target);
BindingProvider provider = (BindingProvider)stub;
provider.getRequestContext().
put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, 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
List<TypeInformation> types = stub.getTypes();
System.out.println("Retrieved " + types.size() + " types:");
for (int i = 0; i < types.size(); i++) {
TypeInformation type = types.get(i);
System.out.println(" '" + type.getName() + "' with " +
type.getCount() + " books");
}
// add a new book
String title = "The Dragon Never Sleeps";
isbn = "0445203498";
try {
List<String> authors = new ArrayList<String>();
authors.add("Cook, Glen");
stub.addBook("scifi", isbn, authors, title);
System.out.println("Added '" + title + '\'');
title = "This Should Not Work";
stub.addBook("scifi", isbn, authors, title);
System.out.println("Added duplicate book - should not happen!");
} catch (AddDuplicateFault e) {
System.out.println("Failed adding '" + title +
"' with ISBN '" + isbn + "' - matches existing title '" +
e.getFaultInfo().getBook().getTitle() + '\'');
}
// get all books of a type
List<BookInformation> books = stub.getBooksByType("scifi");
System.out.println("Retrieved " + books.size() + " books of type 'scifi':");
for (int i = 0; i < books.size(); i++) {
System.out.println(" '" + books.get(i).getTitle() + '\'');
}
}
}
JAX-WS 客戶端處理通常要求在運行時訪問服務 WSDL,並且使用 WSDL 來初始化服務器鏈接。如果您已經獲知目標服務的 WSDL 始終可以在運行時直接從服務器獲得,並且該服務器將始終位於同一地址,那麼您可以將 WSDL URL 提供給 WsImport,並讓它將 URL 硬編碼到生成的代碼中。對於大多數嚴肅的工作,最好使用 WSDL 的本地副本,然後,如果目標服務地址不同於 WSDL 中的地址,那麼在運行時重寫該地址。附帶的構建文件采用了這種方法,而 清單 6 中粗體所示的代碼部分演示了如何在不修改 WSDL 的情況下在運行時修改服務地址。
服務器端 JAX-WS 使用
服務器端代碼的 JAX-WS 版本如 清單 7 所示。實現類的 @WebService 注釋(粗體顯示)將實現代碼與某個特定 Web 服務接口關聯起來。實現類的這個注釋允許您從生成的服務接口(清單 5)的相應注釋中重寫設置。在本例中,注釋將設置服務和端口名,並給出 WSDL 服務定義的位置(顯然 Axis2 希望該位置與類路徑的根相關,或是一個絕對 URL)。
清單 7. JAX-WS 服務器代碼
@javax.jws.WebService(endpointInterface="com.sosnoski.ws.library.jaxws.Library",
portName="library", targetNamespace="http://ws.sosnoski.com/library/wsdl",
wsdlLocation="com/sosnoski/ws/library/jaxws/library.wsdl", serviceName="JaxwsLibrary")
public class JaxwsLibraryImpl implements Library
{
private final BookServer m_server;
public JaxwsLibraryImpl() {
m_server = new BookServer();
}
public boolean addBook(String type, String isbn, List<String> author, String title)
throws AddDuplicateFault {
BookInformation prior = m_server.getBook(isbn);
if (prior == null) {
BookInformation book = new BookInformation();
book.getAuthor().addAll(author);
book.setIsbn(isbn);
book.setTitle(title);
book.setType(type);
return m_server.addBook(book);
} else {
AddDuplicate ad = new AddDuplicate();
ad.setBook(prior);
AddDuplicateFault e =
new AddDuplicateFault("Book already present with matching ISBN", ad);
throw e;
}
}
public BookInformation getBook(String isbn) {
return m_server.getBook(isbn);
}
public List<BookInformation> getBooksByType(String type) {
return m_server.getBooksByType(type);
}
public List<TypeInformation> getTypes() {
return m_server.getTypes();
}
}
清單 7 中的其余代碼僅僅是 清單 3 所示的(封裝)JAXB 示例的未封裝版本。
Axis2 中的 JAX-WS 問題
Axis2 在基礎 JAX-WS 處理方面表現得非常好,但它的確也存在一些限制。最嚴重的一點就是當您在 Axis2 中使用 JAX-WS 時,缺乏對 WS-Security 或其他 Web 服務擴展技術的支持(但是圍繞 Axis2 構建的應用服務器可能使用自己的方式實現配置和使用 WS-Security)。這對於企業應用程序來說是一個服務器限制,並且,除了一個稍微簡單些的配置方法外,不存在任何起到補償作用的優勢,似乎沒有足夠的理由說服人們馬上在 Axis2 中使用 JAX-WS。
結束語
在本文中,您了解了將 JAXB 2.x 和 JAX-WS 2.x Java 標准用於 Axis2 的基礎知識。Axis2 中的 JAXB 支持存在某些限制,但是,至少對那些不需要定制的簡單模式來說,它為 Axis2 中支持的其他數據綁定方法提供了一種有用的替代選擇。JAX-WS 支持的限制更多,目前只對那些不需要 WS-Security 或其他任何增值功能的簡單服務有用。
本系列目前為止一直關注 Apache Axis2 框架。有關 JAXB 和 JAX-WS 的介紹為研究其他一些同樣支持這些標准的開源 Java Web 服務框架提供了一個良好的起點。在下個月中,本專欄將研究由 Sun 開發的 Metro Web Services 框架以及 JAXB 和 JAX-WS 參考實現。我們將更深入地研究 JAX-WS 的使用和特性。
本文配套源碼