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 用于 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 秒超时设置),并会调用服务方法。
LibraryStub stub = new LibraryStub("http://localhost:8800/axis2/services/library"); stub.getServiceClient().getOptions().setTimeoutInMilliseconds(10000000); Types[] types = stub.getTypes();
清单 1 中的代码显示了同步方法调用,其中的客户机线程将阻塞在服务调用内,在调用完成且结果可用之后才会返回。Axis2 还支持使用回调接口进行异步调用。清单 2 显示了经过修改的清单 1 代码,其中使用了一个小小的异步调用(应用程序代码仅仅等待操作完成,而不进行任何有用的工作)。
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()
方法通过委托到实际实现类进行实现。
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 提供了一系列工具来帮助开发人员使用此框架。其中最重要的是允许从 WSDL 服务定义生成 Java 链接代码(在下面讨论)的工具和从现有 Java 代码生成 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 选项。稍后讨论数据绑定示例时会看到几个此类选项。
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
操作中涉及的部分。
<!----> <wsdl:definitions xmlns:tns="http://ws.sosnoski.com/library/types" targetnamespace="http://ws.sosnoski.com/library/wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wns="http://ws.sosnoski.com/library/wsdl"> <wsdl:types> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetnamespace="http://ws.sosnoski.com/library/wsdl" elementformdefault="qualified"></schema> <element name="getBook"></element> <complextype></complextype> <sequence></sequence> <element name="isbn" type="string"></element> <element name="getBookResponse"></element> <complextype></complextype> <sequence></sequence> <element name="getBookReturn" type="tns:BookInformation" minoccurs="0"></element> ... <schema xmlns="http://www.w3.org/2001/XMLSchema" targetnamespace="http://ws.sosnoski.com/library/types" elementformdefault="qualified"></schema> <complextype name="BookInformation"></complextype> <sequence></sequence> <element name="author" type="string" minoccurs="0" maxoccurs="unbounded"></element> <element name="title" type="string"></element> <attribute name="type" type="string" use="required"></attribute> <attribute name="isbn" type="string" use="required"></attribute> ... </wsdl:types> <wsdl:message name="getBookRequest"> <wsdl:part name="parameters" element="wns:getBook"></wsdl:part> </wsdl:message> <wsdl:message name="getBookResponse"> <wsdl:part name="parameters" element="wns:getBookResponse"></wsdl:part> </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:output> </wsdl:operation> ... </wsdl:porttype> <wsdl:binding name="LibrarySoapBinding" type="wns:Library"> <wsdlsoap:binding transport="http://schemas.xmlsoap.org/soap/http"></wsdlsoap:binding> <wsdl:operation name="getBook"> <wsdlsoap:operation soapaction="urn:getBook"></wsdlsoap:operation> <wsdl:input name="getBookRequest"> <wsdlsoap:body use="literal"></wsdlsoap:body> </wsdl:input> <wsdl:output name="getBookResponse"> <wsdlsoap:body use="literal"></wsdlsoap:body>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 构建文件。接下来,让我们分析三个数据绑定框架对应的客户机代码,了解每种方法的优缺点。
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
类。
// 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
类,甚至没有)有用数据,但生成的消息签名需要这些类。幸运的是,还有适用于很多常见服务的备用代码生成形式。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 仍然生成消息类,并在生成的代码中使用这些类,但代码通常会忽略这些类,而直接使用数据。
// 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.3 更新
本文发布时,Axis2 1.3 版已经快能够投入生产使用了。此部分讨论的 ADB 取消包装的问题已经得到修复,因此,如果您能够使用 Axis2 1.3 代码,使用 ADB 的体验将比 Axis2 早期的版本好得多。下载部分提供了本文示例代码特定于 V1.3 的版本。Axis2 1.2 的后续版本发布时,ADB 取消包装问题应该得到了极大的消除。但 ADB 并不是让 Axis2 支持取消包装的唯一数据绑定框架。JiBX 也提供取消包装支持,JiBX 版本从 Axis2 1.1.1 发布以来就稳定了。可以在本文稍后(讨论了 Axsi 2 数据绑定主要选项后)了解 JiBX 客户机代码。