Apache Axis2 1.1已发布,它为长期运行的Apache系列Web服务框架的爱好者提供了激动人心的新功能。 我们将在以后的文章中介绍Axis2本身,但是本文将深入探讨Axis2核心的AXI对象模型(AXIOM)XML文档模型。 AXIOM是Axis2背后的重大创新之一,也是Axis2能够提供比原始Axis更好的性能的潜力之一。 本文将指导您完成AXIOM的工作方式,如何在AXIOM上构建Axis2的各个部分,然后最后看一下AXIOM性能与其他Java™文档对象模型的比较。
文档模型是XML处理的常用方法,并且Java开发可以使用许多不同的样式,包括原始W3C DOM规范,JDOM,dom4j,XOM等的各种实现。 每个模型都具有相对于其他模型的某些优势,无论是性能,灵活性还是严格遵守XML标准,并且每个模型都有坚定的支持者。 那么,为什么Axis2需要一个新模型? 答案在于SOAP消息的结构,尤其是如何将扩展添加到基本SOAP框架中。
SOAP本身实际上只是XML应用程序有效负载的薄包装。 清单1给出了一个示例,其中SOAP实际定义的唯一部分是带有soapenv前缀的那些元素。 文档的大部分是构成soapenv:Body元素内容的应用程序数据。
尽管基本的SOAP包装器很简单,但它通过使用称为标头的可选组件提供了无限扩展的潜力。 该标题提供了各种添加,这将伴随应用程序数据,而不被应用程序看到的元数据的地方( 有可能包括在标题中的应用程序数据,但没有一个令人信服的情况下,为什么你会想要做到这一点,而不仅仅是将主体用于应用程序数据)。 基于SOAP的扩展(例如整个WS- *系列)可以将标头用于自己的目的,而不会影响应用程序。 这使这些扩展可以作为附件运行,在其中可以轻松地在部署时选择应用程序所需的特定扩展功能,而不必将其烘焙到代码中。
清单2显示了与清单1 SOAP中的示例相同的应用程序数据,但其中包含WS-Addressing信息。 虽然原始SOAP消息可能仅可通过HTTP传输使用(因为HTTP提供了双向连接,以便将立即响应发送回客户端),但是清单2版本可以在其他协议上运行,因为它包含响应元数据直接在SOAP请求消息中。 清单2中的消息处理涉及存储转发步骤,甚至很容易,因为元数据既提供了请求目标信息又提供了响应目标信息。
由于SOAP标头的全部目的是允许将任意元数据添加到消息中,因此SOAP框架能够接受某些扩展决定添加的内容非常重要。 一般来说,使用任意XML的最简单方法是使用一种形式或另一种形式的文档模型。 毕竟,这就是文档模型的全部要点-忠实地表示XML,而无需对XML的形式进行任何假设。
但是文档模型并不是使用XML的非常有效的方式,而XML用于在应用程序之间交换数据。 应用程序数据通常具有预定义的结构,并且大多数开发人员更喜欢以数据对象而不是原始XML的形式使用该数据。 在数据对象和XML之间进行转换的工作是在所谓的数据绑定领域。 数据绑定不仅为开发人员提供了比使用文档模型更方便的方法,而且在性能和内存使用方面都更加高效。
因此,大多数应用程序都希望使用数据绑定来处理SOAP消息的应用程序有效负载,但是文档模型方法更适合于处理标头中存在的元数据。 理想的方法是将SOAP框架内的两种技术结合起来,但是没有建立普通的文档模型来允许这样做。 他们希望使用整个文档,或者至少使用文档的整个子树。 实际上,它们并不是为仅适合文档的选定部分而设置的,因为最适合SOAP。
除了AXIOM,Axis和Axis2之间还有另一个变化。 原始Axis使用标准的推式(SAX)解析器来处理XML,而Axis2使用拉式(StAX)解析器。 通过推入方法,解析器可以控制解析操作-您为解析器提供要解析的文档和处理程序引用。 然后,当处理输入文档时,它使用处理程序对代码进行回调。 您的处理程序代码可以利用回调传递的信息,但不能影响解析(除非引发异常)。 另一方面,通过拉方法,解析器实际上是用于按需浏览文档组件的迭代器。
推和拉方法都有其用途,但是当涉及到逻辑上分离的组件(例如SOAP)的XML时,拉样式具有很大的好处。 使用拉式解析器,处理文档一部分的代码只能解析所需的内容,然后将解析器移交给文档处理中的下一部分。
AXIOM围绕StAX拉式解析器接口构建。 AXIOM提供了一个虚拟文档模型,它可以按需扩展,仅构建与客户端应用程序要求的一样多的树结构文档模型表示形式。 该虚拟文档模型在XML文档中的元素级别上起作用。 当解析器报告元素开始标记时,将创建一个元素表示形式,但是该元素的初始形式实质上只是一个外壳,其中包含对解析器的引用。 如果应用程序需要获取元素内容的详细信息,它只需通过调用接口的方法(例如org.apache.axiom.om.OMContainer.getChildren()方法)来请求信息。 然后,该元素根据方法调用从解析器构建子内容。
由于解析器按文档顺序交付数据(相同的订单项出现在XML文档文本中),因此AXIOM实现的按需构造需要一些巧妙的处理。 例如,使多个元素处于不完整(正在构建)状态是正常的,但是这些元素都必须位于直接的继承行中。 就XML的树形图的标准排序而言,根元素在顶部,不完整的元素将始终在树的右下方。 随着应用程序请求更多数据,该树将进一步向右扩展,首先完成底部元素。
所有XML文档模型在API方面都有很多共同点(不足为奇,因为它们都使用相同的基础数据),但是每种模型都有一些区别之处。 原始的W3C文档对象模型(DOM)是为实现跨语言和跨平台兼容性而设计的,因此它基于接口构建,并且避免使用特定于Java的集合来支持自己的版本。 JDOM使用具体的类而不是接口,并为许多Java开发人员发现比DOM更友好的API合并了标准Java集合类。 dom4j将类似DOM的接口与Java集合类结合在一起,从而提供了一种非常灵活的API,该API提供了很多功能-付出了一些复杂性。
AXIOM与其他文档模型有很多共同点。 它还具有与按需构建过程相关的一些重大差异,以及一些支持其在Web服务中使用的特殊功能。
从总体上看,AXIOM的API可能最接近DOM,但是它有自己的怪癖。 例如,访问方法的设计围绕使用java.util.Iterator实例来访问组件(由org.apache.axiom.om.OMContainer.getChildren()和相关方法返回),而不是任何形式的列表。 导航没有使用索引到组件列表,而是使用org.apache.axiom.om.OMNode.getNextOMSibling()和org.apache.axiom.om.OMNode.getPreviousOMSibling()方法在文档级别的节点之间顺序移动树(在这方面类似于DOM)。 访问和导航方法的这种结构与按需树结构的工作方式匹配,因为这意味着AXIOM可以让您移至起始元素的第一个子元素,而不必先处理所有子元素。
像DOM和dom4j一样,AXIOM定义了用于使用接口访问和操纵树表示的API。 AXIOM发行版包括这些接口的几种不同的专用实现。 一个实现(在org.apache.axiom.om.impl.dom包中)是双头的,以相同的实现类支持AXIOM和DOM接口。 由于预期可以与数据的DOM视图一起使用的Web服务附加组件数量众多,因此这很有用。 为了更通用, org.apache.axiom.om.impl.llom包提供了基于对象的链接列表(包名称的“ ll”部分)的实现。 org.apache.axiom.om基本接口和org.apache.axiom.soap包树中的这些实现都有扩展,这些扩展是为与SOAP消息一起使用而定制的。
为了快速了解实际使用的AXIOM API,我们将查看一些代码示例,这些代码用于针对其他文档模型进行AXIOM性能测试。 清单3根据用于构建输入文档的AXIOM表示的代码给出了第一个示例。 与DOM和dom4j一样,在使用AXIOM进行任何操作之前,您需要获得一个工厂来构建模型的组件对象。 清单3代码使用org.apache.axiom.org.OMFactory接口的org.apache.axiom.om.impl.llom.factory.OMLinkedListImplFactory实现选择AXIOM接口的基本链表实现。 工厂接口包括直接从各种来源构建文档的方法,还包括用于创建XML文档表示形式的各个组件的方法。 清单3使用该方法从输入流构建文档。 build()方法返回的对象实际上是org.apache.axiom.om.OMDocument的实例,尽管此代码未指定。
清单3使用org.apache.axiom.om.impl.builder.StAXOMBuilder类通过解析输入流来构建文档表示形式。 这只是在返回之前创建一个StAX解析器实例和基本文档结构,将解析器放置在文档的根元素内,并在需要时保留其余文档表示形式。 AXIOM 不必使用StAX的解析器来构建。 实际上, org.apache.axiom.om.impl.builder.SAXOMBuilder是基于SAX推送解析器的构建器的部分实现。 但是,如果以其他任何方式构建它,您将无法获得按需构建的好处。
清单4显示了用于“遍历”文档表示形式中的元素并累积摘要信息(元素的数量,属性值文本的数量和总长度以及文本内容的数量和总长度)的代码。 底部的walk()方法将文档与摘要数据结构一起进行汇总,而顶部的walkElement()方法则处理一个元素(递归调用以处理子元素)。
最后,清单5提供了用于将文档表示形式写入输出流的代码。 AXIOM定义了许多输出方法作为OMNode接口的一部分,包括目标的变体(作为输出流,普通字符OMNode器或StAX流OMNode器),带有或不带有格式信息,带有和不带有访问文档表示后的能力。编写(如果尚未构建,则需要构建完整的表示形式)。 OMElement接口通过从元素获取StAX解析器,定义了另一种访问文档信息的方式。 使用解析器将XML从表示中拉出的能力提供了一种吸引人的对称性,并且当按需构建AXIOM表示时,该功能很好地工作(因为可以直接返回用于构建表示的解析器)。
AXIOM提供了一些用于修改现有文档组件的基本方法(例如OMElement.setText()以将元素的内容设置为文本值)。 如果从头开始,则需要直接创建组件的新实例。 由于AXIOM API基于接口,因此它使用工厂来创建组件的实际实现。
有关AXIOM API的更多详细信息,请参见参考资料部分中AXIOM教程和JavaDocs的链接。
AXIOM最有趣的功能之一是它对最新版本的SOAP附件中使用的W3C XOP和MTOM标准的内置支持。 这两个标准协同工作,XML二进制优化包装(XOP)为XML文档提供了一种逻辑上包含任意二进制数据的斑点的方法,并且与将XOP技术应用于SOAP消息的MTOM(SOAP消息传输优化机制)一起使用。 XOP和MTOM是新一代Web服务框架的关键功能,因为它们最终提供了可互操作的附件支持并结束了该领域中的当前问题。
XOP处理base64编码的字符数据内容。 Base64编码通过使用一个ASCII字符表示原始数据的每个六位,将任意数据值转换为可打印的ASCII字符。 由于二进制数据通常不能嵌入XML内(XML仅适用于字符,而不适用于原始字节; XML中甚至不允许使用许多字符代码),因此base64编码对于将二进制数据嵌入XML消息很有用。
XOP用XOP名称空间中的特殊“包含”元素替换了实际的base64文本。 include元素提供一个URI,该URI标识一个单独的实体(在XML文档外部),该实体是要包含在XML文档中的实际数据。 通常,此URI将在与XML文档相同的传输中标识一个单独的块(尽管没有要求这样做,这为通过中介交换文档或存储文档提供了潜在的好处)。 参照原始数据替换base64文本的好处是,文档大小有所减小(对于常规字符编码,最大减小了25%),并且处理速度更快,而没有对base64数据进行编码和解码的开销。
MTOM建立在XOP的基础上,首先定义一个如何将XOP用于SOAP消息的抽象模型,然后专门将该模型与MIME Multipart / Related打包一起使用,最后将其应用于HTTP传输。 总之,这提供了一种使用广泛使用的HTTP传输将XOP应用于SOAP消息的标准方法。
AXIOM通过org.apache.AXIOM.om.OMText接口以及该接口的实现来支持XOP / MTOM。 OMText定义了一些方法来支持由二进制数据支持的文本项(以javax.activation.DataHandler的形式,该形式是Java Activation API的一部分,广泛用于Java Web服务框架中的附件支持),以及一个“ optimize”标志来指示是否可以使用XOP处理项目。 org.apache.AXIOM.om.impl.llom.OMTextImpl实现添加了与MTOM兼容的内容ID,该ID可在创建类的实例时设置,如果未设置则自动生成。
清单6显示了如何在AXIOM中使用XOP / MTOM构建消息的示例。 该代码取自一个性能测试示例,该示例使用Java序列化将结果数据结构转换为字节数组,然后将该数组作为附件返回。
即使清单6中的代码生成了可以使用XOP / MTOM发送的响应,但在当前版本的Axis2中,默认情况下仍禁用XOP / MTOM支持。 要启用它,您需要在服务的Axis2 axis2.xml文件或services.xml文件中包含参数<parameter name="enableMTOM">true</parameter> 。 作为即将进行的性能比较的一部分,我们将提供此示例的完整代码,但现在,我们将使用一个XOP / MTOM示例结束。
清单7显示了清单6服务生成的响应消息的结构,其中启用和不启用XOP / MTOM(在第一种情况下,没有MIME头和实际的二进制附件,而在第二种情况下,大部分数据被忽略了)。第二种情况)。
--
大多数使用Web服务的开发人员需要使用Java对象形式的数据,而不是XML文档(甚至是文档模型,例如AXIOM)。 诸如Axis和JAX-RPC之类的最后一代框架实现了它们自己的数据绑定形式,以在XML和Java对象之间进行转换,但这是一个非常有限的解决方案。 Web服务框架中的数据绑定实现通常无法与专用数据绑定框架竞争,因此想要更好地控制XML处理的用户必须将框架与效率低下的转换代码“粘合”在一起。 由于这些问题,Axis2从一开始就被设计为使用各种数据绑定框架来支持“插件”数据绑定。
数据绑定支持使用Axis2中包含的WSDL2Java工具的自定义扩展。 该工具基于WSDL服务描述(以客户端的存根或服务器端的消息接收方的形式)生成Axis2链接代码。 客户端存根用作对服务进行调用的代理,定义实现服务操作的方法调用。 服务器端消息接收器充当客户端的代理,调用实际的用户定义的服务方法。 在WSDL2Java命令行中请求数据绑定时,该工具将调用指定的数据绑定框架扩展,以在存根或消息接收器中生成代码,以在OMElement和Java对象之间进行转换。 对于存根,将在方法调用中传递Java对象(或原始值),并将转换后的XML作为请求发送到服务器。 然后,将返回的XML转换回Java对象,然后将其作为方法调用的结果返回。 服务器端的消息接收器反向执行相同的转换。
在入站或解组方面(将收到的XML转换为Java对象),处理起来很容易。 将要转换为Java对象的XML文档有效负载以OMElement的形式提供,并且数据绑定框架仅需要处理来自该元素的数据。 OMElement以StAX javax.xml.stream.XMLStreamReader的形式提供对元素数据的访问,大多数当前数据绑定框架都可以将其直接用作输入。
出站或编组(将Java对象转换为传输的XML)要困难一些。 除非绝对必要,否则AXIOM的全部重点是避免构建XML数据的完整表示。 为了在编组时支持此原理,必须有一种方法仅在需要时才调用数据绑定框架。 AXIOM通过使用org.apache.AXIOM.om.OMDataSource作为数据绑定转换的包装器来处理此问题。 OMDataSource定义使用AXIOM支持的任何方法(到java.io.OutputStream , java.io.Writer或StAX javax.xml.stream.XMLStreamWriter )写出包装内容的方法,以及另一种方法返回包装内容的解析器实例。 OMElement实现可以使用OMDataSource的实例OMDataSource提供数据,并且OMFactory接口提供了一种创建此类元素的方法。
我们将通过快速了解性能来总结AXIOM的内容。 在撰写本文时,AXIOM可以作为1.1版本使用,这意味着本文中描述的接口应该是稳定的。 另一方面,随着实际实现代码的修改,性能可能会随时间变化。 与其他文档模型相比,我们预计AXIOM的性能不会出现重大变化,但是某些细节可能会有所不同。
为了将AXIOM与其他文档模型进行比较,我们更新了文档模型的早期研究中的代码(请参阅参考资料部分),添加了AXIOM和另一个新文档模型(XOM),将代码转换为使用StAX解析器标准,而不是旧的SAX。标准,然后切换到Java 5中引入的改进的System.nanoTime()计时方法。性能测试代码首先将要在测试中使用的文档读取到内存中,然后对这些文档执行一系列操作。 首先,从解析器构建一些文档表示形式的副本,并保留每个结果文档对象。 接下来,对每个文档对象进行“遍历”,这意味着代码将扫描整个文档表示形式(包括所有属性,值和文本内容)。 最后,将每个文档对象写入输出流。 记录每个单独操作的时间,并在测试运行结束时取平均值。
由于AXIOM(尤其是在Axis2中的使用)的主要重点是SOAP消息处理,因此我们使用了三种不同的SOAP消息测试用例来检查性能。 第一个是来自Web服务性能测试的样本响应,该响应提供了有关特定时间和纬度/经度范围(“地震”,18 KB)内地震的信息。 本文档包含许多重复的元素,并带有一些嵌套和对属性的大量使用。 第二个是来自Microsoft WCF互操作性测试的较大示例,该示例由重复的单个结构组成,其值略有变化(“人”,202 KB)。 该文档的子元素带有文本内容,但不使用属性。 第三个测试用例是从一些较旧的互操作性测试中提取的30个小型SOAP消息文档的集合(总大小为19 KB)。 从测试文档中删除了所有格式化空白,以使文档代表实际生产服务(通常会关闭格式化以减小消息大小)交换的XML。
下图显示了每个文档进行50次传递所需的平均时间。 测试环境为带有1600 MHz ML-30 AMD Turion处理器和1.5 GB RAM的Compaq笔记本系统,该系统在Mandriva 2006 Linux上运行Sun的1.5.0_07-b03 JVM。 我们从Nux 1.6发行版测试了AXIOM 1.0,dom4j 1.6.1,JDOM 1.0,Xerces2 2.7.0和XOM。 来自StAX解析器的定制构建器用于dom4j,JDOM和Xerces2,而Nux StAX解析器构建器用于XOM。 所有测试均使用Woodstox StAX解析器2.9.3。
图1显示了测试序列中前两个步骤,从解析器构建文档以及遍历文档表示以检查内容所需的平均时间之和。 如果您单独看第一步(此处未显示时间),那么至少在前两个文档中,AXIOM比其他文档模型要好得多。 但是,这仅表明AXIOM正在按预期工作,并且直到需要时才真正构建完整文档。 我们希望使用在内存中实际构建完整表示形式所花费的时间进行公平的比较,这就是为什么此图表中的两次被求和的原因。
如图1所示,除了Xerces2之外,AXIOM总体上比任何其他测试的文档模型都要慢。
在收集小型文档(“肥皂”)时,一些文档模型显示出性能问题。 Xerces2在这种情况下特别糟糕,但是AXIOM也显示出很多开销,这可能是此图表显示的最麻烦的问题。 小消息在许多Web服务中很常见,并且AXIOM应该能够有效地处理它们。 鉴于AXIOM确实是围绕树的按需扩展设计的,因此两个较大的文档的时间安排不是什么大问题,因为它们至少与其他文档模型很接近。
图2显示了使用每种模型将文档写入输出流的平均时间。 在这里,Xerces2实际上提供了最佳的时间(但是,不足以弥补其在构建步骤中的不良性能;两个图表的比例不同),而AXIOM是最差的。 同样,AXIOM在处理小文档方面似乎做得特别差。
最后,图3显示了每个框架用来表示文档的内存。 dom4j仍然是最好的,而AXIOM则是最差的。 AXIOM内存使用性能不佳的部分原因是解析器被构造的文档引用,因此只要使用文档实例,解析器实例就会保留下来。 这可能是AXIOM在处理小文档方面再次表现特别差的部分原因。 但是,AXIOM用作文档组成部分的对象也比其他文档模型中的对象大得多,而这种差异可能就是为什么AXIOM甚至为两个较大的测试文档使用更大空间的原因(固定大小的开销解析器和其他数据结构的大小与总内存使用量成正比)。
如果将前两个图表的时间相加,则总体性能领先者是dom4j,而性能落后者是Xerces2(AXIOM只是后者的一个中端)。 在内存使用方面,dom4j仍然是最好的,但是在本次比赛中,AXIOM无疑是输家。 AXIOM听起来有些冷酷吗?
如果始终建立完整的树表示,那将是严峻的情况,但请记住,AXIOM的要点是通常不需要此完整的表示。 图4显示了仅在AXIOM中从初始文档构建开始的时间,以及其他文档模型用于构建文档表示形式的相应时间。 此处,AXIOM的发布速度比其他文件快得多(在两个较大的文档中,它甚至无法注册)。 同样的比较适用于内存方面。 最终结果是,如果您只需要使用文档模型的一部分(按文档顺序的术语是“第一部分”),则AXIOM可以提供出色的性能。
本文提供了Axis2核心的AXIOM文档对象模型的内部视图。 AXIOM体现了一些有趣的创新,尤其是在构建完整表示形式的按需构建方法方面。 当您需要完全扩展的表示形式时,它与其他Java文档模型并不完全相同。 它在处理小文档时的性能尤其令人不安,但是它为Web服务处理提供的灵活性平衡了许多这些问题。
现在,您知道Axis2如何使用AXIOM处理SOAP消息表示形式,包括它如何将XML与数据绑定框架之间来回传递。 下一篇文章从用户角度研究Axis2对不同数据绑定框架的支持,包括使用三个框架的代码示例。
翻译自: https://www.ibm.com/developerworks/java/library/j-java2/index.html
相关资源:webservice客户端,axiom-api,axis2-adb