SOAP with Attachments

While XML and SOAP are very good at describing data, many kinds of application data aren’t well-suited for XML—for example, a piece of binary data such as an image, or a CAD file that contains schematic diagrams of parts being ordered electronically. SOAP with Attachments (SwA) was born in recognition of this limitation. SwA combines the SOAP protocol with the MIME format to allow any arbitrary data to be included as part of a SOAP message. The model is exactly the same as the model used for including email attachments.

Parts Is Parts

The MIME protocol allows multiple arbitrary blocks of data to be strung together in a message, with each block separated by a MIME header. The MIME headers delineate where each part begins and the previous part ends. The next example shows what a MIME header looks like. In SwA, the entire message consists of multiple MIME parts; the first part (part 0) is the SOAP envelope, and the remaining parts (1 through n) are the attachments. All parts are wrapped by the underlying protocol, as illustrated by Figure 3-3.

The structure of a SOAP with Attachments message

Figure 3-3. The structure of a SOAP with Attachments message

To construct and deconstruct SwA messages, use the Apache SOAP and JavaMail APIs. Before running the example, note that the example archive contains a text file called attachment.txt. It is a simple text file that contains the string “This is an attachment.” There is also a file called poWithAttachment.xml that is identical to PO.xml, except that the root level tag is <PurchaseOrderWithAttachment>. The other modification we have made is the addition of an element with the name of attachment. This element contains an href attribute:

<attachment href="cid:the-attachment"/>

This element helps identify the attachment when processing the document. Now run the example from the command line:

java GenericHTTPSWAClient -df ./poWithAttachment.xml -at attachment.txt

In addition to the purchase order, you should see the following output in the Tomcat console window:

Content-ID = the-attachment
The attachment is...
This is an attachment.

Let’s see the raw output from our client by redirecting it at the SimpleHTTPReceive servlet. Run the following command:

java GenericHTTPSWAClient -url 
    http://localhost:8080/examples/servlet/SimpleHTTPReceive
    -df ./poWithAttachment.xml -at attachment.txt

You should see the following output:

____________________________
Received request.
-----------------------
  SOAPAction = "urn:oreilly-jaws-samples"
  Host = localhost
  Content-Type = multipart/related; boundary="----=_Part_0_252212802.10059402721
20"; type="text/xml"; start="1730639424.1005940272280.apache-soap.nbchappell3"
  Content-Length = 1283
-----------------------
------=_Part_0_252212802.1005940272120
Content-Type: text/xml; charset=utf-8
Content-Transfer-Encoding: 8bit
Content-ID: <1730639424.1005940272280.apache-soap.nbchappell3>
Content-Length: 869

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org
/1999/XMLSchema">
<SOAP-ENV:Header>
<jaws:MessageHeader xmlns:jaws="urn:oreilly-jaws-samples"><From>Me</From><To>You
</To><MessageId>9999</MessageId></jaws:MessageHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<PurchaseOrder xmlns="urn:oreilly-jaws-samples">
    ... same as before ...
   <attachment href="cid:the-attachment"/>
</PurchaseOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

------=_Part_0_252212802.1005940272120
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-ID: the-attachment
               This is an attachment.
------=_Part_0_252212802.1005940272120--
____________________________

Note the Content-Type = multipart/related in the HTTP header itself and the individual part boundaries and their associated MIME headers for each of the parts.

Constructing SOAP with Attachments

GenericHTTPSWAClient is identical to GenericHTTPClient, with the addition of the code used to construct the MIME attachments. For that reason, we’ll look only at the changes. First, we need an import statement to allow the use of the MimeBodyPart class, which is included in the JavaMail APIs:

import javax.mail.internet.MimeBodyPart;

Next, there are some parts we won’t get into, which relate to parsing the command line for the attachment file and passing the file to the constructor. The SOAP envelope is created and populated as before, complete with the <header> and <body> constructs. The message is created as usual. Here’s the additional code for creating a MimeBodyPart object and setting its content as the text that we read from the attachment file:

// Build the Message.
org.apache.soap.messaging.Message msg = 
new org.apache.soap.messaging.Message(  );

//Attach any attachments
if(m_attachment != null)
{
    BufferedReader attachmentReader = 
    new BufferedReader(new FileReader(m_attachment));
    StringBuffer buffer = new StringBuffer(  );
    for(String line = attachmentReader.readLine(  ); line != null; 
        line = attachmentReader.readLine(  ))
    {
        buffer.append(line);
    }
    MimeBodyPart attachment = new MimeBodyPart(  );
    attachment.setText(buffer.toString(  ));

Next, we need a way for the receiver to reference the attachment. To enable the receiver to dissect the message, we add an element in the XML document with an href value of the-attachment. We use that value for this attachment part’s content-id:

    attachment.setHeader("Content-ID", "the-attachment");

Finally, we add the attachment part to the message and send it. Apache SOAP knows that you have added an attachment to the message and formats the message appropriately:

    msg.addBodyPart(attachment);
}

msg.send (new java.net.URL(m_hostURL), URI, envelope);
System.out.println("Sent SOAP Message with Apache HTTP SOAP Client.");

Receiving the SOAP with Attachments Message

The method PurchaseOrderAcceptor.PurchaseOrderWithAttachment( ) is similar to PurchaseOrderAcceptor.PurchaseOrder( ), with the addition of the MIME code:

//import statements
...

public class PurchaseOrderAcceptor
{
...

  public void PurchaseOrderWithAttachment(Envelope requestEnvelope,
                                          SOAPContext requestContext,
                                          SOAPContext responseContext)
    throws SOAPException
  {
    System.out.println("Received a PurchaseOrderWithAttachment!!");

    String cid = null;
    java.io.StringWriter writer = new java.io.StringWriter(  );

    // process SOAP header - nothing new here
    ...
    
    // process SOAP body
    org.apache.soap.Body body = requestEnvelope.getBody(  );
    java.util.Vector bodyEntries = body.getBodyEntries(  );
    
    writer.write("
Body====>
");
    for (java.util.Enumeration e = bodyEntries.elements(); e.hasMoreElements(  );)
    {
        org.w3c.dom.Element el = (org.w3c.dom.Element)e.nextElement(  );
        org.apache.soap.util.xml.DOM2Writer.serializeAsXML(
            (org.w3c.dom.Node)el, writer);

Remember the <attachment href="cid:the-attachment"/> element that we put into poWithAttachment.xml? We now use it to find the attachment. First, we retrieve the <attachment> element by name. Then we extract the value of the content-id stored in the href attribute. Once we have the ID, we can use the getBodyPart( ) method on the SOAPContext object to retrieve the MIME attachment by content-id:

        org.w3c.dom.Element attachmentEl = 
           (org.w3c.dom.Element)el.getElementsByTagName("attachment").item(0);
        if (attachmentEl != null)
        {
            writer.write("
Attachment==>
");
            cid = attachmentEl.getAttribute("href").substring(4);//get rid of cid:
            writer.write("Content-ID = "+cid+"
");
            MimeBodyPart attachment = requestContext.getBodyPart(cid);
           try
            {
                writer.write(
                    "The attachment is...
"+attachment.getContent(  )+"
");
            }catch(Exception ex)
            {
                throw new SOAPException(Constants.FAULT_CODE_SERVER, 
                    "Error writing response", ex);
            }
        }else
            writer.write("The Content-ID is null!
");
    }
    System.out.println(writer.toString(  ));
...
  }

}

Now that we’ve had a closer look at SOAP and how it structures XML messages and the types of processing it can perform as part of message handling, let’s study SOAP more deeply. SOAP message passing is important in its own right (and, as we’ve already seen, the SOAP specification stresses it’s message-passing foundations), but most developers see SOAP as a mechanism for remote procedure call (RPC). In Chapter 4, we’ll look at SOAP-RPC in more detail and discuss the Fault mechanism and the MustUnderstand header.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset