第 41 章 使用 Raw XML 消息

摘要

高级别 JAX-WS API 防止开发人员使用原生 XML 消息,将数据放入 JAXB 对象。然而,在有些情况下,最好直接访问传输的原始 XML 消息数据。JAX-WS API 提供了两个接口,它提供对原始 XML 的访问: Dispatch 接口是客户端侧接口,而 Provider 接口则是服务器端接口。

41.1. 在 Consumer 中使用 XML

摘要

Dispatch 接口是一个低级的 JAX-WS API,可让您直接使用原始消息。它接受并返回消息或有效负载,包括 DOM 对象、SOAP 消息和 JAXB 对象。由于它是低级 API,所以 Dispatch 接口不执行任何消息准备更高级别的 JAX-WS API 执行。您必须确保传递给 Dispatch 对象的消息或有效负载会被正确构建,并对被调用的远程操作有意义。

41.1.1. 使用模式

概述

分配对象有两个 使用模式

您为 Dispatch 对象指定的使用模式决定了传递给用户级别代码的详细信息量。

消息模式

在消息模式中,Dispatch 对象可用于完整的消息。完整的消息包括任何绑定特定标头和打包程序。例如,与需要 SOAP 消息的服务交互的消费者必须提供 Dispatch 对象的 invoke() 方法,这是完全指定的 SOAP 消息。invoke() 方法还返回完全指定的 SOAP 消息。使用者代码负责完成和读取 SOAP 消息的标头和 SOAP 消息的信封信息。

在使用 JAXB 对象时,消息模式并不理想。

要指定 Dispatch 对象使用消息模式,在创建 Dispatch 对象时提供 java.xml.ws.Service.Mode.MESSAGE 的值。有关创建 Dispatch 对象的更多信息,请参阅 “创建 Dispatch 对象”一节

有效负载模式

有效负载模式中,也称为消息有效负载模式,Dispatch 对象只能用于消息有效负载。例如,在有效负载模式下工作的 Dispatch 对象只适用于 SOAP 消息的正文。绑定层处理任何绑定级别打包程序和标头。当从 invoke() 方法返回结果时,绑定级别打包程序和标头已经被剥离,并且只保留消息的正文。

在使用不使用特殊打包程序的绑定时,如 Apache CXF XML 绑定、有效负载模式和消息模式提供相同的结果。

要指定 Dispatch 对象使用有效负载模式,在创建 Dispatch 对象时提供 java.xml.ws.Service.Mode.PAYLOAD 值。有关创建 Dispatch 对象的更多信息,请参阅 “创建 Dispatch 对象”一节

41.1.2. 数据类型

概述

由于 Dispatch 对象是低级对象,所以它们不会被优化来使用与更高级别的消费者 API 相同的 JAXB 生成的类型。分配对象可用于以下类型对象:

使用 Source 对象

Dispatch 对象接受并返回来自 javax.xml.transform.Source 接口的对象。源对象由任何绑定支持,也可以在消息模式或有效负载模式下支持。

源对象是存放 XML 文档的低级别对象。每个 Source 实施都提供访问存储的 XML 文档的方法,然后操作其内容。以下对象实施 Source 接口:

DOMSource
将 XML 消息作为文档对象模型(DOM)树保存。XML 消息存储为一组 Node 对象,这些对象通过 getNode() 方法访问。可以使用 setNode() 方法更新或添加到 DOM 树。
SAXSource
将 XML 消息作为 XML(SAX)对象的简单 API 保存。SAX 对象包含一个 InputSource 对象,其中包含原始数据和 XMLReader 对象来解析原始数据。
StreamSource
将 XML 消息作为数据流保存。数据流可以像任何其他数据流一样操作。

如果您创建 Dispatch 对象使其使用通用 Source 对象,Apache CXF 会将消息返回为 SAXSource 对象。

可以使用端点的 source-preferred-format 属性来更改此行为。有关配置 Apache CXF 运行时的详情,请参考 第 IV 部分 “配置 Web 服务端点”

使用 SOAPMessage 对象

当以下条件为 true 时,分配对象可以使用 javax.xml.SOAPMessage 对象:

  • Dispatch 对象使用 SOAP 绑定
  • Dispatch 对象使用消息模式

SOAPMessage 对象包含 SOAP 消息。它们包含一个 SOAPPart 对象和零个或多个 AttachmentPart 对象。SOAPPart 对象包含 SOAP 消息的特定部分,包括 SOAP envelope、任何 SOAP 标头和 SOAP 消息正文。AttachmentPart 对象包含作为附件传递的二进制数据。

使用 DataSource 对象

当以下条件满足时,分配对象可以使用实现 javax.activation.DataSource 接口的对象:

  • Dispatch 对象使用 HTTP 绑定
  • Dispatch 对象使用消息模式

数据源对象提供了一种机制,用于处理各种来源中的数据,包括 URL、文件和字节数组。

使用 JAXB 对象

虽然 Dispatch 对象应该是低级 API,但可让您使用原始消息,但它们还允许您使用 JAXB 对象。若要搭配 JAXB 对象使用 Dispatch 对象,必须传递一个 JAXBContext,该对象可以使用的 marshal 和 unmarshal 和 unmarshal。在创建 Dispatch 对象时,会传递 JAXBContext

您可以将 JAXBContext 对象理解的任何 JAXB 对象作为参数传递给 invoke() 方法。您也可以将返回的消息转换为由 JAXBContext 对象理解的任何 JAXB 对象。

有关创建 JAXBContext 对象的详情,请参考 第 39 章 使用 A JAXBContext 对象

41.1.3. 使用 Dispatch 对象

流程

要使用 Dispatch 对象调用远程服务,应遵循以下序列:

  1. 创建 Dispatch 对象。
  2. 构建 请求消息。
  3. 调用正确的 invoke() 方法。
  4. 解析响应消息。

创建 Dispatch 对象

要创建 Dispatch 对象,请执行以下操作:

  1. 创建一个 Service 对象来代表 wsdl:service 元素,用于定义 Dispatch 对象要调用的服务。请参阅 第 25.2 节 “创建服务对象”
  2. 使用 Service 对象的 createDispatch() 方法创建 Dispatch 对象,如 例 41.1 “createDispatch() 方法”

    例 41.1. createDispatch() 方法

    publicDispatch<T&gt;createDispatchQNameportNamejava.lang.Class<T&gt;typeService.ModemodeWebServiceException

    注意

    如果使用 JAXB 对象,则 createDispatch() 的方法签名是:publicDispatch<T&gt;createDispatchQNameportNamejavax.xml.bind.JAXBContextcontextService.ModemodeWebServiceException

    表 41.1 “createDispatch()的参数” 描述 createDispatch() 方法的参数。

    表 41.1. createDispatch()的参数

    参数描述

    portName

    指定 wsdl:port 元素的 QName,它代表 Dispatch 对象发出调用的服务供应商。

    type

    指定 Dispatch 对象使用的对象类型。请参阅 第 41.1.2 节 “数据类型”。在使用 JAXB 对象时,此参数指定用于聚合和联合 JAXB 对象的 JAXBContext 对象。

    模式

    指定 Dispatch 对象的使用模式。请参阅 第 41.1.1 节 “使用模式”

例 41.2 “创建 Dispatch 对象” 显示用于在有效负载模式下创建与 DOMSource 对象搭配使用的 Dispatch 对象的代码。

例 41.2. 创建 Dispatch 对象

package com.fusesource.demo;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;

public class Client
{
public static void main(String args[])
  {
    QName serviceName = new QName("http://org.apache.cxf", "stockQuoteReporter");
    Service s = Service.create(serviceName);

    QName portName = new QName("http://org.apache.cxf", "stockQuoteReporterPort");
    Dispatch<DOMSource> dispatch = s.createDispatch(portName,
                                                  DOMSource.class,
                                                  Service.Mode.PAYLOAD);
    ...

构建请求消息

使用 Dispatch 对象时,必须从头开始构建请求。开发人员负责确保传递给 Dispatch 对象的消息与目标服务提供商可以处理的请求匹配。这需要精确了解服务提供商使用的消息,以及它所需的标题信息。

此信息可由 WSDL 文档或定义消息的 XML 架构文档提供。虽然服务提供商有很大不同,但遵循一些指南:

  • 请求的根元素基于与正在调用 的操作 对应的 wsdl:operation 元素的值。

    警告

    如果正在调用的服务使用 doc/literal 裸机消息,则请求的根元素将基于 wsdl:operation 元素引用的 wsdl:part 元素的值。

  • 请求的根元素是命名空间限定。
  • 如果正在调用的服务使用 rpc/literal 消息,则请求中的顶层元素将不会被命名空间限定。

    重要

    顶级元素的子项可能是命名空间限定。要保证您必须检查其模式定义。

  • 如果正在调用的服务使用 rpc/literal 消息,则所有顶级元素都不能为空。
  • 如果正在调用的服务使用 doc/literal 消息,则消息的 schema 定义会确定任何元素是否合格。

有关服务如何使用 XML 消息的更多信息,请参阅 WS-I 基本配置集

同步调用

对于执行生成响应的同步调用的用户,请使用 例 41.3 “Dispatch.invoke() 方法” 中显示的 Dispatch 对象的 invoke() 方法。

例 41.3. Dispatch.invoke() 方法

TinvokeTmsgWebServiceException

在创建了 Dispatch 对象时,决定传递到 invoke() 方法的响应和传递到 invoke()方法的请求类型。例如,如果您使用 createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE) 创建 Dispatch 对象,则响应和请求都是 SOAPMessage 对象。

注意

使用 JAXB 对象时,响应和请求可以是任何类型的 JAXBContext 对象,可以总结和解包。此外,响应和请求可以是不同的 JAXB 对象。

例 41.4 “使用 Dispatch 对象生成 Synchronous Invocation” 显示使用 DOMSource 对象对远程服务进行同步调用的代码。

例 41.4. 使用 Dispatch 对象生成 Synchronous Invocation

// Creating a DOMSource Object for the request
DocumentBuilder db = DocumentBuilderFactory.newDocumentBuilder();
Document requestDoc = db.newDocument();
Element root = requestDoc.createElementNS("http://org.apache.cxf/stockExample",
                                          "getStockPrice");
root.setNodeValue("DOW");
DOMSource request = new DOMSource(requestDoc);

// Dispatch disp created previously
DOMSource response = disp.invoke(request);

异步调用

分配对象也支持异步调用。与 第 40 章 开发同步应用程序 中讨论的更高级别异步 API 一样,Dispatch 对象都可以使用轮询方法和回调方法。

使用轮询方法时,调用Async() 方法会返回一个 Response<t& gt; 对象,该对象可以轮询来查看响应是否已到达。例 41.5 “用于轮询的 Dispatch.invokeAsync() 方法” 显示使用轮询方法进行异步调用的方法的签名。

例 41.5. 用于轮询的 Dispatch.invokeAsync() 方法

响应 <T&gt; 调用AsyncTmsgWebServiceException

有关使用轮询方法进行异步调用的详情,请参考 第 40.4 节 “使用轮询方法实施同步客户端”

在使用回调方法时,调用Async() 方法采取 AsyncHandler 实施,在返回时处理响应。例 41.6 “使用回调的 Dispatch.invokeAsync() 方法” 显示使用回调方法进行异步调用的方法的签名。

例 41.6. 使用回调的 Dispatch.invokeAsync() 方法

future<?&gt; 调用AsyncTmsgAsyncHandler<T&gt;handlerWebServiceException

有关使用回调方法进行异步调用的详情,请参考 第 40.5 节 “使用回调方法实施同步客户端”

注意

与同步 调用() 方法一样,在创建 Dispatch 对象时,响应的类型和请求类型决定。

Oneway invocation

当请求没有生成响应时,使用 Dispatch 对象的 calls OneWay()进行远程调用例 41.7 “Dispatch.invokeOneWay() 方法” 显示此方法的签名。

例 41.7. Dispatch.invokeOneWay() 方法

调用OneWayTmsgWebServiceException

用于在创建 Dispatch 对象时确定用于打包请求的对象类型。例如,如果使用 createDispatch(portName, DOMSource.class, Service.Mode.PAYLOAD) 创建 Dispatch 对象,则请求被打包为 DOMSource 对象。

注意

使用 JAXB 对象时,响应和请求可以是任何类型的 JAXBContext 对象,可以总结和解包。

例 41.8 “使用 Dispatch 对象以一个方式调用” 演示了使用 JAXB 对象对远程服务进行单向调用的代码。

例 41.8. 使用 Dispatch 对象以一个方式调用

// Creating a JAXBContext and an Unmarshaller for the request
JAXBContext jbc = JAXBContext.newInstance("org.apache.cxf.StockExample");
Unmarshaller u = jbc.createUnmarshaller();

// Read the request from disk
File rf = new File("request.xml");
GetStockPrice request = (GetStockPrice)u.unmarshal(rf);

// Dispatch disp created previously
disp.invokeOneWay(request);