Using Strings to Represent XML Documents in a Service Interface: Design Details

The strategy for using String as the document type when designing a document-centric interface and endpoint are illustrated by the document-oriented applications available under the bpcatalog project hosted at java.net. This application has a service endpoint designed to use String as the document type in the interface and in the endpoint implementation. The XML document is represented by a purchase order sent by the client.

The application consists of these major entities:

The WSDL File for the Service

The WSDL file describes the web service. Please note an application-defined service exception declared in the WSDL. A snippet from the WSDL for this service would look like this:

<>  <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>
      <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="StringPurchaseOrderServiceSEI_submitPO">
    <part name="parameters" element="tns:submitPO"/>
  </message>
  <message name="StringPurchaseOrderServiceSEI_submitPOResponse">
    <part name="result" element="tns:submitPOResponse"/>
  </message>
  <message name="InvalidPOException">
    <part name="InvalidPOException" element="tns:InvalidPOException"/>
  </message>
  <portType name="StringPurchaseOrderServiceSEI">
    <operation name="submitPO">
      <input message="tns:StringPurchaseOrderServiceSEI_submitPO"/>
      <output message="tns:StringPurchaseOrderServiceSEI_submitPOResponse"/>
      <fault name="InvalidPOException" message="tns:InvalidPOException"/>
    </operation>
  </portType>

Code Example 1:  Snippet From the WSDL for the Service

The Service Endpoint Interface

The endpoint interface has a submitPO() method that accepts an XML document as a String. Here is the service endpoint interface:

public interface StringPurchaseOrderServiceSEI extends Remote {  
    public String submitPO(String xmlPO)
                                throws InvalidPOException, RemoteException;
}
Code Example 2:  The Service Endpoint Interface

The Java[TM] Endpoint Implementation

For this application, we chose to use an Enterprise JavaBeans[TM] (EJB[TM]) component to implement the service interface. We could instead have used a web component to implement the endpoint. Either choice would have worked. Note that the method getID() returns only the purchase order ID to the client. In a real world scenario, there will be some work that needs to be done before returning the result to the client, but we are not focusing on business logic. Also, please note the endpoint first validates the document against its schema.An important point to notice is that the purchase order XML is handled as a String. The endpoint implementation class would look like this:

public class StringPurchaseOrderServiceBean implements SessionBean {
   
    private SessionContext sc;
    private DocumentBuilderFactory docBuilderFactory;
   
    public StringPurchaseOrderServiceBean(){}
   
    public String submitPO(String xmlPO) throws InvalidPOException, RemoteException {
        String id =null;
        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;
    }
   
    private String getID(String xmlDoc) throws org.xml.sax.SAXException, javax.xml.parsers.ParserConfigurationException, 
        java.io.IOException {
        String ret = "";
        DocumentBuilder db = null;
        if (docBuilderFactory != null)
            db = docBuilderFactory.newDocumentBuilder();
        InputSource is =  new InputSource(new StringReader(xmlDoc));
        Document doc = db.parse(is);
        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) return child.getNodeValue();
            }
        }
        return ret;
    }
  private boolean validate(String xmlDoc) {
        Document doc = null;
        try {
            DocumentBuilderFactory dbf = null;
            DocumentBuilder db = null;
            try {
                dbf = DocumentBuilderFactory.newInstance();
                dbf.setValidating(true);
                dbf.setNamespaceAware(true);
                dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                        "http://www.w3.org/2001/XMLSchema");
                if (dbf != null){
                    db = dbf.newDocumentBuilder();
                }
                db.setEntityResolver(new POEntityResolver());
                db.setErrorHandler(new POXMLErrorHandler());
            } catch ( javax.xml.parsers.ParserConfigurationException pce) {
                System.err.println(pce);
            }
            InputSource is =  new InputSource(new StringReader(xmlDoc));
            doc = db.parse(is);
            return true;
        } catch (Exception e) {
            System.err.println("XML Validation Error " + e);
        }
        return false;
    }
   
    //life cycle methods
    ...
}
Code Example 3:  The Endpoint Implementation

The Deployment Descriptors

A web service in J2EE[TM] is packaged into a deployment module such as a war or an ear file. In this application, we packaged it as an ear file. The portable packaging of a web service endpoint requires several deployment descriptors as well as the Java classes. Here are some snippets from the endpoint deployment descriptors:

<enterprise-beans>
  <session>     
    <ejb-name>StringPurchaseOrderServiceBean</ejb-name>
    <service-endpoint>
      com.sun.j2ee.blueprints.stringposervice.StringPurchaseOrderServiceSEI
    </service-endpoint>    
    <ejb-class>
      com.sun.j2ee.blueprints.stringposervice.StringPurchaseOrderServiceBean
    </ejb-class>    
    <session-type>Stateless</session-type>   
    <transaction-type>Container</transaction-type>
  </session>  
</enterprise-beans>
Code Example 4: Snippet From ejb-jar.xml Deployment Descriptor

Please note the service endpoint interface is defined as the <service-endpoint> in ejb-jar.xml.The endpoint also needs a web services deployment descriptor file webservices.xml that looks like this:

<webservice-description>
  <webservice-description-name>StringPurchaseOrderService</webservice-description-name>
  <wsdl-file>META-INF/wsdl/StringPurchaseOrderService.wsdl</wsdl-file>
  <jaxrpc-mapping-file>META-INF/stringpurchaseorderservice-mapping.xml</jaxrpc-mapping-file>
  <port-component>
    <description>port component description</description>
    <port-component-name>StringPurchaseOrderService</port-component-name>
    <wsdl-port xmlns:PurchaseOrderns="urn:StringPurchaseOrderService">PurchaseOrderns:StringPurchaseOrderServiceSEIPort</wsdl-port>
    <service-endpoint-interface>
      com.sun.j2ee.blueprints.stringposervice.StringPurchaseOrderServiceSEI
    </service-endpoint-interface>
    <service-impl-bean>
      <ejb-link>StringPurchaseOrderServiceBean</ejb-link>
    </service-impl-bean>
 </port-component>
</webservice-description>
Code Example 5: Snippet From webservice.xml Deployment Descriptor

The web services deployment descriptor file contains information about the service such as the names of the WSDL file and the JAX-RPC mapping file, and also the service endpoint interface and the service implementation class names.


© Sun Microsystems 2005. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.