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

本文档以 java.netbpcatalog 项目下的面向文档的应用程序为例,说明了在设计以文档为中心的接口和端点时使用 xsd:anyType 作为文档类型的策略。此应用程序具有一个服务端点,旨在将 xsd:anyType 用作文档类型。XML 文档以客户端发送的订单表示。

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

服务的 WSDL 文件

WSDL 文件对 Web 服务进行了描述。代码示例 1 包含了此服务 WSDL 中的代码片段。我们选择的开发形式是从 WSDL 开始的,并基于 WSDL 生成必要的 Java[TM] 类。因此,为消息部分在 WSDL 中选择正确的类型是非常重要的,因为这些类型会在生成的 Java 代码中产生参数类型。请注意请求消息被声明为 type="anyType",这将生成 Java 接口(如代码示例 2 所示),接口中的服务将输入参数映射到 Java SOAPElement 类型。此外,还请注意在 WSDL 中声明的应用程序定义的服务异常。

  <types>
      <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="urn:AnyTypePurchaseOrderService">
      <complexType name="submitPO">
    <sequence>
      <element name="BusinessDocumentRequest" type="anyType" nillable="true"/>                                            
    </sequence>
      </complexType>
      <complexType name="submitPOResponse">
    <sequence>
     <element name="result" type="string" nillable="true"/>                                               
    </sequence>
      </complexType>
      <complexType name="InvalidPOException">
    <sequence>
      <element name="message" type="string" nillable="true"/>                   
    </sequence>
      </complexType>
      <element name="submitPO" type="tns:submitPO"/>
      <element name="submitPOResponse" type="tns:submitPOResponse"/>
      <element name="InvalidPOException" type="tns:InvalidPOException"/>
      </schema>
    </types>
    <message name="AnyTypePurchaseOrderServiceSEI_submitPO">
        <part name="parameters" element="ns1:submitPO"/>
    </message>
    <message name="AnyTypePurchaseOrderServiceSEI_submitPOResponse">
        <part name="result" element="ns1:submitPOResponse"/>
    </message>
    <message name="InvalidPOException">
        <part name="InvalidPOException" element="ns1:InvalidPOException"/>
    </message>
    <portType name="AnyTypePurchaseOrderServiceSEI">
        <operation name="submitPO">
            <input message="tns:AnyTypePurchaseOrderServiceSEI_submitPO"/>
            <output message="tns:AnyTypePurchaseOrderServiceSEI_submitPOResponse"/>
            <fault name="InvalidPOException" message="tns:InvalidPOException"/>
        </operation>
    </portType>

代码示例 1:服务 WSDL 中的代码片段

服务端点接口

由于我们是从 WSDL 开始的,因此会基于 WSDL 中包含的信息生成服务端点接口。服务端点接口具有一个接收 javax.xml.soap.SOAPElement 对象的 submitPO() 方法。此外,还从 WSDL 生成了应用程序定义的异常 InvalidPOException

public interface AnyTypePurchaseOrderServiceSEI extends Remote {
    public String submitPO(SOAPElement businessDocumentRequest) throws
        InvalidPOException,  RemoteException;
}
代码示例 2:服务端点接口

Java 端点实现

对于此应用程序,我们选择使用 Enterprise JavaBeans[TM] (EJB[TM]) 组件来实现服务接口。还可以使用 Web 组件实现端点。两种选择均可。需要注意的是,订单 XML 被当作 SOAPElement 处理。端点实现类如代码示例 3 所示。

public class AnyTypePurchaseOrderServiceBean implements SessionBean {
   
    private SessionContext sc;
    private POXMLUtil xmlUtil;
   
    public AnyTypePurchaseOrderServiceBean() {}
   
    public String submitPO(SOAPElement request) throws InvalidPOException, RemoteException {
        String poID = null;
        SOAPElement reply = null;
        try {
            NodeList list = ((Element)request).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){
                        poID = child.getNodeValue();
                    }
                }
            }                      
        } catch (Exception exe) {
            throw new EJBException("AnyTypePOService Having Problems:"+exe.getMessage(), exe);
        }
        //this is done just to illustrate throwing an application specific exception
        if(poID.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 poID;
    }
   
    //life cycle methods
    ...
}
代码示例 3:端点实现

访问服务的客户端代码

我们已经查看了服务代码,我们再简单看一下此应用程序的客户端。客户端将使用代码示例 1 中的 WSDL 文件,并生成一个 Java 服务接口(如代码示例 4 所示),并且使用此服务的 Java 接口(如代码示例 5 中的客户端代码所示)将订单发送到服务中。请注意,将收集此客户端应用程序中的订单数据作为 Web 应用程序(它包含客户端定义的 PurchaseOrder 类型中的数据,如代码示例 6 所示)的输入,并将此订单 Java 对象转换为 SOAPElement,然后发送到服务中。

public interface AnyTypePurchaseOrderServiceSEI extends Remote {

    public String submitPO(SOAPElement businessDocumentRequest) throws
        com.sun.j2ee.blueprints.docoriented.client.anytypeposervice.InvalidPOException, RemoteException;
}

代码示例 4:使用客户端生成的 Java 服务接口访问 Web 服务

public class AnyTypePOServiceBD {
   
    private ServiceLocator serviceLocator;
   
    public AnyTypePOServiceBD(){
        serviceLocator = new ServiceLocator();
    }
   
    public String submitPO(PurchaseOrder po) throws RequestHandlerException {
        try {
            //this flag is to generate a wrapper element BusinessDocumentRequest
            //as defined in the WSDL
            boolean wrapper = true;
            AnyTypePurchaseOrderServiceSEI port = (AnyTypePurchaseOrderServiceSEI)
            serviceLocator.getServicePort(JNDINames.ANY_TYPE_SERVICE_REF,
                    AnyTypePurchaseOrderServiceSEI.class);
            SOAPElement requestSOAPElem = po.toXMLSOAPElement(wrapper);                  
            return port.submitPO(requestSOAPElem);
        } catch(InvalidPOException ipe){
            ipe.printStackTrace(System.err);
            throw new RequestHandlerException("Request Handler Exception: Service Endpoint Application-Defined Exception "+ipe.getMessage(), ipe);
        } catch(RemoteException re){
            re.printStackTrace(System.err);
            throw new RuntimeException("The web service you are trying to access is not available. A possible reason could be that the service has not been deployed yet. "+ re.getMessage(), re);
        }
    }
}
代码示例 5:将订单 XML 文档发送到 anyType 服务的客户端代码

由客户端应用程序生成的帮助 PurchaseOrder 类如代码示例 6 所示。在客户端应用程序中,会提交 Web 页表单,然后用数据填充 PurchaseOrder。然后在代码示例 5 中使用此类以获取提交到服务的订单 XML 文档的表示。请注意 PurchaseOrder 类具有一个转换 Java 订单的方法 toXMLSOAPElement()。特别要注意的是,订单 XML 被封装在此服务的 WSDL 定义的 BusinessDocumentRequest 父元素中。因此,在使用 anyTpe 定义服务时,客户端必须修改 XML 文档并将其封装在 WSDL 定义的父元素中。这会给要使用服务接口(使用 anyType 表示与服务交换的文档)的客户端带来一些负担。

public class PurchaseOrder {    

    private String poId;
    private Calendar createDate;
    private Address shipTo;
    private Address billTo;
    private LineItem[] items;   

    public PurchaseOrder() {}   

    public PurchaseOrder(String poId, Calendar createDate,
            Address shipTo, Address billTo, LineItem[] items) {

        this.poId = poId;
        this.shipTo = shipTo;
        this.createDate = createDate;
        this.billTo = billTo;
        this.items = items; 
    }

    ...

     public SOAPElement toXMLSOAPElement(boolean wrapper) {
        SOAPElement soapElem = null;
        try {
            //construct the DOM tree
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilderFactory.setNamespaceAware(true);
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.newDocument();
            Element  poElem = doc.createElement("PurchaseOrder");
            if(wrapper){
                Element  docElem = doc.createElement("BusinessDocumentRequest");
                doc.appendChild(docElem);
                docElem.appendChild(poElem);
            } else{
                doc.appendChild(poElem);
            }
            Element  elem = doc.createElement("poId");
            elem.appendChild(doc.createTextNode(poId));
            poElem.appendChild(elem);
            elem = doc.createElement("createDate");
            elem.appendChild(doc.createTextNode((new SimpleDateFormat("MM-dd-yy")).format(createDate.getTime())));
            poElem.appendChild(elem);
            elem = doc.createElement("shipTo");
            poElem.appendChild(shipTo.toDOM(doc, elem));
            elem = doc.createElement("billTo");
            poElem.appendChild(billTo.toDOM(doc, elem));
            for(int i = 0; i < items.length; ++i){
                poElem.appendChild(items[i].toDOM(doc));
            }
           
            //create a SOAPElement
            SOAPElement parent = SOAPFactory.newInstance().createElement("dummy");
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.transform(new DOMSource(doc), new DOMResult(parent));
            soapElem = (SOAPElement) parent.getChildElements().next();
        } catch(TransformerException te) {
            te.printStackTrace(System.err);
            throw new RuntimeException(te.getMessage(), te);
        } catch(ParserConfigurationException pce) {
            pce.printStackTrace(System.err);
            throw new RuntimeException(pce.getMessage(), pce);
        } catch(SOAPException se) {
            se.printStackTrace(System.err);
            throw new RuntimeException(se.getMessage(), se);
        }
        return soapElem;
}

代码示例 6:显示封装订单 XML 文档的 PurchaseOrder Java 类的代码片段

服务端点的部署描述符

将 J2EE[TM] 中的 Web 服务打包为 war 或 ear 文件之类的部署模块。在此应用程序中,我们将其打包为一个 ear 文件。Web 服务端点的可移植打包需要多个部署描述符以及 Java 类。下面是端点部署描述符中的一些代码片段:

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

请注意服务端点接口在 ejb-jar.xml 中被定义为 <service-endpoint>。端点还需要 Web 服务部署描述符文件 webservices.xml,如下所示:

  <webservice-description>
    <webservice-description-name>AnyTypePurchaseOrderService</webservice-description-name>
    <wsdl-file>META-INF/wsdl/AnyTypePurchaseOrderService.wsdl</wsdl-file>
    <jaxrpc-mapping-file>META-INF/anytypepurchaseorderservice-mapping.xml</jaxrpc-mapping-file>
    <port-component>
      <description>port component description</description>
      <port-component-name>AnyTypePurchaseOrderService</port-component-name>
      <wsdl-port xmlns:PurchaseOrderns="urn:AnyTypePurchaseOrderService">PurchaseOrderns:AnyTypePurchaseOrderServiceSEIPort</wsdl-port>
      <service-endpoint-interface>
        com.sun.j2ee.blueprints.anytypeposervice.AnyTypePurchaseOrderServiceSEI
      </service-endpoint-interface>
      <service-impl-bean>
        <ejb-link>AnyTypePurchaseOrderServiceBean</ejb-link>
      </service-impl-bean>
    </port-component>
  </webservice-description>
代码示例 8:webservice.xml 部署描述符中的代码片段

Web 服务部署描述符文件包含有关服务的信息,如 WSDL 文件和 JAX-RPC 映射文件的名称,以及服务端点接口和服务实现类的名称。

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