by Michelle Cope
We want to hear from you! Please send us your
FEEDBACK.
The following Technical Article may contain actual software programs in source
code form. This source code is made available for developers to use as needed,
pursuant to the terms and conditions of this
license.
Overview
The Java [tm] API for XML Messaging ("JAXM") provides a set of
tools to construct and send Simple Object Access Protocol (SOAP)
messages. The API provides two main ways to construct SOAP messages:
creating an object tree where each object represents part of the
message (for example, SOAPBody, SOAPBodyElement) or using the
setContents(javax.xml.transform.Source) method on the SOAPPart
object. The javax.xml.transform.Source object of setContent is a
handle on an XML input source - for example, an XML file. However, this XML
input source must represent a valid SOAP message. That is, the input
source must have all the correct SOAP namespaces, otherwise a
javax.xml.soap.SOAPException is thrown. There is no provision in the
API to include a non-SOAP XML file as part of the body of a SOAP
message. It would save time if there was some way to add the XML file
into the SOAP message without doing the tedious chore of constructing
a SOAP-compliant object tree of an existing XML file. This article
will illustrate a way to use an existing XML file as part of
the SOAP message's body.
The solution is pretty simple. It uses a feature of the SOAP
Messages with Attachments specification. You place the
existing XML file as an attachment to the SOAP Message, and place a
reference to the attachment in the correct part of the SOAP message's
body. The reference is a URI value of an attribute called href. The
href attribute can be an attribute of both SOAP header and body
elements. The href attribute value uses MIME headers to reference
attachments carried as MIME parts in the entire message package.
The example used in this article implements a simple invoicing
service for the purchase of a CD player. A supply company sends an
invoice form, in a SOAP message to a customer. The following SOAP
message is an example of the invoice form. The invoice form has the
details of the customer in the main SOAP body and the CD player
details are in an attached XML file. The attached XML file has its
content-location MIME header set to
"http://www.mystore.com/products/cdplayer.xml". The same
value is set for the href attribute value of the <Attachment>
element.
--13595793.1012478930408.JavaMail.root.ditton
Content-Type: text/xml
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header/>
<soap-env:Body>
<CustomerDetails>
<Name>Joe Bloggs</Name>
<Customer_No>1234</Customer_No>
<Telephone>01234-43525</Telephone>
</CustomerDetails>
<Purchase>
<Attachment href="http://www.mystore.com/products/cdplayer.xml"/>
</Purchase>
</soap-env:Body>
</soap-env:Envelope>
--13595793.1012478930408.JavaMail.root.ditton
Content-Type: text/xml
Content-Location: http://www.mystore.com/products/cdplayer.xml
<?xml version="1.0"?>
<cdplayer>
<Manufacturer> Panasonic </Manufacturer>
<Serial_No>SKJ128329</Serial_No>
<Catalogue_No> 2343</Catalogue_No>
<Purchase_Price> 54.45</Purchase_Price>
</cdplayer>
--13595793.1012478930408.JavaMail.root.ditton--
The following skeleton code will generate the above SOAP Message.
The full code for creating and sending the message is in
SendingServlet.java
import java.net.*;
import java.io.*;
import java.util.*;
import javax.servlet.http.*;
import javax.servlet.*;
import javax.xml.messaging.*;
import javax.xml.soap.*;
import javax.activation.*;
import javax.naming.*;
...
// Create a soap body from the envelope.
SOAPBody bdy = envelope.getBody();
// Add a soap the customer details to the body
SOAPBodyElementcustomerDetails = bdy.addBodyElement(envelope.createName("CustomerDetails"));
customerDetails.addChildElement(envelope.createName("Name")).addTextNode("Joe Bloggs");
customerDetails.addChildElement(envelope.createName("Customer_No")).addTextNode("1234");
customerDetails.addChildElement(envelope.createName("Telephone")).addTextNode("01234-43525");
//Create the reference to the attached xml file
SOAPElement purchase = bdy.addBodyElement(envelope.createName("Purchase"));
SOAPElement attachment = purchase.addChildElement(envelope.createName("Attachment"));
attachment.addAttribute(envelope.createName("href"),"http://www.mystore.com/products/cdplayer.xml");
//Data is a URL pointing to the cdplayer.xml.
URL url = new URL(data);
AttachmentPart ap = msg.createAttachmentPart(new DataHandler(url));
ap.setContentType("text/xml");
ap.setContentLocation("http://www.mystore.com/products/cdplayer.xml");
// Add the attachment part to the message.
msg.addAttachmentPart(ap);
...
If you have sent a message constructed using the above
functionality, on the receiver side you may want to treat the SOAP
Message and the attached XML file as one XML document. The following
code will demonstrate how to create one DOM representation of the
SOAP body and the attached XML document. The entire code is in
ReceivingServlet.java. The
steps are as follows:
- Initially retrieve the SOAP message as an XML input source,
and create its corresponding DOM object, which will be called
domSOAPMessage.
...
SOAPPart sp = message.getSOAPPart();
//Retrieve SOAPMessage part
Source source = sp.getContent();
//Create a DOMRepresentation of SOAPMessage
DOMResult domSOAPMessage = getDOMResult(source);
//Needfully qualified name of Node to avoid ambiguity
//Retrieve the root of the SOAPMessage
org.w3c.dom.Node domSOAPMessageRoot = domSOAPMessage.getNode();
...
where
getDOMResult(javax.xml.transform.Source)will
transform the javax.xml.transform.Source into a DOMResult object.
The DOMResult is contained in the Document object returned
bygetNewEmptyDocument().
public DOMResult getDOMResult(Source source)
throws ParserConfigurationException,
TransformerConfigurationException,
TransformerException{
DOMResult domResult = null;
//Create a new transformer object
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
//Transform the source, into its DOM representation and keep a reference to the DOMresult
transformer.transform(source,(domResult = new DOMResult(getNewEmptyDocument())));
return domResult;
}
- Determine if there is an element in the
domSOAPMessage
which has an href attribute. Keep a reference to this element, which
will be called attachmentNode.
Next, determine if there is an accompanying attachment which can be
referenced by the href attribute's value.
...
//The SOAPMessage can be parsed into a DOM, so continue
Element domSOAPMessageRootEl= (((Document)domSOAPMessageRoot).getDocumentElement());
//loop around a while loop to place all XML attachments into
//DOM representation of SOAPMessage
//Find an attachment node by looking for elements with an
//attribute name equal to href.
org.w3c.dom.Node attachmentNode = null;
while((attachmentNode = findAttachmentNode(domSOAPMessageRootEl)) != null){
//As there is attachmentNode in the SOAP message body there must be a matching attachment
//Retrieve attachment as DOM
//Retrieve href reference value
System.out.println("Attribute node value"+ attachmentNode.getNodeName());
String attachmentName = getHrefValue(attachmentNode);
//Retrieve the attachment with matching values
AttachmentPart xmlAttachment = findXMLAttachment(message,attachmentName);
...
The findAttachmentNode(org.w3c.dom.Node
node)method conducts a depth-first search of the SOAP
body message to find if any element has an href attribute. If an
attachment element exists then extract the href attribute's value and
look for the referent attachment using the
findXMLAttachment(javax.xml.soap.SOAPMessage,
java.lang.String) method. According to the SOAP
Messages with Attachments specification there are three ways to reference
an attachment. Only one of these is implemented in findXMLAttachment
for the sake of simplicity. It assumes that the href attribute value
is an absolute URI, and that the matching attachment has a
content-location MIME header with the same value.
findXMLAttachment(javax.xml.soap.SOAPMessage,
java.lang.String) will throw an exception if either no
matching attachment exists or a matching attachment exists but it
does not have a content type of "text/html".
- If so, extract the contents of the attachment using a
javax.activation.DataHandler, and create its DOM representation,
which will be called
domAttachment.
...
//Convert the attachment part into its DOM Representation
DataHandler attachmentContent = xmlAttachment.getDataHandler();
StreamSource attachmentStream = new StreamSource(attachmentContent.getInputStream());
DOMResult domAttachment = getDOMResult(attachmentStream);
org.w3c.dom.Node domAttachmentNode = domAttachment.getNode();
...
- Replace the
attachmentNode
(the element with the href attribute) in the SOAP message's DOM
representation with the DOM representation of the attachment. The
org.w3c.dom.DocumentFragment interface is used to create a new piece
on an existing DOM representation.
...
//Add the DOM representation of attachment into the DOM
//representation of the SOAPMessage at the correct place
//Create a documentFragment of DOM representation of attachment
DocumentFragment docFrag = domAttachmentDocument.createDocumentFragment();
Element el = domAttachmentDocument.getDocumentElement();
System.out.println("adding element to docFrag");
//Add the DOM attachment as a child of docFrag
docFrag.appendChild(el);
/**
Want to replace the attachmentNode in SOAPMessage (i.e. the
node with href attribute) with the contents of attachment.
Need to retrieve the parent node of the attachmentNode to
replace the attachmentNode with the docFragment that contains
the contents of the attachment as a DOM Node
**/
org.w3c.dom.Node attachmentparent =
attachmentNode.getParentNode();
org.w3c.dom.Node result = ((Document)domSOAPMessageRoot).importNode(docFrag, true);
attachmentparent.replaceChild(result, attachmentNode);
} //end of while loop in point2
...
- Print out the modified
domSOAPMessage using the printOutDocument(org.w3c.dom.Node
root, java.lang.String outputFile) method.
public void printOutDocument(org.w3c.dom.Node root, String outputFile)
throws TransformerException,
TransformerConfigurationException,
FileNotFoundException{
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(root),
new StreamResult(new FileOutputStream(outputFile)));
}
The modified domSOAPMessage,
when output, looks like:
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header/>
<soap-env:Body>
<CustomerDetails>
<Name>Joe Bloggs</Name>
<Customer_No>1234</Customer_No>
<Telephone>01234-43525</Telephone>
</CustomerDetails>
<Purchase>
<cdplayer>
<Manufacturer> Panasonic </Manufacturer>
<Serial_No>SKJ128329</Serial_No>
<Catalogue_No> 2343</Catalogue_No>
<Purchase_Price> 54.45</Purchase_Price>
</cdplayer>
</Purchase>
</soap-env:Body>
</soap-env:Envelope>
The entire sample application is packaged in the downloadable war
file invoicing.war.
In order to download this in Netscape Navigator[tm] and
Microsoft Internet Explorer, press shift
and click the link. The war file was deployed and run on TOMCAT 4.0
using JAXM version 1.0.1-ea1 and JAXP1.2 used in the Java XML Pack
Winter 01. Follow the instructions in the XML pack on using JAXM with
TOMCAT. As a word of warning this application will not work with JAXM
version 1.0 because any MIME headers set for the attachments were
lost during the transportation of the entire message. This article
was aimed to illustrate how to add existing xml files as part of a
SOAP message, and how to create one DOM representation of the
SOAPMessage and the attached XML file.