使用附件表示服务接口中的 XML 文档:设计详细信息


本文档以 java.netbpcatalog 项目下的面向文档的应用程序为例,说明了使用附件设计以文档为中心的接口和端点的策略。此应用程序具有一个服务端点,旨在使用附件处理订单。XML 文档以客户端发送的订单表示。在此应用程序中,订单文档作为附件进行接收,包含订单 ID 的 XML 短文档会作为附件返回。因此,此示例为使用附件在 Web 服务交互中交换 XML 文档显示了两种可供选择的不同类型。

该应用程序由以下主要实体组成:

服务的 WSDL 文件

WSDL 文件对 Web 服务进行了描述。代码示例 1 包含了此服务的 WSDL。我们选择开发此应用程序的形式是从 WSDL 开始的,并基于 WSDL 生成必要的 Java[TM] 类。因此,为消息部分在 WSDL 中选择正确的类型是非常重要的,因为这些类型会在生成的 Java 代码中产生参数类型。请注意在 WSDL 中声明的应用程序定义的服务异常。

让我们看一下接收订单以及返回 xml 短消息所使用的类型,它们都作为附件但使用不同的类型。请注意,在 WSDL 文件中,附件的结构是使用 导入的。这样,作为附件从服务返回的、包含订单 ID 的 XML 短文档就可以使用此类型了。它使用 wsi:swaRef 引用该结构文件中定义的类型。对于 submitPO 操作中要由服务接收的订单文档,请注意该文档使用了 xsd:hexBinary 作为其类型,并在其绑定中将 MIME 内容类型指定为 text/xml。通过选择此 MIME 类型,会通过 JAX-RPC 运行环境将其映射到 Java javax.xml.transform.Source 对象中。使用此 WSDL 文件生成代码示例 3 中的 Java 接口时,附件的这两种不同类型被映射到两种不同的 Java 类型了。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:tns="urn:AttachmentPurchaseOrderService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:ns1="urn:AttachmentPurchaseOrderService" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="urn:AttachmentPurchaseOrderService" name="AttachmentPurchaseOrderService">

   <types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
     xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
     targetNamespace="urn:AttachmentPurchaseOrderService">
    <import namespace="http://ws-i.org/profiles/basic/1.1/xsd" schemaLocation="WS-ISwA.xsd"/>
    <element name="statusmessage" type="wsi:swaRef"/>
    <element name="InvalidPOException" type="string"/>
    </schema>
   </types>
   
    <message name="AttachmentPurchaseOrderServiceSEI_submitPO">
          <part name="attachment_1" type="xsd:hexBinary"/>
    </message>

    <message name="AttachmentPurchaseOrderServiceSEI_submitPOResponse">
        <part name="response" element="ns1:statusmessage"/>
    </message>
       
        <message name="InvalidPOException">
        <part name="InvalidPOException" element="ns1:InvalidPOException"/>
    </message>

    <portType name="AttachmentPurchaseOrderServiceSEI">
        <operation name="submitPO">
            <input message="tns:AttachmentPurchaseOrderServiceSEI_submitPO"/>
            <output message="tns:AttachmentPurchaseOrderServiceSEI_submitPOResponse"/>
                        <fault name="InvalidPOException" message="tns:InvalidPOException"/>
        </operation>
    </portType>

    <binding name="AttachmentPurchaseOrderServiceSEIBinding" type="tns:AttachmentPurchaseOrderServiceSEI">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="submitPO">
            <input>
                <mime:multipartRelated>       
                                        <mime:part>
                        <soap:body use="literal"/>
                    </mime:part>
                    <mime:part>
                        <mime:content part="attachment_1" type="text/xml"/>
                    </mime:part>
                </mime:multipartRelated>
            </input>
            <output>
                <mime:multipartRelated>
                    <mime:part>
                        <soap:body parts="response"  use="literal"/>
                    </mime:part>
                </mime:multipartRelated>
            </output>
                        <fault name="InvalidPOException">
                <soap:fault name="InvalidPOException" use="literal"/>
            </fault>
        </operation>
    </binding>
    <service name="AttachmentPurchaseOrderService">
        <port name="AttachmentPurchaseOrderServiceSEIPort" binding="tns:AttachmentPurchaseOrderServiceSEIBinding">
            <soap:address location="http://localhost:8080/webservice/AttachmentPurchaseOrderService"/>
        </port>
    </service>
</definitions>

代码示例 1:使用附件交换 XML 文档的服务 WSDL 中的代码片段

请注意,代码示例 1 中的 WSDL 文件导入了使用附件的 SOAP 结构。此结构很简短,让我们来了解一下。代码示例 2 包含了导入的结构。此结构创建了一个名为 wsi:swaRef 的类型,该类型基于 xsd:anyURI。

<?xml version="1.0" encoding="utf-8" ?>
<!-- July, 2002
    (c) Copyright 2003, The Web Services-Interoperability Organization (WS-I)
    Download or use of this file is governed by the Policies and Bylaws of WS-I.
    For more information, send email info@ws-i.org.    
-->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
 targetNamespace="http://ws-i.org/profiles/basic/1.1/xsd"
 xmlns:tns="http://ws-i.org/profiles/basic/1.1/xsd">
    <xsd:simpleType name="swaRef">
        <xsd:restriction base="xsd:anyURI" />
    </xsd:simpleType>
</xsd:schema>
代码示例 2:使用附件的 SOAP 结构

服务端点接口

由于我们是从 WSDL 开始的,因此会基于 WSDL 中包含的信息生成服务端点接口。服务端点接口具有一个接收 javax.xml.transform.Source 对象的 submitPO() 方法。此外,还会从 WSDL 生成应用程序定义的异常 InvalidPOException。请注意,对于使用附件配置文件结构的 wsi:swaRef 附件类型,它返回 java.net.URI 类型,而对于订单 XML 文档(作为使用 xsd:hexBinary 类型的附件接收),它使用 javax.xml.transform.Source 类型。

public interface AttachmentPurchaseOrderServiceSEI extends Remote {
    public URI submitPO(Source attachment_1) throws
        InvalidPOException, RemoteException;
}
代码示例 3:服务端点接口

Java 端点实现

对于此应用程序,我们选择使用 EJB[TM] 组件来实现服务接口。端点实现类如代码示例 4 所示。还可以使用 Web 组件来实现端点。两种选择均可。让我们简单了解一下如何使用 WS-I Attachment Profile 1.0 将返回消息作为附件发送的一些详细信息。请注意从会话上下文检索消息上下文并对消息上下文设置附件上下文属性的方法。由于对此类型附件的支持与应用服务器相关,因此不同的 J2EE[TM] 1.4 平台提供此类支持也有所不同。

public class AttachmentPurchaseOrderServiceBean implements SessionBean {
   
    private SessionContext sc;
    private TransformerFactory transfactory;
   
    public AttachmentPurchaseOrderServiceBean() {}
   
    /**
     * This method implements a web service that processes an XML purchase
     * order document it receives as an attachment
     * and returns a WS-I Attachment profile conformat message
     * using the swaRef data type
     */
    public java.net.URI submitPO(Source xmlsrc) throws InvalidPOException, RemoteException{
        URI uri = null;
        String id = null;
        try {
            Transformer transformer = transfactory.newTransformer();
            DOMResult result = new DOMResult();
            transformer.transform(xmlsrc, result);
            Document doc = (Document)result.getNode();
            Element root = doc.getDocumentElement();
            NodeList list = root.getElementsByTagName("poId");
            for (int loop = 0; loop < list.getLength(); loop++) {
                Node node = list.item(loop);
                if (node != null) {
                    Node child = node.getFirstChild();
                    if ((child != null) && child.getNodeValue() != null) id = child.getNodeValue();
                }
            }
            MessageContext mc = sc.getMessageContext();
            AttachmentPart att = MessageFactory.newInstance().createMessage().createAttachmentPart();
            att.setContentId(" < "+ id + " >");
            att.setContent("<submitPO response/>","text/plain");
            ArrayList arrList = new ArrayList();
            arrList.add(att);
            // app server implementation specific
            mc.setProperty("com.sun.xml.rpc.attachment.SetAttachmentContext", arrList);
            uri = new java.net.URI("cid:" + id);
        } catch (Exception e) {
            throw new EJBException("AttachmentPOService Having Problems:"+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 uri;
    }
   
    //life cycle methods
   ...
}
代码示例 4:端点实现 Java 类

让我们再来简单地了解一下使用附件交换文档的此类服务的客户端代码。代码示例 5 显示了访问此 Web 服务的客户端应用程序的一些代码。请注意,客户端应用程序使用包含此服务的 WSDL 文件生成可访问该服务的 Java 类。Java 服务接口是通过映射到 WSDL 文件指定的类型的 Java 类型生成的。为了将订单作为参数传递到 submitPO 方法,客户端代码必须从 XML 文档创建 Java 源对象。对于 submitPO 方法返回的 XML 短文档,客户端代码使用 Java URI 对象。

public class  AttachmentPOServiceBD {
   
    ...
   
    public String submitPO(PurchaseOrder po) throws RequestHandlerException {
        try {
            AttachmentPurchaseOrderServiceSEI port = (AttachmentPurchaseOrderServiceSEI)
            serviceLocator.getServicePort(JNDINames.ATTACHMENT_SERVICE_REF,
                    AttachmentPurchaseOrderServiceSEI.class);
            String xmlDoc = po.toXMLString();
            Source src = new StreamSource(new StringReader(xmlDoc));
            URI resp= port.submitPO(src);
            return resp.getSchemeSpecificPart();
        } catch(InvalidPOException ipe){
            ...
    }
}

代码示例 5:访问附件 Web 服务的客户端应用程序中的代码片段

部署描述符

将 J2EE 中的 Web 服务打包为 war 或 ear 文件之类的部署模块。在此应用程序中,我们将其打包为一个 ear 文件。Web 服务端点的可移植打包需要多个部署描述符以及 Java 类。请注意,在使用附件时不需要在部署描述符中执行任何特殊的操作。下面是端点部署描述符中的一些代码片段:

  <enterprise-beans>
    <session>
      <ejb-name>AttachmentPurchaseOrderServiceBean</ejb-name>
      <service-endpoint>com.sun.j2ee.blueprints.attachmentservice.AttachmentPurchaseOrderServiceSEI</service-endpoint>
      <ejb-class>com.sun.j2ee.blueprints.attachmentservice.AttachmentPurchaseOrderServiceBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
   </session>  
  </enterprise-beans>
代码示例 6:ejb-jar.xml 部署描述符中的代码片段

请注意服务端点接口在 ejb-jar.xml 中被定义为 <service-endpoint>。端点还需要 Web 服务部署描述符文件 webservices.xml,如代码示例 5 所示。请注意在使用附件时不需要对部署描述符执行任何特殊的操作。

  <webservice-description>
    <webservice-description-name>AttachmentPurchaseOrderService</webservice-description-name>
    <wsdl-file>META-INF/wsdl/AttachmentPurchaseOrderService.wsdl</wsdl-file>
    <jaxrpc-mapping-file>META-INF/attachmentpurchaseorderservice-mapping.xml</jaxrpc-mapping-file>
    <port-component>
      <description>port component description</description>
      <port-component-name>AttachmentPurchaseOrderService</port-component-name>
      <wsdl-port xmlns:PurchaseOrderns="urn:AttachmentPurchaseOrderService">PurchaseOrderns:AttachmentPurchaseOrderServiceSEIPort</wsdl-port>
      <service-endpoint-interface>
        com.sun.j2ee.blueprints.attachmentservice.AttachmentPurchaseOrderServiceSEI
      </service-endpoint-interface>
      <service-impl-bean>
        <ejb-link>AttachmentPurchaseOrderServiceBean</ejb-link>
      </service-impl-bean>
    </port-component>
  </webservice-description>
代码示例 7:webservice.xml 部署描述符中的代码片段

Web 服务部署描述符文件包含有关服务的信息,例如 WSDL 文件和 JAX-RPC 映射文件的名称以及服务端点接口和服务实现类的名称。
© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。