用SAAJ 和JAX-RPC构建SOAP响应信封

日期: 2008-04-24 作者:Andre Tost 来源:TechTarget中国

  服务端点接口


  在 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
           http://www.w3.org/2001/XMLSchema”>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”   http://www.w3.org/2001/XMLSchema”>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   http://schemas.xmlsoap.org/soap/envelope/”>http://schemas.xmlsoap.org/soap/envelope/”
  http://schemas.xmlsoap.org/soap/encoding/”>http://schemas.xmlsoap.org/soap/encoding/”
  http://www.w3.org/2001/XMLSchema”>http://www.w3.org/2001/XMLSchema”
  http://www.w3.org/2001/XMLSchema-instance”>http://www.w3.org/2001/XMLSchema-instance“>
  <soapenv:Body>
  <getOrderResponse http://any.webservices.ibm.com”>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 部分。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐