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
(编辑:aniston)
|