SCDJWS Study Guide: SAAJ


Printer-friendly version Printer-friendly version | Send this 
article to a friend Mail this to a friend


Previous Next vertical dots separating previous/next from contents/index/pdf Contents

Maniplate SOAP Message with SAAJ



Manipulating Message Content Using SAAJ or DOM APIs


Because SAAJ nodes and elements implement the DOM Node and Element interfaces, you have many options for adding or changing message content:

  • Use only DOM APIs
  • Use only SAAJ APIs
  • Use SAAJ APIs and then switch to using DOM APIs
  • Use DOM APIs and then switch to using SAAJ APIs

The first three of these cause no problems. Once you have created a message, whether or not you have imported its content from another document, you can start adding or changing nodes using either SAAJ or DOM APIs.

But if you use DOM APIs and then switch to using SAAJ APIs to manipulate the document, any references to objects within the tree that were obtained using DOM APIs are no longer valid. If you must use SAAJ APIs after using DOM APIs, you should set all of your DOM typed references to null, because they can become invalid. For more information about the exact cases in which references become invalid, see the SAAJ API documentation.

The basic rule is that you can continue manipulating the message content using SAAJ APIs as long as you want to, but once you start manipulating it using DOM, you should not use SAAJ APIs after that.

How to Add Content to the SOAPHeader Object?

To add content to the header, you need to create a SOAPHeaderElement object. As with all new elements, it must have an associated Name object, which you can create using the message's SOAPEnvelope object or a SOAPFactory object.

For example, suppose you want to add a conformance claim header to the message to state that your message conforms to the WS-I Basic Profile 1.0.

The following code fragment retrieves the SOAPHeader object from message and adds a new SOAPHeaderElement object to it. This SOAPHeaderElement object contains the correct qualified name and attribute for a WS-I conformance claim header.

SOAPHeader header = message.getSOAPHeader();
 
Name headerName = soapFactory.createName("Claim",
             "wsi", "http://ws-i.org/schemas/conformanceClaim/");
 
SOAPHeaderElement headerElement = header.addHeaderElement(headerName);
 
Name conformsToName = soapFactory.createName("conformsTo");
 
headerElement.addAttribute(conformsToName,"http://ws-i.org/profiles/basic1.0/");

At this point, header contains the SOAPHeaderElement object headerElement identified by the Name object headerName. Note that the addHeaderElement method both creates headerElement and adds it to header.

A conformance claim header has no content.

This code produces the following XML header:

<SOAP-ENV:Header>
  <wsi:Claim conformsTo="http://ws-i.org/profiles/basic1.0/"
   xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/"/>
</SOAP-ENV:Header>

For more information about creating SOAP messages that conform to WS-I Basic Profile 1.0, see the Messaging section of the WS-I Basic Profile 1.0.

For a different kind of header, you might want to add content to headerElement. The following line of code uses the method addTextNode to do this.

headerElement.addTextNode("order");

Now you have the SOAPHeader object header that contains a SOAPHeaderElement object whose content is "order".

How to Add Content to the SOAPBody Object?

The process for adding content to the SOAPBody object is the same as the process for adding content to the SOAPHeader object. You access the SOAPBody object, add a SOAPBodyElement object to it, and add text to the SOAPBodyElement object. It is possible to add additional SOAPBodyElement objects, and it is possible to add subelements to the SOAPBodyElement objects with the method addChildElement. For each element or child element, you add content with the method addTextNode.

An example shows adding multiple SOAPElement objects and adding text to each of them can be found in Populating SOAP Message sub-section in the previous section.br>

How to Add Content to the SOAPPart Object?

If the content you want to send is in a file, SAAJ provides an easy way to add it directly to the SOAPPart object. This means that you do not access the SOAPBody object and build the XML content yourself, as you did in the previous section.

To add a file directly to the SOAPPart object, you use a javax.xml.transform.Source object from JAXP (the Java API for XML Processing). There are three types of Source objects: SAXSource, DOMSource, and StreamSource. A StreamSource object holds content as an XML document. SAXSource and DOMSource objects hold content along with the instructions for transforming the content into an XML document.

The following code fragment uses the JAXP API to build a DOMSource object that is passed to the SOAPPart.setContent method. The first three lines of code get a DocumentBuilderFactory object and use it to create the DocumentBuilder object builder. Because SOAP messages use namespaces, you should set the NamespaceAware property for the factory to true. Then builder parses the content file to produce a Document object.

DocumentBuilderFactory dbFactory =  DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder builder = dbFactory.newDocumentBuilder();
Document document =  builder.parse("file:///music/order/soap.xml");
DOMSource domSource = new DOMSource(document); 

The following two lines of code access the SOAPPart object (using the SOAPMessage object message) and set the new Document object as its content. The method SOAPPart.setContent not only sets content for the SOAPBody object but also sets the appropriate header for the SOAPHeader object.

SOAPPart soapPart= message.getSOAPPart();
soapPart.setContent(domSource);

The XML file you use to set the content of the SOAPPart object must include Envelope and Body elements, like this:

<SOAP-ENV:Envelope
      xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    ...
 
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

You will see other ways to add content to a message in the sections Adding a Document to the SOAP Body and Adding Attachments.

How to Add a Document to the SOAP Body?

In addition to setting the content of the entire SOAP message to that of a DOMSource object, you can add a DOM document directly to the body of the message. This capability means that you do not have to create a javax.xml.transform.Source object. After you parse the document, you can add it directly to the message body:

SOAPBody body = message.getSOAPBody();
SOAPBodyElement docElement = body.addDocument(document);

How to Add Attributes?

An XML element may have one or more attributes that give information about that element. An attribute consists of a name for the attribute followed immediately by an equals sign (=) and its value.

The SOAPElement interface provides methods for adding an attribute, for getting the value of an attribute, and for removing an attribute.

import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPBody;
import java.net.URL;

public SimpleSAAJ {

    public static void main(String args[]) {

       try {   
           //Create a SOAPConnection   
           SOAPConnectionFactory factory =
                    SOAPConnectionFactory.newInstance();

           SOAPConnection connection =
                    factory.createConnection(); 
 
           //Create a SOAPMessage
           SOAPMessageFactory messageFactory = MessageFactory.newInstance();
           SOAPMessage message = messageFactory.createMessage();
           SOAPPart  soapPart = messge.getSOAPPart();
           SOAPEnvelope envelope = soapPart.getEnvelope();
           SOAPHeader header = envelope.getHeader();
           SOAPBody body = envelope.getBody();
           header.detachNode();
 
           //Create a SOAPBodyElement
           Name bodyName = envelope.createName("GetLastTradePrice"
                              "m", "http://wombat.ztrade.com");
           SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
           //Insert Content
           Name name = envelope.createName("symbol");
           SOAPElement symbol = bodyElement.addChildElement(name);
           symbol.addTextNode("SUNW");
           //Add countryCode attribute to the <symbol> element
           Name attributeName = envelope.createName("countryCode");
           symbol.addAttribute(attributeName, "US");

               .................
  
           // Close the SOAPConnection
           connection.close();

       } catch (Exception e) {
           System.out.println(e.getMessage());
       }
  }
}

The above code fragment produces the following SOAP message:

<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <m:GetLastTradePrice xmlns:m="http://wombat.ztrade.com">
       <symbol>SUNW</symbol>
    </m:GetLastTradePrice>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The symbol is a SOAPElement object rather than a SOAPBodyElement object or SOAPHeaderElement object; it is legal for its Name object to contain only a local name.

The following line of code retrieves the value of the attribute whose name is countryCode.

String attributeValue = 
person.getAttributeValue(attributeName);

If you had added two or more attributes to person, the previous line of code would have returned only the value for the given attribute named.

If you wanted to retrieve the values for all of the attributes under an element, you would use the method getAllAttributes, which returns an iterator over all of the values. The following lines of code retrieve and print out each value on a separate line until there are no more attribute values. Note that the method Iterator.next returns a Java Object, which is cast to a Name object so that it can be assigned to the Name object attributeName.

Iterator iterator = person.getAllAttributes();
while (iterator.hasNext()){
  Name attributeName = (Name) iterator.next();
 
  System.out.println("Attribute name is " +                        
                     attributeName.getQualifiedName());
 
  System.out.println("Attribute value is " +
    element.getAttributeValue(attributeName));
}

 

The following line of code removes the attribute named id from person. The variable successful will be true if the attribute was removed successfully.

boolean successful = symbol.removeAttribute(attributeName);

In this section you saw how to add, retrieve, and remove attributes. This information is general in that it applies to any element.

Header Attributes

Attributes that appear in a SOAPHeaderElement object determine how a recipient processes a message. You can think of header attributes as offering a way to extend a message, giving information about such things as authentication, transaction management, payment, and so on. A header attribute refines the meaning of the header, while the header refines the meaning of the message contained in the SOAP Body.

The SOAP 1.1 specification defines two attributes that can appear only in SOAPHeaderElement objects: actor and mustUnderstand. The next two sections discuss these attributes.

The Actor Attribute

The attribute actor is optional, but if it is used, it must appear in a SOAPHeaderElement object. Its purpose is to indicate the recipient of a header element. The default actor is the message's ultimate recipient; that is, if no actor attribute is supplied, the message goes directly to the ultimate recipient.

An actor is an application that can both receive SOAP messages and forward them to the next actor. The ability to specify one or more actors as intermediate recipients makes it possible to route a message to multiple recipients and to supply header information that applies specifically to each of the recipients.

For example, suppose that a message is an incoming purchase order. Its SOAPHeader object might have SOAPHeaderElement objects with actor attributes that route the message to applications that function as the order desk, the shipping desk, the confirmation desk, and the billing department. Each of these applications will take the appropriate action, remove the SOAPHeaderElement objects relevant to it, and send the message on to the next actor.

An actor is identified by its URI. For example, the following line of code, in which orderHeader is a SOAPHeaderElement object, sets the actor to the given URI.

orderHeader.setActor("http://gizmos.com/orders");

Additional actors may be set in their own SOAPHeaderElement objects. The following code fragment first uses the SOAPMessage object message to get its SOAPHeader object header. Then header creates four SOAPHeaderElement objects, each of which sets its actor attribute.

SOAPHeader header= message.getSOAPHeader();
SOAPFactory soapFactory = SOAPFactory.newInstance();
String nameSpace = "ns";
String nameSpaceURI = "http://gizmos.com/NSURI";
 
Name order = soapFactory.createName("orderDesk",                       
                       nameSpace, nameSpaceURI);
 
SOAPHeaderElement orderHeader = header.addHeaderElement(order);
 
orderHeader.setActor("http://gizmos.com/orders");
 
Name shipping = soapFactory.createName("shippingDesk",
                  nameSpace, nameSpaceURI);
 
SOAPHeaderElement shippingHeader = header.addHeaderElement(shipping);
shippingHeader.setActor("http://gizmos.com/shipping");
 
Name confirmation = soapFactory.createName("confirmationDesk",
                     nameSpace, nameSpaceURI);
SOAPHeaderElement confirmationHeader = header.addHeaderElement(confirmation);
confirmationHeader.setActor("http://gizmos.com/confirmations");
 
Name billing = soapFactory.createName("billingDesk",
                  nameSpace, nameSpaceURI);
SOAPHeaderElement billingHeader = header.addHeaderElement(billing);
billingHeader.setActor("http://gizmos.com/billing");

The SOAPHeader interface provides two methods that return a java.util.Iterator object over all of the SOAPHeaderElement objects with an actor that matches the specified actor. The first method, examineHeaderElements, returns an iterator over all of the elements with the specified actor.

java.util.Iterator headerElements =       
         header.examineHeaderElements("http://gizmos.com/orders");

The second method, extractHeaderElements, not only returns an iterator over all of the SOAPHeaderElement objects with the specified actor attribute but also DETACHES them from the SOAPHeader object. So, for example, after the order desk application has done its work, it would call extractHeaderElements to remove all of the SOAPHeaderElement objects that applied to it.

java.util.Iterator headerElements =
         header.extractHeaderElements("http://gizmos.com/orders");

Each SOAPHeaderElement object may have only one actor attribute, but the same actor may be an attribute for multiple SOAPHeaderElement objects.

Two additional SOAPHeader methods, examineAllHeaderElements and extractAllHeaderElements, allow you to examine or extract all the header elements, whether or not they have an actor attribute. For example, you could use the following code to display the values of all the header elements:

Iterator allHeaders =   header.examineAllHeaderElements();
while (allHeaders.hasNext()) {
    SOAPHeaderElement headerElement = (SOAPHeaderElement)allHeaders.next();
    Name headerName = headerElement.getElementName();
 
    System.out.println("\nHeader name is " +
                headerName.getQualifiedName());
    System.out.println("Actor is " +
           headerElement.getActor());
}

The mustUnderstand Attribute

The other attribute that must be added only to a SOAPHeaderElement object is mustUnderstand. This attribute says whether or not the recipient (indicated by the actor attribute) is required to process a header entry. When the value of the mustUnderstand attribute is true, the actor must understand the semantics of the header entry and must process it correctly to those semantics. If the value is false, processing the header entry is optional. A SOAPHeaderElement object with no mustUnderstand attribute is equivalent to one with a mustUnderstand attribute whose value is false.

The mustUnderstand attribute is used to call attention to the fact that the semantics in an element are different from the semantics in its parent or peer elements. This allows for robust evolution, ensuring that the change in semantics will not be silently ignored by those who may not fully understand it.

If the actor for a header that has a mustUnderstand attribute set to true cannot process the header, it must send a SOAP fault back to the sender. The actor must not change state or cause any side-effects, so that to an outside observer, it appears that the fault was sent before any header processing was done.

The following code fragment creates a SOAPHeader object with a SOAPHeaderElement object that has a mustUnderstand attribute.

SOAPHeader header = message.getSOAPHeader();
Name name = soapFactory.createName("Transaction", "t",
              "http://gizmos.com/orders");
SOAPHeaderElement transaction = header.addHeaderElement(name);
transaction.setMustUnderstand(true);
transaction.addTextNode("5");

This code produces the following XML:

<SOAP-ENV:Header>
  <t:Transaction xmlns:t="http://gizmos.com/orders" 
    SOAP-ENV:mustUnderstand="1">
  5 
  </t:Transaction>
</SOAP-ENV:Header>

You can use the getMustUnderstand method to retrieve the value of the MustUnderstand attribute. For example, you could add the following to the code fragment at the end of the previous section:

System.out.println(“MustUnderstand is “ +
            headerElement.getMustUnderstand());

Note: Although the SAAJ API provides the API for adding these attributes, it does not supply the API for processing them. For example, the actor attribute requires that there be an implementation such as a messaging provider service to route the message from one actor to the next.



Previous Next vertical dots separating previous/next from contents/index/pdf Contents

  |   |