清单 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 生成的类比其他数据绑定框架的对等项使用更为复杂。
(编辑:aniston)
|