使用字符串表示服务接口中的 XML 文档

作者:Sean BrydonSmitha KangathSameer Tyagi

问题描述

开发者在设计 Web 服务交互环境(客户端和服务在其中交换 XML 文档)时,需要选择一种用于表示服务接口中的 XML 文档的类型。此决定是至关重要的。选定的类型将在 WSDL 服务描述文件和服务实现代码中反映出来。在 Java[TM] 2 Platform, Enterprise Edition(J2EE[TM] 平台)1.4 应用程序中,使用基于 XML 的远程过程调用的 Java API (JAX-RPC) 交换 XML 文档(如订单或发票)。设计服务接口时,可以考虑以下几种策略来选择表示 XML 文档的类型: 选择使用字符串类型表示面向文档的服务中的 XML 文档时,解决方案的重点将放在设计考虑上。有关所有这些策略的描述,请参见面向文档的服务项。

解决方案

使用 xsd:string 表示客户端和服务之间交换的 XML 文档时,让我们首先考虑以下一些问题。

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

策略:使用字符串表示文档

可以将 XML 文档作为字符串在客户端和服务之间传递。在此策略中,XML 文档被转换为一个字符串,该字符串包含 XML 文档的所有元素和内容。这样,便为复杂的业务文档的传递提供了一个简单的选项,而无需创建序列化器或对基础元素执行编码-解码操作。因为某些用户习惯于使用字符串,因此这是一种非常简单的接口创建操作。此策略意味着 Java 接口会将字符串当作文档的参数,例如 public void submitPO(String PO),并且 WSDL 文件会具有输入参数 xsd:string。

以下示例演示了此策略。请注意,WSDL 文件、对应的 Java 服务接口和实现类都使用字符串类型表示 xml 文档。此处应注意的一点是,订单是以字符串表示的。代码示例 1 显示了一个服务的 WSDL 文件的代码片段,该服务用于交换使用字符串类型表示的订单 xml 文档。

<?xml version="1.0" encoding="UTF-8"?>
  <definitions name="StringPurchaseOrderService"
    targetNamespace="urn:StringPurchaseOrderService" 
    xmlns:tns="urn:StringPurchaseOrderService"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    <types>
    <schema targetNamespace="urn:StringPurchaseOrderService" xmlns:tns="urn:StringPurchaseOrderService" 
    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">
   <complexType name="submitPO">
     <sequence>
       <element name="String_1" type="string" nillable="true"/>
     </sequence>
   </complexType>
    ...
   <element name="submitPO" type="tns:submitPO"/>
    ...
   </schema>
  </types>
  <message name="StringPurchaseOrderServiceSEI_submitPO">
    <part name="parameters" element="tns:submitPO"/>
  </message>
  ...
</definitions>

代码示例 1:使用字符串表示 XML 订单文档的服务的 WSDL 文件代码片段

请注意,代码示例 2 中与 WSDL 服务文件对应的 Java 接口还会将字符串指定为 XML 订单文档的类型。这意味着,字符串 xmlPO 会将所有文档数据和 XML 标记设置为自己的值。

public interface PurchaseOrderServiceSEI extends Remote {  
    public String submitPO(String xmlPO)
                                throws InvalidPOException, RemoteException;
}

代码示例 2:与 WSDL 服务文件对应的订单 Java 接口

与在服务接口中一样,Java 实现也会将订单 XML 文档当作字符串处理。代码示例 3 显示了服务接口的实现。此示例显示了服务接口的 EJB Bean 实现,但是可以选择使用 Web 组件实现它。

public class PurchaseOrderServiceBean implements SessionBean {
  ...
  public String submitPO(String xmlPO) throws InvalidPOException, RemoteException {
        String id =null;
        //validate the doc against its schema      
        if (!(validate(xmlPO))) {
            throw new InvalidPOException("Error parsing the purchase order XML document!!!");
        }
        try {
            //extract the PO ID from the document and return to the client
            id = getID(xmlPO);
        } catch (Exception e) {
            throw new EJBException("StringPurchaseOrderService implementation had trouble parsing PO.xml, some system or
            configuration problem " + e.getMessage(), e);
        }
        //this is done just to illustrate throwing an application specific exception
        if(id.equals("100"))
            throw new InvalidPOException("Invalid ID for the purchase order!!! " +
                    "For demo purposes, we throw " +
                    "an application defined exception for the ID value of 100.");
        return id;
    }

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

除了创建用字符串表示文档的接口工件外,端点开发者还必须考虑如何处理文档。对端点的一个常见需求是验证接收到的订单文档。由于订单是一个字符串,因此无法通过 JAX-RPC 运行环境并根据订单结构对其进行验证(其他策略则可以做到这一点),因此,应用程序必须编写代码才能根据订单的结构进行验证。这种策略的一个缺陷在于无法使用运行环境提供的结构验证,并且,仅当服务读取内存中的文档并试图处理文档时,才能识别文档中的错误。

对端点的另一个常见需求是处理文档。这通常意味着绑定到某些 Java 类型,例如 PurchaseOrder.java 和对应的 LineItem.java 对象。通常,在服务交互层完成此绑定,然后将绑定委托到可能包含现有 Java 数据模型的业务处理层。对于其他策略而言,例如使用结构定义的类型表示 XML 文档(其中,订单结构详细信息已嵌入 WSDL 中),此绑定将由 JAX-RPC 运行环境自动完成。因此开发者必须编写代码将字符串文档转换为表示该文档的 Java 对象。这会给开发者带来更多的工作量,并且会影响某些性能。

让我们考虑一下服务使用字符串类型表示所交换的文档时,对客户端代码产生的一些影响。客户端需要采用其 PurchaseOrder.xml 文档并创建一个字符串版本,以便按照 WSDL 文件中定义的方式使用服务。客户端可以先将业务文档转换为字符串,然后将其传递到服务。当端点使用字符串表示 XML 文档时,客户端还必须执行一个额外的步骤,即创建字符串版本的 XML 文档。此外,由于 WSDL 将订单的参数定义为字符串,因此 WSDL 实际上并不能详尽地描述服务接口。如果 WSDL 文件将字符串指定为文档类型,则客户端开发者将如何知道该文档的结构?相反,使用结构定义的类型表示 XML 文档的策略会在 WSDL 中包括订单 XML 文档的结构,这会使服务描述更完整,因而更便于客户端确定服务的需求。如果 WSDL 使用字符串表示订单 XML 文档,则服务需要在某个位置发布描述,从而为希望访问服务的客户端提供协助。这是使用字符串表示与服务交换的 XML 文档的策略中的一个缺陷。

正如传递任何有效负载一样,将字符串文档从客户端传递到服务时,字符串文档会被序列化到连线上。JAX-RPC 运行环境将为 XML 中的标记和引号创建相应的转义符,如下面的 SOAP 请求中所示。请注意,通过添加全部信息进行字符串编码会增加消息的大小。代码示例 4 显示了订单的字符串编码。您可以看到额外添加的内容。服务器接收到其实现类中的字符串将包含客户端创建的原始 XML 文档。请注意,原始文档可能会稍微小一些。

POST /xmlstring/jaxrpc HTTP/1.1
Content-Type: text/xml; charset="utf-8"
Content-Length: 1397
SOAPAction: ""
User-Agent: Java/1.4.2_03
Host: localhost:9090
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schema.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://schema.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://www.examples.com/types">
<env:Body>
<ns0:submitPO>
<String_1>&lt;?xml version="1.0" encoding="UTF-8"?&gt;&#13;
&lt;PurchaseOrderDocument&gt;&#13;
&lt;createDate&gt;11-15-04&lt;/createDate&gt;&#13;
&lt;shipTo&gt;&#13;
&lt;street&gt;abc st&lt;/street&gt;&#13;
&lt;city&gt;abc city&lt;/city&gt;&#13;
&lt;state&gt;CA&lt;/state&gt;&#13;
&lt;zipCode&gt;99999&lt;/zipCode&gt;&#13;
&lt;/shipTo&gt;&#13;
&lt;billTo&gt;&#13;
&lt;street&gt;abc st&lt;/street&gt;&#13;
&lt;city&gt;abc city&lt;/city&gt;&#13;
&lt;state&gt;CA&lt;/state&gt;&#13;
&lt;zipCode&gt;99999&lt;/zipCode&gt;&#13;
&lt;/billTo&gt;&#13;
&lt;items&gt;&#13;
&lt;itemname&gt;Glass Ceiling Fan&lt;/itemname&gt;&#13;
&lt;price&gt;2&lt;/price&gt;&#13;
&lt;quantity&gt;200.0&lt;/quantity&gt;&#13;
&lt;/items&gt;&#13;
&lt;items&gt;&#13;
&lt;itemname&gt;Stainless Steel Blender &lt;/itemname&gt;&#13;
&lt;price&gt;1&lt;/price&gt;&#13;
&lt;quantity&gt;75.0&lt;/quantity&gt;&#13;
&lt;/items&gt;&#13;
&lt;/PurchaseOrderDocument&gt;&#13;
</String_1>

</ns0:submitPO>
</env:Body>
</env:Envelope>

代码示例 4:使用字符串类型表示的 XML 文档的 SOAP 请求

参考资料

有关本主题的详细信息,请参阅以下资料:

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