Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Patterns and Strategies for Building Document-Based Web Services

 
Using Message Attachments

[Page 1] [Page 2] [Page 3] [Page 4] [Page 5] [Page 6]

A SOAP message may also contain one or more attachments using the MIME encoding, and is often referred to as a compound message. Sending information in an attachment, rather than the SOAP message body, is quite efficient because smaller SOAP message bodies are processed faster, and since the message contains only a reference to the data and not the data itself, it reduces the translation time in mapping the data to Java objects is reduced

JAX-RPC uses the JavaBeans activation framework for dealing with SOAP attachments. When un-marshalling this message to Java, the JAX-RPC runtime can use two mapping techniques.

  1. It can map well-known mime types to Java objects as per Table 1 and vice versa using built-in DataHandlers and DataContentHandlers in the runtime.
  2. It can map the attachment to a javax.activation.DataHandler using the JavaBeans Activation framework and vice versa.

In simple terms, this means that if a method in a service endpoint uses a data type from Table 1 or a javax.activation.DataHandler, the runtime will map that to a MIME attachment in the SOAP message. When using the javax.activation.DataHandler, the content of the attachment can then be extracted using a getContent() on the DataHandler. If the content type is not understood by the installed DataContentHandler it will return a java.io.InputStream object with the raw bytes.

Table 1: MIME Types to Java Data Type Mapping
 
 
MIME
Type
image/gif
java.awt.Image
image/jpeg
java.awt.Image
text/plain
java.lang.String
multipart/*
javax.mail.internet.MimeMultipart
text/xml or application/xml
javax.xml.transform.Source


Example

The example below demonstrates use of attachments and the standard mappings through the DataHandler, as well as the Source objects. The service describes a document service that stores documents it receives on the server. The endpoint interface (Code Sample 52) contains two methods, one that accepts a javax.activation.DataHandler and another that accepts a javax.xml.transform.Source object. The implementation (Code Sample 53) retrieves the contents, stores them as files with the given filename, and returns a timestamp to the client.

Code Sample 52: Endpoint Interface for the Attachment Service

package com.examples.attachmentservice;

import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.activation.DataHandler;
import javax.xml.transform.Source;

public interface AttachmentService extends Remote{
    
    public String storeDocumentService( DataHandler dh,String filename) 
	    throws RemoteException ;
	public String storeDocumentXML( Source dh,String filename) 
	    throws RemoteException ;
}

Code Sample 53: Implementation Class for the Attachment Service

package com.examples.attachmentservice;

import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.activation.DataHandler;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import java.util.Date;
import java.io.*;

public class AttachmentServiceImpl implements AttachmentService {

    /**
     * This method implements a Web service that stores any attachment it receives.
     */
    public String storeDocumentService(DataHandler dh, String filename) {
        try {
            BufferedOutputStream out = new BufferedOutputStream(new 
			    FileOutputStream("/" + filename));
            BufferedInputStream in = new BufferedInputStream(dh.getInputStream());
            byte[] buffer = new byte[256];
            while (true) {
                int bytesRead = in.read(buffer);
                if (bytesRead == -1)
                    break;
                out.write(buffer, 0, bytesRead);
            }
            in.close();
            out.close();
        } catch (Exception e) {
            System.out.println(e);
            return e.toString();
        }
        return ("File processes successfully " + filename + "  " + new Date());
    }


    public String storeDocumentXML(Source dh, String filename) {

        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
		  File f = new File ("/"+filename);
            transformer.transform(dh, new StreamResult(f));        
       } catch (Exception e) {
            System.out.println(e);
        }
         return ("File processes successfully " + filename + "  " + new Date());
    }



}

When this example found in the <Attachment-From-Java> directory, is built and deployed, the WSDL is automatically generated for the service by wsdeploy and is shown in Code Sample 54. This example uses the RPC-Encoded formatting.

In JWSDP, attachment handling is only supported in this formatting mode when starting from Java. The WS-I Attachment Profile 1.0 defines an interoperability requirements for attachment handling and is fully supported by JWSP 1.4. It is possible to build interoperable, WS-I conforming, attachment based web services when starting from WSDL using the MIME bindings. The example in the <Attachments-From-WSDL> directory demonstrates this. Another example of using the WS-I "swaRef" type can be found in this white paper.

Code Sample 54: WSDL for the Attachment Service

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://examples.com/wsdl/AttachmentService"
    xmlns:ns2="http://java.sun.com/jax-rpc-ri/internal"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	name="AttachmentService"
    targetnamespace="http://examples.com/wsdl/AttachmentService">
	<types>
		<schema xmlns="http://www.w3.org/2001/XMLSchema"
		    xmlns:tns="http://java.sun.com/jax-rpc-ri/internal"
			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/"
			targetnamespace="http://java.sun.com/jax-rpc-ri/internal">
			<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
			<simpletype name="datahandler">
				<restriction base="base64Binary"/>
			</simpletype>

			<simpletype name="text_xml">
				<restriction base="string"/>
			</simpletype>
		</schema>
	</types>
	<message name="AttachmentService_storeDocumentService">
		<part name="DataHandler_1" type="ns2:datahandler"/>
		<part name="String_2" type="xsd:string"/>
	</message>
	<message name="AttachmentService_storeDocumentServiceResponse">
		<part name="result" type="xsd:string"/>
	</message>
	<message name="AttachmentService_storeDocumentXML">
		<part name="Source_1" type="ns2:text_xml"/>
		<part name="String_2" type="xsd:string"/>
	</message>
	<message name="AttachmentService_storeDocumentXMLResponse">
		<part name="result" type="xsd:string"/>
	</message>
	<porttype name="AttachmentService">
		<operation name="storeDocumentService" parameterorder="DataHandler_1 String_2">
			<input message="tns:AttachmentService_storeDocumentService"/>
			<output message="tns:AttachmentService_storeDocumentServiceResponse"/>
		</operation>
		<operation name="storeDocumentXML" parameterorder="Source_1 String_2">
			<input message="tns:AttachmentService_storeDocumentXML"/>
			<output message="tns:AttachmentService_storeDocumentXMLResponse"/>
		</operation>
	</porttype>
	<binding name="AttachmentServiceBinding" type="tns:AttachmentService">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
		<operation name="storeDocumentService">
			<soap:operation soapaction=""/>
			<input>
				<soap:body
				    encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
					use="encoded"
                    namespace="http://examples.com/wsdl/AttachmentService"/>
			</input>
			<output>
				<soap:body
				    encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
					use="encoded"
					namespace="http://examples.com/wsdl/AttachmentService"/>
			</output>
		</operation>
		<operation name="storeDocumentXML">
			<soap:operation soapaction=""/>
			<input>
				<soap:body
				    encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
					use="encoded"
				    namespace="http://examples.com/wsdl/AttachmentService"/>
			</input>
			<output>
				<soap:body
				    encodingstyle="http://schemas.xmlsoap.org/soap/encoding/"
					    use="encoded"
                        namespace="http://examples.com/wsdl/AttachmentService"/>
			</output>
		</operation>
	</binding>
	<service name="AttachmentService">
		<port name="AttachmentServicePort" binding="tns:AttachmentServiceBinding">
			<soap:address xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
			    location="http://127.0.0.1:8080/attachmentservice/jaxrpc"/>
		</port>

	</service>
</definitions>

The client (Code Sample 55) sends two different attachments; the first is a binary document file in Word format and the second is in XML.

Code Sample 55: Client Class Sending Attachments

import javax.activation.*;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Source;
import java.io.*;
import com.examples.attachmentservice.clientbindings.*;

public class WSDLClient {
    public static void main(String[] args) {
        try {
            String url = ""http://127.0.0.1:9090/attachmentservice/jaxrpc"";
            String filename = "Uploadme.doc";
            String xmlfilename="purchaseorder.xml";
            if (args.length == 3) {
                url = args[0];
                filename = args[1];
                xmlfilename=args[2];
            }
            AttachmentService_PortType_Stub stub = (AttachmentService_PortType_Stub) 
			   (new AttachmentService_Service_Impl().getAttachmentServicePort());
            stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, url);

	   sentAttachmentWithSource(xmlfilename,stub);
	   sendAttachmentWithDataHandler(filename, stub);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static void sendAttachmentWithDataHandler(String filename,
	    AttachmentService_PortType_Stub stub) throws Exception {
        DataHandler dh = new DataHandler(new FileDataSource(filename));
        String response = stub.storeDocumentService(dh, filename);
        System.out.println("Response from server " + response);
    }
    private static void sentAttachmentWithSource  (String filename,
	    AttachmentService_PortType_Stub stub) throws Exception{
        Source src= new StreamSource(new File(filename));
       String response = stub.storeDocumentXML(src, filename);
        System.out.println("Response from server " + response);
    }

}

The SOAP requests sent over the wire are also shown in Code Sample 56-a for the binary attachment and Code Sample 56-b with the XML data respectively.

Code Sample 56-a: SOAP Request with a Binary Attachment

------=_Part_1_18471958.1078366362677
<?xml version="1.0" encoding="UTF-8"?>
<env:envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://examples.com/wsdl/AttachmentService"
xmlns:ns1="http://java.sun.com/jax-rpc-ri/internal"
env:encodingstyle="http://schemas.xmlsoap.org/soap/encoding/">
<env:body>
<ns0:storedocumentservice><datahandler_1 xsi:type="ns1:datahandler" href="cid:ID1"/>
<string_2 xsi:type=xsd:string">Uploadme.doc</string_2>
</ns0:storedocumentservice></env:body></env:envelope>
------=_Part_1_18471958.1078366362677
Content-Type: application/octet-stream
Content-Id: ID1
ÐÏàì±à

Code Sample 56-b: SOAP Request with an XML Attachment

------=_Part_0_2352593.1093885906546
Content-Type: text/xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" 
	      xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	      xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" 
	      xmlns:ns0="http://examples.com/wsdl/AttachmentService" 
	      xmlns:ns1="http://java.sun.com/jax-rpc-ri/internal" 
	      env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<env:Body>
		<ns0:storeDocumentXML>
			<Source_1 xsi:type="ns1:text_xml" href="cid:ID1"/>
			<String_2 xsi:type="xsd:string">purchaseorder.xml</String_2>
		</ns0:storeDocumentXML>
	</env:Body>
</env:Envelope>
------=_Part_0_2352593.1093885906546
Content-Type: text/xml
Content-Id: ID1

<?xml version="1.0" encoding="UTF-8"?>
<tns:PurchaseOrderDocument xmlns:tns="http://www.examples.com/types"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	    xsi:schemaLocation="http://www.examples.com/types PurchaseOrder.xsd">
	<billTo>
		<street>1 Main Street</street>
		<city>Beverly Hills</city>
		<state>CA</state>
		<zipCode>90210</zipCode>
	</billTo>
	<createDate>2004-03-27T12:21:02.055-05:00</createDate>
	<items>
		<itemname>Copier Paper</itemname>
		<price>10</price>
		<quantity>2</quantity>
	</items>
	<items>
		<itemname>Toner</itemname>
		<price>920</price>
		<quantity>1</quantity>
	</items>
	<poID>ABC-CO-19282</poID>
	<shipTo>
		<street>1 Main Street</street>
		<city>Beverly Hills</city>
		<state>CA</state>
		<zipCode>90210</zipCode>
	</shipTo>
</tns:PurchaseOrderDocument>

------=_Part_0_2352593.1093885906546--

Sample Architecture - PetStore Application

The web services-enabled PetStore sample application uses this strategy for implementing a document-oriented solution. The OPC-Supplier in the PetStore uses the JAX-RPC API for passing XML documents.

The communications that happen between the OPC and the Supplier are:

  • The OPC submits the approved purchase order to the Supplier
  • The Supplier returns the invoice back to the OPC for the Items that have been shipped

The supplier implements a web service with two methods as shown in Code Sample 57-a. The Service Endpoint Implementation is a Java class shown in Code Sample 57-b that extracts the Source object and places it on a JMS queue for asynchronous processing at a later time.

Code Sample 57-a: PetStore SupplierService Endpoint Interface

package com.sun.j2ee.blueprints.supplier.webservice;

import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.xml.transform.Source;

public interface SupplierService extends Remote {
   public String submitOrder(Source supplierOrder) 
       throws InvalidOrderException, RemoteException;

    public String queryOrderStatus(String supplierOrderId) 
	    throws UnknownOrderIdException, RemoteException;
}

Code Sample 57-b: PetStore SupplierService Implementation

package com.sun.j2ee.blueprints.supplier.webservice;

import javax.xml.rpc.*;
import javax.xml.rpc.server.*;
import javax.xml.transform.Source;
import java.rmi.*;

import com.sun.j2ee.blueprints.xmldocuments.XMLDocumentException;
import com.sun.j2ee.blueprints.supplier.webservice.porcvr.*;
import com.sun.j2ee.blueprints.supplier.webservice.poquery.*;


public class SupplierServiceImpl implements SupplierService, ServiceLifecycle {


  public void init(Object context) throws JAXRPCException {}

  public String submitOrder(Source supplierOrder) throws 
      InvalidOrderException, RemoteException {
      SupplierOrderRcvr supplierOrderRcvr = new SupplierOrderRcvr();
      return supplierOrderRcvr.receive(supplierOrder);
  }

  public String queryOrderStatus(String supplierOrderId) 
      throws UnknownOrderIdException, RemoteException {
      SupplierOrderStatusQuery supplierOrderStatusQuery = new 
	      SupplierOrderStatusQuery();
      return supplierOrderStatusQuery.queryStatus(supplierOrderId);
  }

  public void destroy() {}
}

Consequences

Pros
  • Useful for documents that may conform to schemas expressed in languages not supported by the web service endpoint, or that are prohibited from being present within a SOAP message infoset (such as the Document Type Declaration <!DOCTYPE> for a DTD-based document)
  • Useful for large documents (can be compressed-decompressed)
  • Additional facilities can be built on the attachments using handlers. For example, Code Sample 58 show a client-side handler that adds custom encryption using passwords to the attachment and then compresses it using the GZIP algorithm. The corresponding server side handler in Code Sample 59 decompresses the content from the attachment and decrypts it using the same password-based encryption algorithm.

Code Sample 58: A Sample Client-Side JAX-RPC Handler

package com.examples.encryptedposervice;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.xml.soap.*;
import javax.xml.rpc.handler.*;
import javax.xml.rpc.handler.soap.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.Security;
import javax.xml.namespace.QName;
import com.examples.encryptedposervice.utils.Base64;

public class SecureZipClientHandler implements Handler {
	private static final byte salt[] = new byte[8];
	private static final int iterations =1;
    private final static String algorithm = "PBEWithMD5AndDES";
    private static SecretKeyFactory skf;
	private static PBEParameterSpec aps ;
    private final static char[] password = 
	    "1eallysecurepassword".toCharArray();

    public void init(HandlerInfo hi) {
        try {
            // This is to make sure that the provider is installed
            // see docs with JCE on how to install providers using the
            // policy files
            Security.addProvider(new com.sun.crypto.provider.SunJCE());
            skf = SecretKeyFactory.getInstance(algorithm);
			/*
			Add another level of encryption by adding a salt to the key
			The salt itself is based on some fixed bytes.
			*/
			aps = new PBEParameterSpec(salt,iterations);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public boolean handleRequest(MessageContext context) {
        try {
            SOAPMessageContext smc = (SOAPMessageContext)context;
            SOAPMessage msg = smc.getMessage();
            SOAPPart sp = msg.getSOAPPart();
            SOAPEnvelope se = sp.getEnvelope();
            // next step based on the processing model for this handler
            SOAPBody body = se.getBody();
            Iterator it = body.getChildElements();
            SOAPElement opElem = (SOAPElement)it.next();
            it = opElem.getChildElements();
            SOAPElement pin = (SOAPElement)it.next();
            it = pin.getChildElements();
            Text textNode = (Text)it.next();
            textNode.detachNode();
            String encContent = textNode.getValue();
            byte[] contentBytes = Base64.decode(encContent);
            // zip it
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPOutputStream zos = new GZIPOutputStream(baos);
            zos.write(contentBytes);
            zos.flush();
            zos.finish();
            zos.close();
            byte[] zippedbytes = encrypt(baos.toByteArray());
            String zippedContent = Base64.encode(zippedbytes);
            System.out.println("Client handler done with 
			    encryption and compression");
            pin.addTextNode(zippedContent);
            return true;
        }
        catch (Exception e) {
            System.out.println(e);
            return false;
        }
    }

    public boolean handleResponse(MessageContext context) {
        return true;
    }

    public boolean handleFault(MessageContext context) {
        return true;
    }

    public void destroy() {
    }

    public QName[] getHeaders() {
        return null;
    }

    private static byte[] encrypt(byte[] clear) throws Exception {
        byte[] ciphertext = null;

	/*
		 Generate the key for the symmetric encryption
	*/

        PBEKeySpec ks = new PBEKeySpec(password);
        SecretKey key = skf.generateSecret(ks);
        Cipher desCipher = Cipher.getInstance(algorithm);
        desCipher.init(Cipher.ENCRYPT_MODE, key,aps);
        ciphertext = desCipher.doFinal(clear);
        return ciphertext;
    }
}

Code Sample 59: A Sample JAX-RPC Server-Side Handler

package com.examples.base64;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import com.examples.encryptedposervice.utils.Base64;
import javax.xml.soap.*;
import javax.xml.rpc.handler.*;
import javax.xml.rpc.handler.soap.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.Security;
import javax.xml.namespace.QName;

public class SecureZipServerHandler implements Handler {
    private final static String algorithm = "PBEWithMD5AndDES";
	private static final byte salt[] = new byte[8];
	private static final int iterations =1;
    private static SecretKeyFactory skf;
    private final static char[] password = "1eallysecurepassword".toCharArray();
	private static PBEParameterSpec aps ;

    public boolean handleResponse(MessageContext context) {
        return true;
    }

    public void init(HandlerInfo hi) {
        try {
            // This is to make sure that the provider is installed
            // see docs with JCE on how to install providers using the
            // policy files
            Security.addProvider(new com.sun.crypto.provider.SunJCE());
            skf = SecretKeyFactory.getInstance(algorithm);
			/*
			Add another level of encryption by adding a salt to the key
			The salt itself is based on some fixed bytes.
			*/
			aps = new PBEParameterSpec(salt,iterations);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public boolean handleRequest(MessageContext context) {
        try {
            SOAPMessageContext smc = (SOAPMessageContext)context;
            SOAPMessage msg = smc.getMessage();
            SOAPPart sp = msg.getSOAPPart();
            SOAPEnvelope se = sp.getEnvelope();
            // next step based on the processing model for this handler
            SOAPBody body = se.getBody();
            Iterator it = body.getChildElements();
            SOAPElement op = (SOAPElement)it.next();
            SOAPElement param = (SOAPElement)op.getChildElements().next();
            Text textNode = (Text)param.getChildElements().next();
            String zippedenccontent = textNode.getValue();
            System.out.println(zippedenccontent);
            textNode.detachNode();
            byte[] rawbytes = Base64.decode(zippedenccontent);
             rawbytes = decrypt(rawbytes);
            // unzip
             ByteArrayInputStream bais = new ByteArrayInputStream(rawbytes);
            GZIPInputStream zis = new GZIPInputStream(bais);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int c = -1;
            while ((c = zis.read()) != -1) {
                baos.write(c);
            }
            baos.flush();
            byte[] contentBytes = baos.toByteArray();
            System.out.println("Server handler done with decryption and decompression");
            String encContent = Base64.encode(contentBytes);
            param.addTextNode(encContent);
            return true;
        }
        catch (Exception e) {
            System.out.println(e);
            return false;
        }
    }

    // method to decrypt bytes
    private static byte[] decrypt(byte[] input) throws Exception {
        byte[] cleartext1 = null;
        PBEKeySpec ks = new PBEKeySpec(password);
        SecretKey key = skf.generateSecret(ks);
        Cipher desCipher = Cipher.getInstance(algorithm);
        desCipher.init(Cipher.DECRYPT_MODE, key,aps);
        cleartext1 = desCipher.doFinal(input);
        return cleartext1;
    }

    public boolean handleFault(MessageContext context) { return true; }

    // Lifecycle method
    public void destroy() { }

    public QName[] getHeaders() { return null; }
}

Cons
  • In JWSDP 1.3, Attachment types are not supported in literal mode for the javax.activation.DataHandler
  • Interoperability
    • Soap With Attachments profile from WS-I is fully supported in JWSDP 1.4 including literal mode
    • Not all vendors support attachment. For example, .NET uses its own mechanism and DIME, though an extensions pack
Using a URI to Reference the Business Document

It is possible for the service consumer to send a SOAP message that contains a reference to the business document, and not the actual document itself. The service provider can then use the reference to resolve and obtain the business document in a separate call.

In XML Schemas, the anyURI data type is used to represent a Uniform Resource Identifier, commonly referred to as a URI. This type is mapped to the Java data type java.net.URI (in J2SE 1.4 only) or a String and can be used to convey the location of the business document.

 
Figure 9: Using a Reference to Pass the Business Document
Figure 9: Using a Reference to Pass the Business Document
Example

The WSDL extract in Code Sample 60 demonstrates how the xsd:anyURI data type can be used to indicate the location of the business document instead of the actual document.

Code Sample 60: WSDL Extract Using xsd:anyURI

<definitions xmlns:tns="http://www.examples.com/wsdl/poservice"  
             xmlns="http://schemas.xmlsoap.org/wsdl/" 
             xmlns:ns1="http://www.examples.com/types"                
             xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
			     targetnamespace="http://www.examples.com/wsdl/poservice"
				     name="POService">
 <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="http://www.examples.com/types">		
    <import schemalocation="PurchaseOrder.xsd" 
           namespace="http://www.examples.com/types" id="ns1"/>

    <import schemalocation="PurchaseOrderStatus.xsd"  
           namespace="http://www.examples.com/types" id="ns1"/>		
    <import schemalocation="POProcessingProblem.xsd"  
           namespace="http://www.examples.com/types" id="ns1"/>		
    <element name="BusinessDocumentRequest" type="xsd:anyURI"/>
    <annotation> The request points to an XML document defined by the 
                 PurchaseOrder.xsd schema </annotation>
   </schema>
  </types>
 <!—other wsdl elements>
</definitions>

Code Sample 61: Generated SEI Interface for xsd:anyURI

package com.examples.anyuri;

public interface IPurchaseOrder extends java.rmi.Remote {
    public com.examples.anyuri.PurchaseOrderStatus acceptPO(java.net.URI parameters) throws 
        com.examples.anyuri.POProcessingProblem,  java.rmi.RemoteException;
}

The endpoint interface generated from this WSDL by wscompile maps the anyURI to a java.net.URI parameter. The implementation can then use the URI and resolve it to obtain the XML data. Code Sample 62 shows the extract for the corresponding implementation class, which upon receiving the SOAP request first opens an HTTP connection to the location indicated by the URI, and then obtains, validates and parses the XML using JAXB API. As mentioned previously, the schema need to be compiled into their Java bindings at compile time using the JAXB schema compiler. The example described here processes the request synchronously, meaning that it retrieves and parses the XML document prior to returning the HTTP response to the client. This can be easily modified so that an immediate acknowledgement message is returned to the client. The service implementation can then asynchronously retrieve and process the XML using techniques like starting threads, thread pools, and concurrency utilities.

Code Sample 62: Service Implementation for the xsd:anyURI Example


public class PurchaseOrderService extends IPurchaseOrder_Impl {
    public PurchaseOrderStatus acceptPO(URI uri) throws POProcessingProblem, RemoteException {
        if (uri == null) {
            throw new POProcessingProblem("Web Service was passed a null URI for a Purchase order");
        } 
        System.out.println("PurchaseOrder received in the web service" + uri);
	try{
		InputStream in=	uri.toURL().openStream();
		PurchaseOrder order=toObject(in);
		System.out.println("JAXB Unmarshalled the purchase Order ");
		System.out.println("PurchaseOrder received in the web service");
		System.out.println("Order ID " + order.getPoID());
		System.out.println("Order creation date " + order.getCreateDate());
		System.out.println("Ship To Address " + order.getShipTo());
		System.out.println("Bill To Address " + order.getBillTo());
		System.out.println("Order Contents :");
		List items = order.getItems();
		Iterator it = items.iterator();
		while (it.hasNext()) {
			LineItem item = (LineItem) it.next();
			System.out.println("Item Name =" + item.getItemname() +  
                                    "Price=" + item.getPrice() + 
                                    " Quantity=" + item.getQuantity());
			}
		}catch(Exception ex){
	           throw new POProcessingProblem("Purchase order cannot be 
                                               processed " +ex);
		}

        PurchaseOrderStatus status = new PurchaseOrderStatus();
        status.setTimestamp(new Date().toString());
        status.setOrderid("ABC" + System.currentTimeMillis());
        return status;
		 } 
    /**
     * Unmarshall the stream to its JAXB object representation
     */
    public PurchaseOrder toObject(InputStream in) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("com.examples.anyuri.po");
        Unmarshaller u = jc.createUnmarshaller();
		// enable validation
        u.setValidating( true );
        Object o = u.unmarshal(in);
		// use the object that is generated by JAXB
        PurchaseOrder order = (PurchaseOrder) o;
		return order;
    }

From the client's perspective, the code is quite simple. It only needs to construct a URI object indicating where the document can be retrieved from and pass that to the web service. The code extract shown in Code Sample 63 demonstrates how this can be done.

Code Sample 63: Extract From the Web Service Client Code

public static void main(String[] args) throws Exception {
  String url = "http://localhost:9090/anyuri/jaxrpc";
  POService_Impl serviceproxy = new POService_Impl();
  IPurchaseOrder_Stub stub = (IPurchaseOrder_Stub) 
                                   (serviceproxy.getIPurchaseOrderPort());
  stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, url);
  PurchaseOrderStatus result = 
      stub.acceptPO(new URI("http://localhost:9090/anyuri/purchaseorder.xml"));
  System.out.println("Order id is : " + result.getOrderid());
  System.out.println("Timestamp is : " + result.getTimestamp());
    }

Consequences

Pros
  • The strategy minimizes the payload size since only a reference to the data and not the data itself is contained in the message.
  • The data can be dynamically generated if necessary as and when the service provider requests it, or even be cached in proxy servers for optimized processing. This can be useful when dealing with repeated reads that use high latency EIS resources.
  • Architectures can be built where the service producer obtains the business document at a later time, thus enabling asynchronous processing. However in such cases, the semantics relating to errors in processing networking need to be carefully predefined.
  • Data binding APIs like JAXB can be used directly by the service provider to parse the business documents.
  • Can be useful when the same service needs to process multiple documents based on different schemas.
Cons
  • Additional network hops and increased IO introduced in the architecture may increase latency.
  • The data may have become stale in the context of the business process by the time the service consumer accesses it.
  • The client needs to have access to a web server or a way to serve up the XML documents when the web service requests them. There also has to be an out-of-band negotiation regarding security and error handling mechanisms for the same.
  • Although the XML document is self describing in that it contains a reference to its schema, out-of-band negotiation may be needed to establish this relationship since using the xsd:anyURI essentially obscures this from the WSDL.
  • Possible security implications of such an approach need to be considered by architects, since spurious clients can introduce malicious code on the server side.

[Page 1] [Page 2] [Page 3] [Page 4] [Page 5] [Page 6]