使用字符串表示服务接口中的 XML 文档
作者:Sean Brydon、Smitha Kangath 及 Sameer Tyagi
问题描述
开发者在设计 Web 服务交互环境(客户端和服务在其中交换 XML 文档)时,需要选择一种用于表示服务接口中的 XML 文档的类型。此决定是至关重要的。选定的类型将在 WSDL 服务描述文件和服务实现代码中反映出来。在 Java[TM] 2 Platform, Enterprise Edition(J2EE[TM] 平台)1.4 应用程序中,使用基于 XML 的远程过程调用的 Java API (JAX-RPC) 交换 XML 文档(如订单或发票)。设计服务接口时,可以考虑以下几种策略来选择表示 XML 文档的类型:
- 使用字符串表示 XML 文档
- 使用结构定义的类型表示 XML 文档
- 使用 XML 片段表示 XML 文档(使用 xsd:anyType)
- 使用 XML 片段表示 XML 文档(使用 xsd:any)
- 使用附件打包 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><?xml
version="1.0" encoding="UTF-8"?>
<PurchaseOrderDocument>
<createDate>11-15-04</createDate>
<shipTo>
<street>abc st</street>
<city>abc city</city>
<state>CA</state>
<zipCode>99999</zipCode>
</shipTo>
<billTo>
<street>abc st</street>
<city>abc city</city>
<state>CA</state>
<zipCode>99999</zipCode>
</billTo>
<items>
<itemname>Glass Ceiling
Fan</itemname>
<price>2</price>
<quantity>200.0</quantity>
</items>
<items>
<itemname>Stainless Steel Blender
</itemname>
<price>1</price>
<quantity>75.0</quantity>
</items>
</PurchaseOrderDocument>
</String_1>
</ns0:submitPO>
</env:Body>
</env:Envelope>
代码示例 4:使用字符串类型表示的 XML 文档的 SOAP 请求
参考资料
有关本主题的详细信息,请参阅以下资料:
© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。