使用结构定义的类型表示服务接口中的 XML 文档

作者:Sean BrydonSmitha KangathSameer Tyagi

问题描述

开发者在设计 Web 服务交互环境(客户端和服务在其中交换 XML 文档)时,需要选择一种用于表示服务接口中的 XML 文档的类型。类型的选择会带来多方面的影响。选定的类型将在 WSDL 服务描述文件和服务实现代码中反映出来。在 J2EE 1.4 应用程序中使用 JAX-RPC 来交换诸如订单或发票之类的 XML 文档。在设计服务接口时,有许多策略可供考虑来选择表示 XML 文档的类型。 有关所有这些策略的描述,请参见设计面向文档的服务上的目录项。

当选择使用您在结构文件中定义的类型来表示 XML 文档时,该解决方案将重点考虑设计问题。在这种情况下,这些可能是应用程序特有的,或者由垂直行业标准机构定义。例如,应用程序可能需要使用一个 purchaseorder.xml 文档,该文档具有一个相应的 purchaseorder.xsd 结构文件。这种情况下最重要的一点是 WSDL 文件将作为其服务的一部分,该类型是在结构文件中定义的,而不是由标准结构定义的。这种类型通常定义为使用标准结构类型的复杂类型。

解决方案

当使用结构定义的类型表示在客户端和服务之间交换的 XML 文档时,我们首先考虑以下问题。

为了描述此策略,我们将使用一个示例,将 PurchaseOrder XML 文档从客户端传递到能够接收订单的服务。被交换的订单文档是一个典型的订单,它包括诸如订单 ID、联系人信息以及所购商品行项目之类的数据。

策略:使用结构定义的类型表示 XML 文档

在这种策略中,服务接口工件(WSDL 文件、Java 接口和实现)可能包含 PurchaseOrder 结构的所有详细信息,因为该工件将作为接口的一部分。使用 document-literal 格式部署此类 Web 服务时会将 XML 文档传递到 SOAP 消息正文中。对于 WSDL 文件,这意味着此结构的所有元素都会被嵌入 WSDL 文件中,或者可以在单独的结构文件中定义文档结构并将其导入到 WSDL 文件中。对于相应的 Java 接口,这意味着所有 PurchaseOrder 结构元素都需要使用相应的 Java 类型来表示 PurchaseOrder。从 WSDL 角度看,对于此操作,WSDL 与被交换的文档的结构之间有一个耦合。

如果选择将结构定义直接嵌入到 WSDL 文件中,则意味着您的接口将包含文档结构的字段和类型的所有详细信息。通过以下代码示例 1 中的 WSDL 代码片段、以及下面的接口和实现类说明了嵌入到接口中的订单详细信息。在维护时很繁琐,因为订单类型实际上是 WSDL 的一部分。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions xmlns="http://schema.xmlsoap.org/wsdl/" xmlns:tns="urn:ObjectPurchaseOrderService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schema.xmlsoap.org/wsdl/soap/" name="ObjectPurchaseOrderService" targetNamespace="urn:ObjectPurchaseOrderService">
<types>
<schema xmlns:soap11-enc="http://schema.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://schema.xmlsoap.org/wsdl/" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:ObjectPurchaseOrderService">
     <xsd:complexType name="Address">
        <xsd:sequence>
            <xsd:element name="street" type="xsd:string" nillable="false"/>
            <xsd:element name="city" type="xsd:string" nillable="false"/>
            <xsd:element name="state" type="xsd:string" nillable="false"/>
            <xsd:element name="postalCode" type="xsd:string" nillable="false"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="LineItem">
        <xsd:sequence>
            <xsd:element name="itemId" type="xsd:string" nillable="false"/>
            <xsd:element name="price" type="xsd:float" nillable="false"/>
            <xsd:element name="quantity" type="xsd:int" nillable="false"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="PurchaseOrder">
        <xsd:sequence>
            <xsd:element name="poId" type="xsd:string" nillable="false"/>
            <xsd:element name="createDate" type="xsd:date" nillable="false"/>
            <xsd:element name="shipTo" type="Address" nillable="false"/>
            <xsd:element name="billTo" type="Address" nillable="false"/>
            <xsd:element name="items" type="LineItem" nillable="false" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>

</schema>
</types>
<message name="PurchaseOrderServiceSEI_submitPO">
  <part name="PurchaseOrder_1" type="tns:PurchaseOrder" />
</message>
   ...
</definitions>

代码示例 1:将订单结构定义显示为嵌入到 WSDL 中的参数类型定义的 WSDL 文件代码片段

将文档结构定义嵌入到 WSDL 内的另一种方法是,在单独的文件中定义类型并将其导入到 WSDL 文件中。这样维护起来会更简单。在此策略中,将创建 purchaseorder.xsd 结构文件。然后 WSDL 文件会将此类型导入到 WSDL 中。之后,仅将它用于服务操作的输入参数中。代码示例 2 对此策略进行了说明。

<types>
   <schema targetNamespace="urn:SchemaDefinedPurchaseOrderService"
   xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://www.w3.org/2001/XMLSchema"> 
    <xsd:import namespace="http://java.sun.com/blueprints/ns/po" schemaLocation="PurchaseOrder.xsd"/>
    <element name="submitPO">
        <complexType>
            <sequence>
            <element name="inputDoc" type="pons:PurchaseOrder" nillable="true"/>                       
      </sequence>
    </complexType>
      </element>  
    ...                  
  </schema>
</types>
<message name="SchemaDefinedPurchaseOrderServiceSEI_submitPO">
    <part name="parameters" element="tns:submitPO"/>
</message>
  ...
<portType name="SchemaDefinedPurchaseOrderServiceSEI">
    <operation name="submitPO">
      <input message="tns:SchemaDefinedPurchaseOrderServiceSEI_submitPO"/>
       ...
    </operation>
</portType>
代码示例 2:显示所导入的订单结构的 WSDL 中的代码片段

现在,看一下将从这些类型的 WSDL 文件生成的 Java 文件。正如 WSDL 文件必须包含为订单指定的所有详细信息一样,Java 接口也需要反映表示订单的类型。

//The service endpoint java interface(either generated or hand rolled)
package com.sun.j2ee.blueprints.objectposervice;
import java.rmi.*;
public interface PurchaseOrderServiceSEI extends Remote {
    public String submitPO(PurchaseOrder inputDoc) throws
        InvalidPOException, RemoteException;
}

代码示例 3:PurchaseOrderServiceSEI Java 接口,对应于服务的 WSDL 文件

请注意,PurchaseOrder.java 会以类的形式进行定义,与它引用的任何类的类型,例如 Address.java 或 LineItem.java 的定义一样

public class PurchaseOrder{
   private String poId;
   private Calendar createDate;
   private Address shipTo;
   private Address billTo;
   private LineItem[] items;
   ...

}
代码示例 4:订单 Java 类,对应于服务的 WSDL 文件中的类型定义

同样,端点实现(此示例中的 EJB)也在其方法签名中使用 PurchaseOrder.java。请注意,该组件可以是 Web 组件,而不能是 EJB 组件,并且会出现同样的问题。就选择 XML 文档的类型而言,选择 Web 组件或 EJB 组件实现服务接口没有任何区别。

public class PurchaseOrderServiceBean implements SessionBean {
  ...
  public String submitPO(PurchaseOrder po) throws
InvalidPOException, RemoteException {
    //see code for details
  }
  ...

}
代码示例 5:订单服务端点的实现类

因此端点需要一个 WSDL 文件、一个 Java 接口、一个具有 submitPO 方法(接收具有 PurchaseOrder 结构中定义的结构和类型的 PurchaseOrder)的 Java 实现类,还可能包括一些 helper Java 类来表示 PurchaseOrder。这就像许多 Java 程序员使用的普通接口,因为它类似于传递对象,为众多开发者提供了一个方便的策略。这种策略非常适用于文档结构明确定义,并需要根据上述结构生成服务的情况。例如,根据垂直行业标准发票文档生成一种服务。这种策略的优势在于能够充分利用 JAX-RPC 的自动结构验证。例如,收到 PurchaseOrder 文档之后,JAX-RPC 运行时会自动根据 PurchaseOrder 结构验证传入的 PurchaseOrder,然后再将订单分发到服务实现类,这样可以减少开发者的某些编码工作。同时,这种策略的优势还在于可以自动绑定到由 JAX-RPC 提供的 Java 对象。例如,在上述订单示例中,XML PurchaseOrder 文档被自动绑定到 submitPO 方法签名中的 PurchaseOrder.java 对象。这样,开发者就可以无需通过编写代码来创建基于收到的 XML 文档的 Java 对象了。

请注意,文档的描述与服务接口紧密耦合。因此,如果试图更改文档定义,则需要更改接口或 WSDL。这会影响接口的演变。使用这种策略,很难通过接收多种类型文档的方法来生成更为通用的服务,因为服务操作被紧密地绑定到了单个文档的结构上。在已知所交换的文档类型的情况下(例如垂直行业定义的结构),则在接口中使用结构定义类型的解决方案会非常有效。这也是一种常用的解决方案。

参考资料

有关本主题的详细信息,请参见下列资源:

© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。