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


The strategy for using attachments for designing a document-centric interface and endpoint is illustrated by the document-oriented applications available under the bpcatalog project hosted at java.net. This application has a service endpoint designed to use attachments to handle a purchase order. The XML document is represented by a purchase order sent by the client. In this application, a purchase order document is received as an attachment and a short XML document containing the purchase order ID is returned as an attachment. So this example shows two different types to choose for using attachments to exchange XML documents in a web service interaction.

The application consists of these major entities:

The WSDL File for the Service

The WSDL file describes the web service. Code Example 1 contains the WSDL for this service. The development style we chose for this application was to start with WSDL and generate the necessary Java[TM] classes from it. So choosing the proper types for the message parts in the WSDL is very important, because these types will result in the types of the parameters in the generated Java code. Please note an application defined service exception declared in the WSDL. 

Let's look at some detail in the type for the purchase order being received and the short xml message being returned, both as attachments but using different types. Note that the schema for attachments was imported with <import namespace="http://ws-i.org/profiles/basic/1.1/xsd" schemaLocation="WS-ISwA.xsd"/> in the WSDL file. This enables the short XML document containing a purchase order ID that is returned from the service as an attachment to use this type. It uses wsi:swaRef to reference the type defined in that schema file. For the purchase order documents to be received by the service on the submitPO operation, notice that it uses the xsd:hexBinary for its type and in its binding it specifies the MIME content type to text/xml. By choosing this MIME type, it causes the JAX-RPC runtime to map it to a Java  javax.xml.transform.Source object. These two different types for attachments are mapped to two different Java types when this WSDL file is used to generate the Java interface in Code Example 3.

<?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>

Code Example 1:  Snippet From the WSDL for the Service Using Attachments to Exchange XML Documents

Notice that the WSDL file in Code Example 1 imports the schema for SOAP with Attachments. This schema is fairly short, so let's take a look at it. Code Example 2 contains the schema which is imported. This schema creates a type called wsi:swaRef which is based on 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>
Code Example 2: Schema for SOAP With Attachments

The Service Endpoint Interface

Because we start with the WSDL, the service endpoint interface gets generated based on the information contained in the WSDL. The service endpoint interface has a submitPO() method that accepts a javax.xml.transform.Source object. Also, the application defined exception InvalidPOException is generated from the WSDL. Notice that it returns a java.net.URI  type for the wsi:swaRef attachment type using the Attachment Profile schema, and for the purchase order XML document received as an attachment specified using the xsd:hexBinary type, it uses the javax.xml.transform.Source type.

public interface AttachmentPurchaseOrderServiceSEI extends Remote {
    public URI submitPO(Source attachment_1) throws
        InvalidPOException, RemoteException;
}
Code Example 3:  The Service Endpoint Interface

The Java Endpoint Implementation

For this application, we chose to use an EJB[TM] component to implement the service interface. The endpoint implementation class is shown in Code Example 4. We could instead have used a web component to implement the endpoint. Either choice would have worked. Let's take a brief look at some details on how the return message is sent as an attachment using the WS-I Attachment Profile 1.0. Please note the way the message context is retrieved from the session context and the attachment context property is set on the message context. Because support for this type of attachments is application server dependent, different J2EE[TM] 1.4 platforms may do this differently.

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
   ...
}
Code Example 4:  The Endpoint Implementation Java Class

Let's also briefly look at the code for the client of this type of service which uses attachments to exchange documents. Code Example 5 shows some code from the client application which accesses this web service. Note that the client application used the WSDL file from the service to generate Java classes which could be used to access the service. The Java service interface was generated with Java types which mapped to the types specified in the WSDL file. For passing the purchase order as a parameter to the submitPO method the client code had to create a Java Source object from the XML document. For the short XML document being returned by the submitPO method, the client code used a Java URI object.

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){
            ...
    }
}

Code Example 5: Snippet from Client Application Which Accesses the Attachment Web Service

The Deployment Descriptors

A web service in J2EE 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. Notice there is nothing special to do in the deployment descriptor when using attachments. Here are some snippets from the endpoint deployment descriptors:

  <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>
Code Example 6: Snippet From ejb-jar.xml Deployment Descriptor

Note the service endpoint interface defined as the <service-endpoint> in ejb-jar.xml. The endpoint also needs a web services deployment descriptor file, webservices.xml, such as in Code Example 5. Notice there is nothing special to do in the deployment descriptor when using attachments.

  <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>
Code Example 7: 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 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.