IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services  >

Web 服务编程技巧与窍门: 用 SAAJ 和 JAX-RPC 构建 SOAP 响应信封

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 初级

Andre Tost (atost@us.ibm.com), 高级认证 IT 专家, IBM Software Group

2004 年 9 月 01 日

本技巧是对上一篇技巧——“将 <xsd:any/> 元素用于自定义序列化”的深入探究,它描述了如何使用 <xsd:any/> 元素自定义序列化。由于上一篇技巧着重于读取和处理 javax.xml.soap.SOAPElement,所以在这里作者描述了如何创建它。

在技巧“将 <xsd:any/> 元素用于自定义序列化”中(请参阅 参考资料),我向您描述了如何将 <xsd:any/> 元素用于自定义序列化,以及如何将这个元素映射到 javax.xml.soap.SOAPElement 类型的 Java 对象。代码示例显示了客户端如何使用这个接口来手动解析从现有的 Web 服务中返回的响应。在本技巧中,我把重点放在了服务器端。我向您描述了如何使用 SOAP API for Attachments in Java (SAAJ) 在 Web 服务实现类中创建响应消息。

回顾 <xsd:any/> 元素

我再简要的讨论一下 <xsd:any/> 元素是怎样来帮助自定义创建和解析 SOAP 消息的。这个元素代表 XML 模式中任意的 XML 内容。换而言之,您可以把它当作您的模式中的通配符。JAX-RPC 引擎不知道这个内容在运行时是什么样,因此将它作为核心 SAAJ 接口中的一个实例 —— javax.xml.soap.SOAPElement ,传送给服务器或者客户端。您可以通过将您的模式中想通过 SAAJ 处理的元素替换为 <xsd:any/> 来利用这种做法。通常,您只在需要生成正确形式的 JAX-RPC 帮助代码时才要做这样的替换,并且最后能保持 WSDL 文件中模式的最初版本。

JAX-RPC 版本 1.1 要求符合规范的引擎必需提供工具,让用户选择是否进行 类型映射 (type-mapping)。这样做和使用 <xsd:any/> 元素产生的效果相似,即引擎不用试图将 XML 内容映射到 JAVA 内容,反之亦然,但使用的是 javax.xml.soap.SOAPElement 接口。

下面描述了如何平衡运用这两种方法以利用 SAAJ 构建 SOAP 响应消息。





回页首


服务端点接口

在 JAX-RPC 中,每个 Web 服务都由一个服务端点接口 (Service Endpoint Interface,SEI) 表示。它主要是将 WSDL portType 映射到 Java 类型,从而让您通过本地 Java 代理类消费 Web 服务,或者是通过执行 SEI 来提供 Web 服务。

通过 javax.xml.soap.SOAPElement 在 SEI 中的出现情况,您很容易就可以检测到 SAAJ 是否用于 Web 服务。如果 SEI 的一个方法使用这个接口作为输入参数,说明传入的 SOAP 请求消息没有被映射到 Java 类型。如果方法返回一个 javax.xml.soap.SOAPElement 的实例,说明响应消息没有被分别映射。

在本实例中,您可以生成两个不同的 SEI,一个用于服务器端,使用 javax.xml.soap.SOAPElement ,另一个用于客户端,使用 Java 类型映射。在这里我只重点讨论服务器端,但是您可以通过单击本文顶部或底部的 Code 图标下载整个实例,包括客户端。

下面是从 WSDL 文件中抽取的用于生成服务端点接口的一段代码:


清单 1.使用 <xsd:any/> 元素的 WSDL 摘录
<wsdl:types>
   <schema elementFormDefault="qualified" 
           targetNamespace="http://any.webservices.ibm.com" 
           xmlns="http://www.w3.org/2001/XMLSchema">
      <element name="getOrder">
         <complexType>
            <sequence>
               <element name="searchCriteria" nillable="true" type="xsd:string"/>
            </sequence>
         </complexType>
      </element>
      <element name="getOrderResponse">
         <complexType>
            <sequence>
               
        <xsd:any maxOccurs="unbounded"/>
            </sequence>
         </complexType>
      </element>
   </schema>
</wsdl:types>
      

注意,响应消息只包含一个元素,即 <xsd:any/> 。这里是 SEI:


清单 2.服务端点接口
package com.ibm.webservices.any;
import javax.xml.soap.SOAPElement;
public interface OrderManager extends java.rmi.Remote {
    public SOAPElement[] getOrder(java.lang.String searchCriteria) throws 
    java.rmi.RemoteException;
}

正如您所看到的,响应作为一组 javax.xml.soap.SOAPElement 实例被返回。





回页首


响应消息

在进一步研究服务实现之前,我描述一下 SOAP 消息的结构,您能够在实现中创建 SOAP 消息。切记,不能从我上面列出的 WSDL 摘录中导出消息的结构。这个定义包含在最初的 XML 模式中,也就是在生成客户端代码前向 WSDL 文件中添加的那段代码:


清单 3.完整的 XML Schema
 <wsdl:types>
  <schema elementFormDefault="qualified" targetNamespace=
  "http://any.webservices.ibm.com" xmlns="http://www.w3.org/2001/XMLSchema">
   <element name="getOrder">
    <complexType>
     <sequence>
      <element name="searchCriteria" nillable="true" type="xsd:string"/>
     </sequence>
    </complexType>
   </element>
   <complexType name="Order">
    <sequence>
     <element name="createDate" nillable="true" type="xsd:dateTime"/>
     <element name="customer" nillable="true" type="xsd:string"/>
     <element maxOccurs="unbounded" name="lineItems" nillable="true" type=
     "impl:LineItem"/>
    </sequence>
   </complexType>
   <complexType name="LineItem">
    <sequence>
     <element name="itemDesc" nillable="true" type="xsd:string"/>
     <element name="itemNumber" nillable="true" type="xsd:string"/>
    </sequence>
   </complexType>
   <element name="getOrderResponse">
    <complexType>
     <sequence>
      
        <element name="getOrderReturn" nillable="true" type="impl:Order"/>
     </sequence>
    </complexType>
   </element>
  </schema>
 </wsdl:types>
      

这里是符合上面模式的一个 SOAP 响应消息的例子:


清单 4.一个样本 SOAP 消息
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getOrderResponse xmlns="http://any.webservices.ibm.com">
<getOrderReturn>
      <createDate>2004-07-03T22:57:45.359Z</createDate>
      <customer>Bill Smith</customer>
      <lineItems>
<itemDesc>This is a line item</itemDesc>
<itemNumber>12345</itemNumber>
      </lineItems>
</getOrderReturn>
</getOrderResponse>
</soapenv:Body>
</soapenv:Envelope>

这是您在服务实现类中创建的消息。





回页首


Web 服务实现

通常,JAX-RPC 工具从 WSDL 文件生成 SEI 的同时也为实际服务实现生成一个框架。在实现中,您需要创建 SEI 定义的 javax.xml.soap.SOAPElement 实例。这将引导您深入到 SAAJ 编程的领域中去。

这里,我将讨论构建适当的响应元素所必需的每一个步骤。 javax.xml.soap.SOAPElement 的实例是通过 javax.xml.soap.SOAPFactory 类型的工厂来创建的,所有您首先需要创建工厂。而且,每个元素需要一个名称,在 SAAJ 中,名称由 javax.xml.soap.Name 接口表示。 Name 实例不是通过工厂创建的,但是,相反您还需要为它准备一个 javax.xml.soap.SOAPEnvelope 实例。这个实例也是通过工厂创建的,即一个 javax.xml.soap.MessageFactory 类型实例。对您可能提出的疑问做一个回答:确实如此,在开始创建实际的元素之前,您的确需要三个不同的工厂。


清单 5.创建 SAAJ 工厂实例
...
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();	
		
SOAPFactory factory = SOAPFactory.newInstance();
...

现在,开始准备创建响应消息的内容。注意,这段代码大部分都是重复性的,您很容易就可以将它们写得比这里显示的更高效些。而且我没有在这个例子中插入任何错误处理,但是,忽略它可能它将不适合于任何产品级别代码:


清单 6.创建 SAAJ 元素
...
// use the factory to create a new element
// the Name is created via the SOAPEnvelope, and we must define a 
// namespace for each element in the body (to be WS-I compliant)
SOAPElement getOrderReturn = factory.createElement(envelope.createName(
"getOrderReturn", "","http://any.webservices.ibm.com"));
// now start adding children and build the full response message
// hardcode the text content for the sakes of simplicity
SOAPElement createDate = getOrderReturn.addChildElement(envelope.createName(
"createDate", "", "http://any.webservices.ibm.com"));
createDate.addTextNode("2004-07-03T22:57:45.359Z");
SOAPElement customer = 
getOrderReturn.addChildElement(envelope.createName(
"customer", "", "http://any.webservices.ibm.com"));
customer.addTextNode("Bill Smith");
SOAPElement lineItems = 
getOrderReturn.addChildElement(envelope.createName(
"lineItems", "", "http://any.webservices.ibm.com"));
SOAPElement itemDesc = 
lineItems.addChildElement(envelope.createName(
"itemDesc", "", "http://any.webservices.ibm.com"));
itemDesc.addTextNode("This is a line item");
SOAPElement itemNumber = 
lineItems.addChildElement(envelope.createName(
"itemNumber", "", "http://any.webservices.ibm.com"));
itemNumber.addTextNode("12345");
...

最后,向 javax.xml.soap.SOAPElement 数组中添加一个名为 getOrderReturn 的元素(记住将 <xsd:any/> 元素的 maxOccurs 属性定义为“unbounded”):


清单 7.构建返回数组
...
SOAPElement[] elements = new SOAPElement[1];
elements[0] = getOrderReturn;
return elements;
...





回页首


从现有的 DOM 文档创建响应

在很多情况下,您可能想创建服务实现,它不是一次只构建响应消息的一个元素,而宁可利用已有的 DOM 文档,这个文档可能是从现有的终端检索到的。在这种情况下,您可能不想解析现有的 XML 片断,而是只想把它构造成一个新的 XML 消息。

SAAJ 1.2 规范在 javax.xml.soap.SOAPBody 接口上提出了一个名为 addDocument() 的新方法。而且,和 SAAJ 1.2(J2EE 版本 1.4 中也包括该规范)一样,好几个 SAAJ 接口都是继承于各自的 DOM 接口。例如, javax.xml.soap.SOAPElement 继承于 org.w3c.dom.Element 接口,这使得您可以使用通常的 DOM 编程方法来构建您的消息。由于现在在市场上对于 SAAJ 1.2 还没有普遍的支持,我暂时使用上面的 SAAJ 1.1 接口。





回页首


总结

SAAJ 规范定义了接口和类,使您从头开始构建 SOAP 消息。您可以利用这一点避免使用从 XML 到任何类型的映射,以及从 Java 类型到任何类型的映射,如在 JAX-RPC 中所定义的。这种方法的典型用例是假设客户序列化(反序列化)是必需的(例如,如果您的 JAX-RPC 引擎不完全支持所需消息的 XML 模式),或者当现有的代码已经处理了 XML 产物,使得类型映射不再需要了或者至少是多余的时候。

本技巧举了一个用于服务器端实现的例子,以聪明元素的方式构建它的响应信息。SAAJ 1.2 提出了另外的方法,可以更容易处理现有的 XML 部分。






回页首


下载

名字大小下载方法
ws-saajcode.earHTTP
关于下载方法的信息


参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 阅读第一个技巧,“ 将 <xsd:any/> 元素用于自定义序列化”,该技巧描述了 <xsd:any/> 元素的使用( developerWorks,2004 年 1 月)。

  • 在教程“ SAAJ”中继续练习 SAAJ 的使用 ( Sun Developer Network)。

  • developerWorks上可以找到大量的 Web 服务编程相关技巧

  • 从 Web 服务互操作性组织 Web 站点获取 WS-I 基本概要

  • 快速启动 Web 服务,学习 Web 服务知识、工具和技巧,那里提供最新的基于 Java 的软件开发工具和来自 IBM 的中间件(测试版),还有在线教程、文章以及在线技术论坛。

  • 访问 Developer Bookstore,那里有比较全面的技术书籍,包括许多 Web 服务主题的书籍。

  • 想要更多的相关信息?developerWorks 的 SOA 和 Web 服务专区有大量关于如何开发 Web 服务应用程序的资料文章和初级、中级以及高级教程。


关于作者

Andre Tost 是 WebSphere Business Development 组的一位解决方案设计师,在那里他帮助 IBM IBM Strategic Alliance 的合作伙伴使用 WebSphere 集成他们的应用程序。他主要研究整个 WebSphere 产品家族中的 Web 服务技术。在担任现在职务之前,他在各类开发和 IBM 软件开发中担任架构师角色已有 10 年,最近主要研究 WebSphere Business Components 产品。他来自德国,现在居住在 Rochester ,Minnesota,并在那里工作。在空闲时间,他喜欢与家人在一起,有可能还会踢足球和看球赛。您可以通过 atost@us.ibm.com 与 Andre 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款