Working with SOAP Documents





Working with SOAP Documents

SAAJ provides a number of interfaces you can use to construct a simple SOAP document. This section covers most of these types; the SOAP fault types are covered in Section 13.4. Figure is an inheritance class diagram that shows all the SOAP document elements. The gray types are fault types, which are covered later.

Inheritance Diagram of SAAJ SOAP Types

graphics/13fig02.gif

A SOAP message or document is an XML instance composed of elements and attributes. As a convenience each of the major parts of a SOAP document has a corresponding type in SAAJ. The Envelope is represented by SOAPEnvelope, the Header is represented by SOAPHeader, the Body by SOAPBody, and so on. The SOAPElement type is used for application-specific elements that don't belong to the SOAP 1.1 namespace. Figure shows the correlation between SOAP elements and SAAJ types.

Comparing the SAAJ Type System to a SOAP Document

graphics/13fig03.gif

13.3.1 The SOAPPart and SOAPEnvelope Types

When working with SwA messages you will frequently make use of the SOAPPart and SOAPEnvelope types. The SOAPPart represents the root MIME part of an SwA message, which is always the SOAP XML document. You access the SOAPPart of an SwA message by invoking the SOAPMessage.getSOAPPart() method. Because the BP doesn't support SwA, you may never need to access the SOAPPart, because you may never use SwA. That said, Appendix F: SAAJ Attachments provides a detailed discussion of the SOAPPart, in case you ever need to transmit or process SwA messages.

You can obtain a reference to the SOAPEnvelope by invoking the getEnvelope() method of a SOAPPart. The SOAPEnvelope represents the root of the XML SOAP document. It includes methods for accessing or creating the SOAPHeader and SOAPBody. Unless you're working with attachments, you won't usually need to deal with the SOAPEnvelope type, because SAAJ constructs the SOAPEnvelope automatically when you create a new SOAPMessage object. In addition, you can access the SOAPBody and SOAPHeader types directly from the SOAPMessage object.

13.3.2 The SOAPFactory Class and Name Types

The SOAPFactory provides two factory methods for creating Name type objects. As you already know from Section 2.2: XML Namespaces, the name of an element or attribute dictates which XML namespace it belongs to. The Name type is simply an abstraction of an XML qualified name. For example, in Listing 13-6 SaajExample_3 shows how the SOAPFactory is used to create the getBookPrice and isbn elements in the BookPrice SOAP message.

Listing 13-6 Creating and Using Name Objects
package com.jwsbook.saaj;
import javax.xml.soap.*;

public class SaajExample_3 {
  public static void main(String [] args)
  throws SOAPException, java.io.IOException{

    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPFactory soapFactory = SOAPFactory.newInstance();

    Name getBookPrice_Name = soapFactory.createName("getBookPrice","mh",
                      "http://www.Monson-Haefel.com/jwsbook/BookQuote");
    Name isbnName = soapFactory.createName("isbn");

    SOAPBody body = message.getSOAPBody();
    SOAPBodyElement getBookPrice_Element =
                    body.addBodyElement(getBookPrice_Name);
    getBookPrice_Element.addChildElement( isbnName );

    SaajOutputter.writeToScreen(message);
  }
}

The following code shows the output from SaajExample_3: a SOAP document that contains the XML names corresponding to ones created using the SOAPFactory object.

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
   <soap:Body>
      <mh:getBookPrice>
          <isbn/>
      </mh:getBookPrice>
   </soap:Body>
</soap:Envelope>

The preceding SOAP message is not complete (it's missing a value for the isbn element), but it does demonstrate how you can add new elements via Name objects created by the SOAPFactory. There is another way to add new elements without having to create Name objects first. This technique was demonstrated in SaajExample_1, and I'll expand on it when we talk about the SOAPElement methods.

Remember that a Name type represents an XML qualified name. The SOAP Envelope or the SOAPFactory can create Name objects—the SOAPFactory is a general-purpose factory class. When a Name object is created, it's assigned either a local name or a fully qualified name that includes the local name, a prefix, and a URI identifying the XML namespace. Listing 13-7 shows the definition of the Name interface, which declares four methods for obtaining various parts of the XML name as String values.

Listing 13-7 The javax.xml.soap.Name Interface
package javax.xml.soap;

public interface Name {

   public String getLocalName();
   public String getPrefix();
   public String getQualifedName();
   public String getURI();

}

Each Name object is associated with an element or attribute. For example, in Listing 13-8 the getBookPrice element declares a local name, prefix, for an XML namespace.

-8 XML Namespaces in a SOAP Document
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
   <soap:Body>
      <mh:getBookPrice>
          <isbn/>
      </mh:getBookPrice>
   </soap:Body>
</soap:Envelope>

Figure Name Properties for the getBookPrice Element in Listing 13-8

Method

Return Value

getLocalName()

"getBookPrice"

getPrefix()

"mh"

getQualifiedName()

"mh:getBookPrice"

getURI()

"http://www.Monson-Haefel.com/jwsbook/BookQuote"

The String values returned by the Name object associated with the getBookPrice element in Listing 13-8 are shown in Figure.

While the Name object obtained from the getBookPrice element has values for each accessor, the Name object for the isbn element would contain only a local name and qualified name of "isbn"—it would not contain a prefix or a URI. In other words, the Name object is a very literal representation of the XML name used in the corresponding SOAP document; it is not derived and will not contain inherited namespaces or prefixes. In the absence of a prefix, the qualified name is equal to the local name. Figure shows the String values that will be returned when invoking the methods of the Name object that represents the isbn element.

Figure Name Properties for the isbn Element in Listing 13-8

Method

Return Value

getLocalName()

"isbn"

getPrefix()

null

getQualifiedName()

"isbn"

getURI()

null

13.3.3 The SOAPElement Type

The application-specific elements, those that are not part of the SOAP 1.1 XML namespace, are represented directly by objects of the SOAPElement type. This type can represent any XML element. It contains methods for accessing the child elements, attributes, namespace information, and so on. Just as an XML element may contain other XML elements, a SOAPElement may contain other SOAPElement objects. The SOAPElement type models a hierarchical structure that corresponds to the hierarchical structure of XML. As you saw in Figure, the SOAPElement type is the supertype of the other SOAP types, including SOAPEnvelope, SOAPBody, SOAP BodyElement, SOAPHeader, SOAPHeaderElement, and the fault elements, which are covered later. The Node type is the supertype of SOAPElement.

As the supertype of all other SOAP element types, SOAPElement contains a number of useful methods you can use to create and access child elements, attributes, namespace declarations, the element name, and the encoding style. Listing 13-9 shows the definition of the SOAPElement type.

Listing 13-9 The javax.xml.soap.SOAPElement Interface
package javax.xml.soap;
import java.util.Iterator;

public interface SOAPElement extends Node, org.w3c.dom.Element {
    public SOAPElement addAttribute(Name name, String value)
      throws SOAPException;
    public SOAPElement addChildElement(Name name) throws SOAPException;
    public SOAPElement addChildElement(SOAPElement element)
      throws SOAPException;
    public SOAPElement addChildElement(String localName) throws SOAPException;
    public SOAPElement addChildElement(String localName, String prefix)
      throws SOAPException;
    public SOAPElement addChildElement(String localName, String prefix,
                                       String uri) throws SOAPException;
    public SOAPElement addNamespaceDeclaration(String prefix, String uri)
      throws SOAPException;
    public SOAPElement addTextNode(String text);
    public Iterator getAllAttributes();
    public String getAttributeValue(Name name);
    public Iterator getChildElements();
    public Iterator getChildElements(Name name);
    public Name getElementName();
    public String getEncodingStyle();
    public Iterator getNamespacePrefixes();
    public String getNamespaceURI(String prefix);
    public Iterator getVisableNamespacePrefixes();
    public boolean removeAttribute(Name name);
    public boolean removeNamespaceDeclaration(String prefix);
    public boolean removeContents();
    public void setEncodingStyle(String encodingStyle);
}

Most of the methods defined in SOAPElement are self-explanatory and won't be covered in any detail here. The SAAJ API provides concise definitions of each method, in case the behavior is not obvious to you. In Listing 13-10, SaajExample_4 shows a SOAP message being created by an initial sender. SaajExample_4 makes extensive use of the SOAPElement interface and methods.

Listing 13-10 Using the SOAPElement Type
package com.jwsbook.saaj;
import javax.xml.soap.*;

public class SaajExample_4 {
  public static void main(String [] args) throws SOAPException {

    // Create SOAPMessage
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPElement header = message.getSOAPHeader();

    // Create message-id header block
    SOAPElement msgIdHeader = (SOAPElement)
      header.addChildElement("message-id","mi",
                           "http://www.Monson-Haefel.com/jwsbook/message-id");
    String uuid = new java.rmi.dgc.VMID().toString();
    msgIdHeader.addTextNode(uuid);


    // Create processed-by header block
    SOAPElement prcssdByHeader = (SOAPElement)
      header.addChildElement("processed-by","proc",
                         "http://www.Monson-Haefel.com/jwsbook/processed-by");
    SOAPElement node = prcssdByHeader.addChildElement("node");
    SOAPElement time = node.addChildElement("time-in-millis");
    long millis = System.currentTimeMillis();
    time.addTextNode(String.valueOf(millis));
    SOAPElement identity = node.addChildElement("identity");
    identity.addTextNode("SaajExample_4");

    // Create getBookPrice RPC call
    SOAPElement body = message.getSOAPBody();
    SOAPElement getBookPrice = body.addChildElement("getBookPrice","mh",
                            "http://www.Monson-Haefel.com/jwsbook/BookQuote");
    SOAPElement isbn = getBookPrice.addChildElement("isbn");
    isbn.addTextNode("0321146182");

    SaajOutputter.writeToScreen(message);
  }
}

Although SAAJ provides special types for SOAP Header, Body, and header-block elements, it's frequently more convenient to use the SOAPElement supertype. SaajExample_4 provides an example: It creates the header blocks using SOAP Element.addChildElement() instead of SOAPHeader.addHeaderElement() because it's easier (you don't have to create a Name object).

// Create message-id header block
SOAPElement msgIdHeader = (SOAPElement)
  header.addChildElement("message-id","mi",
                       "http://www.Monson-Haefel.com/jwsbook/message-id");
...

// Create processed-by header block
SOAPElement prcssdByHeader = (SOAPElement)
  header.addChildElement("processed-by","proc",
                       "http://www.Monson-Haefel.com/jwsbook/processed-by");

Naturally, the more familiar you are with the methods of SOAPElement and its derived types, the easier and more efficient your code becomes.

It's important to note that each of the addChildElement() methods returns a SOAPElement. If you use the overloading of addChildElement() that expects another SOAPElement, the instance returned may not be the same one you passed into the method. Be careful not to use the original reference after you pass it to addChildElement(); modifying it may not have the desired effect. For example:

SOAPElement child = ... // get SOAPElement from somewhere
element.addChildElement( child );
child.addAttribute( attribName, attribValue );

This code may not actually add the attribute to the child as you might expect, because addChildElement() may have copied the child when adding it to the parent element, and returned the copy rather than the original. You should always use the SOAPElement returned by the addChildElement() method if you need to modify a SOAPElement object after you add it, thus:

SOAPElement child = // get SOAPElement from somewhere
child = element.addChildElement( child );
child.addAttribute( attribName, attribValue );

13.3.4 The Node Type

The supertype of the SOAPElement type, and therefore all of its subtypes, is Node (see Figure). The Node interface provides a few useful methods for navigating through a hierarchical tree of elements, removing nodes from the tree, and marking nodes for "recycling." Listing 13-11 shows the interface definition for the Node type.

Listing 13-11 The javax.xml.soap.Node Interface
package javax.xml.soap;

public interface Node extends org.w3c.dom.Node {
    public void detachNode();
    public SOAPElement getParentElement()
      throws java.lang.UnsupportedOperationException;
    public String getValue();
    public void setValue(String value)
      throws java.lang.IllegalStateException;
    public void recycleNode();
    public void setParentElement(SOAPElement parent)
      throws SOAPException;
}

The detachNode() method is frequently used to remove the SOAPHeader object from a newly created SOAP message. When a SOAPMessage is first created, it automatically contains Envelope, Header, and Body elements. If the SOAP message you are constructing will not be using any header blocks, then removing the Header element is a good idea. You may have noticed that SaajExample_1 (Listing 13-2) used detachNode() for this purpose:

message.getSOAPHeader().detachNode();

The recycleNode() method is a bit odd. It's supposed to help the underlying SAAJ implementation conserve resources—no harm in that, but with recent improvements in JVM garbage collection it hardly seems necessary. It's difficult to determine the overhead of using this method compared to simply dereferencing a node to release it for garage collection, but you may want to err on the side of resource conservation, and call recycleNode() if you detach a node you won't be using further. The following snippet illustrates.

SOAPHeader header = message.getSOAPHeader();
header.detachNode();
header.recycleNode();

The behavior of the setValue() method depends on the type of Node. If it's a Text type, setValue() assigns the String argument to be the value of the text. If setValue() is invoked on an Element, then it will assign the String argment to the Text node contained by the Element. If the Element object has no children, the setValue() method will create a child Text node. If, however, the Element object has another Element object as its child, or multiple children of any type, then setValue() will throw a java.lang.IllegalStateException.

13.3.5 The SOAPHeader Type

In Chapter 4 you learned that the SOAP Header element may have zero or more header blocks. In SAAJ, the SOAPHeader type represents the Header element, and the SOAPHeaderElement type represents an individual header block. SOAPHeader provides methods for adding, examining, and removing SOAPHeaderElement objects—effectively adding, examining, or removing header blocks from the SOAP document. For example, we can insert the message-id header block into a SOAP message as shown in bold in Listing 13-12.

-12 Adding a Header Block to a SOAP Message
package com.jwsbook.saaj;
import javax.xml.soap.*;

public class SaajExample_5 {
  public static void main(String [] args) throws SOAPException {
    // Create SOAPMessage
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPHeader header = message.getSOAPHeader();

    // Create message-id header block
    SOAPHeaderElement msgId = (SOAPHeaderElement)
      header.addChildElement("message-id","mi",
                           "http://www.Monson-Haefel.com/jwsbook/message-id");
    String uuid = new java.rmi.dgc.VMID().toString();
    msgId.addTextNode(uuid);
    msgId.setActor("http://www.Monson-Haefel.com/logger");
    msgId.setMustUnderstand(false);


    // Create getBookPrice RPC call
    SOAPBody body = message.getSOAPBody();
    SOAPElement getBookPrice = body.addChildElement(
                            "getBookPrice",
                            "mh",
                            "http://www.Monson-Haefel.com/jwsbook/BookQuote");
    SOAPElement isbn = getBookPrice.addChildElement("isbn");
    isbn.addTextNode("0321146182");
    SaajOutputter.writeToScreen(message);
    }
}

SOAPHeader.addChildElement() adds the root element of the header block, in this case the message-id element. The header block should be assigned a namespace and possibly an actor. Here we have assigned it both a namespace, "http://www.Monson-Haefel.com/jwsbook/message-id", and an actor, "http://www.Monson-Haefel.com/logger". As you learned in Chapter 4, the actor attribute specifies a role that should process the header block. The call to java.rmi.dgc.VMID() is a simple hack to obtain a unique ID.

There are multiple addChildElement() methods. If you pass a SOAPHeaderElement parameter, you can add the whole header block, not just its root. The addChildElement() method is defined by the SOAPElement class, which is the supertype of SOAPHeader. The SOAPElement is discussed in more detail in Section 13.3.3.

The output of SaajExample_5 would look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
  <soap:Header>
    <mi:message-id xmlns:mi="http://www.Monson-Haefel.com/jwsbook/message-id"
     soap:actor="http://www.Monson-Haefel.com/logger"
     soap:mustUnderstand="0">
       11d1def534ea1be0:b1c5fa:f3bfb4dcd7:-8000
    </mi:message-id>
  </soap:Header>
  <soap:Body>
    <mh:getBookPrice>
      <isbn>0321146182</isbn>
    </mh:getBookPrice>
  </soap:Body>
</soap:Envelope>

In addition to adding header blocks, the SOAPHeader type allows us to examine and remove specific header blocks. SOAP receivers use this functionality to access all header blocks, header blocks associated with a particular actor, or only those header blocks with mustUnderstand equal to true for a particular actor. The SOAPHeader class provides five methods for examining or extracting (accessing or removing) header blocks. These methods are shown in bold in the Listing 13-13, which is the definition of the SOAPHeader class.

Listing 13-13 The javax.xml.SOAPHeader Interface
package javax.xml.soap;
import java.util.Iterator;

public interface SOAPHeader extends SOAPElement {
    public SOAPHeaderElement addHeaderElement( Name name )
    throws SOAPException;
    public Iterator extractHeaderElements(String actor);
    public Iterator examineHeaderElements(String actor);
    public Iterator examineMustUnderstandHeaderElements(String actor);
    public Iterator examinAllHeaderElements();
    public Iterator extractAllHeaderElements();
}

All of these methods return a java.util.Iterator whose elements are SOAPHeaderElement objects. For example, the following SOAP message contains two header blocks (message-id and processed-by), each with a different actor attribute.

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Header>
      <mi:message-id soap:actor="http://www.Monson-Haefel.com/logger"
         xmlns:mi="http://www.Monson-Haefel.com/jwsbook/message-id">
           11d1def534ea1be0:b1c5fa:f3bfb4dcd7:-8000
      </mi:message-id>
      <proc:processed-by
         soap:actor="http://schemas.xmlsoap.org/soap/actor/next"
         xmlns:proc="http://www.Monson-Haefel.com/jwsbook/processed-by">
         <node>
            <time-in-millis>1013694684723</time-in-millis>
            <identity>http://local/SOAPClient2</identity>
         </node>
      </proc:processed-by>
   </soap:Header>
   <soap:Body>
       <!-- application-specific data goes here -->
  </soap:Body>
</soap:Envelope>

Upon receiving this SOAP message, a SOAP node will request all header blocks that are associated with the standard next actor so that those headers can be processed. The following snippet shows code for extracting and processing SOAPHeaderElement objects associated with the next actor role.

SOAPHeader header = message.getSOAPHeader();
String actor = "http://schemas.xmlsoap.org/soap/actor/next";
Iterator headerBlocks = header.extractHeaderElements( actor );
while(headerBlocks.hasNext()){
    SOAPHeaderElement block = (SOAPHeaderElement)headerBlocks.next();
    if(block.getElementName().getLocalName().equals("processed-by")){
            SOAPElement node = block.addChildElement("node");

            // do something useful with header blocks and then discard them
    }
}

Obviously, the ability to examine, modify, and remove header blocks is a very important feature of SAAJ, one receivers (such as JAX-RPC handlers) will use extensively as they process incoming messages. As you learned in Chapter 4, intermediaries are required to remove any header block targeted to a role they play. The extractHeaderElements() method enables a receiver to fulfill that obligation in one operation. Chapter 4 also mentioned, though, that some receivers will feign removal and insertion of the same header block, by simply modifying it. In such cases the receiver invokes examineHeaderElements() instead of extract HeaderElements(). This method allows the node to search for and access header blocks easily, without removing them.

13.3.6 The SOAPHeaderElement Type

SOAPHeaderElement is an abstraction of a header block that lets us create and examine the attributes and child elements of a particular header block. Each header block may have an actor attribute, a mustUnderstand attribute, or both, in addition to child elements and other attributes. The definition of the SOAPHeaderElement is shown in Listing 13-14 (its methods for accessing child elements and other attributes are defined in its supertype, SOAPElement).

Listing 13-14 The javax.xml.soap.SOAPHeaderElement Interface
package javax.xml.soap;

public interface SOAPHeaderElement extends SOAPElement {
    public String getActor();
    public boolean getMustUnderstand();
    public void setActor(String actorURI);
    public void setMustUnderstand(boolean flag);
}

The SOAPHeaderElement methods are employed when modifying and examining header blocks. For example, when creating the message-id header block, we use these methods to set the value of the actor and mustUnderstand attributes, as shown in the following snippet from SaajExample_5 (Listing 13-12).

// Create message-id header block
SOAPHeaderElement msgId = (SOAPHeaderElement)
  header.addChildElement("message-id","mi",
                         "http://www.Monson-Haefel.com/jwsbook/message-id");
String uuid = new java.rmi.dgc.VMID().toString();
msgId.addTextNode(uuid);
msgId.setActor("http://www.Monson-Haefel.com/logger");
msgId.setMustUnderstand(false);

In addition to the actor and mustUnderstand attributes, a SOAPHeader Element may also contain one or more SOAPElement objects, which represent the child elements of the header block.

13.3.7 The SOAPBody Type

As its names suggests, the SOAPBody type represents the SOAP Body element. Of its four methods, three deal with SOAP faults and one with the Body of a non-fault SOAP message. Listing 13-15 shows the definition of the SOAPBody interface.

Listing 13-15 The javax.xml.soap.SOAPBody Interface
package javax.xml.soap;

public interface SOAPBody extends SOAPElement {
  public SOAPBodyElement addBodyElement(Name name) throws SOAPException;
  public SOAPBodyElement addDocument(org.w3c.dom.Document doc)
    throws SOAPException;
  public SOAPFault addFault() throws SOAPException;
  public SOAPFault addFault(Name faultcode, String faultString,
                            java.util.Locale local) throws SOAPException;
  public SOAPFault addFault(Name faultcode, String faultString)
    throws SOAPException;
  public SOAPFault getFault();
  public boolean hasFault();
}

The SOAPBody type is used in several of the earlier examples. The following snippet from SaajExample_3 (Listing 13-6) shows how SAAJ can be used to create a SOAP body with contents.

Name getBookPrice_Name = soapFactory.createName("getBookPrice","mh",
                         "http://www.Monson-Haefel.com/jwsbook/BookQuote");
Name isbnName = soapFactory.createName("isbn");

SOAPBody body = message.getSOAPBody();
SOAPBodyElement getBookPrice_Element =
                body.addBodyElement(getBookPrice_Name);
getBookPrice_Element.addChildElement( isbnName );

The addFault(), getFault(), and hasFault() methods are discussed in Section 13.4: Working with SOAP Faults. The addDocument() method is discussed in Section 13.6: SAAJ 1.2 and DOM 2.

13.3.8 The SOAPBodyElement Type

SOAPBodyElement extends SOAPElement and doesn't add any methods of its own, as you see in Listing 13-16.

Listing 13-16 The javax.xml.soap.SOAPBodyElement Interface
package javax.xml.soap;
public interface SOAPBodyElement extends SOAPElement {}

Although it may seem a bit silly to define a SOAPBodyElement type that adds no methods—SOAPElement would seem to suffice—this empty type may be beneficial in the future. As the SOAP protocol evolves, the SOAPBodyElement will already be present as a construct for adding new functionality (methods), without breaking backward-compatibility of existing code. In addition, the SOAPBodyElement type, which is also the base type of the SOAPFault, suggests type safety by restricting the type of SOAPElement object a SOAPBody can contain.

A SOAPBodyElement can be added to a SOAPBody object using a Name object, as shown in the following snippet from SaajExample_3.

Name getBookPrice_Name = soapFactory.createName("getBookPrice","mh",
                         "http://www.Monson-Haefel.com/jwsbook/BookQuote");
...
SOAPBodyElement getBookPrice_Element =
                body.addBodyElement(getBookPrice_Name);

13.3.9 The Text Type

The Text type is an extension of Node that represents literal text contained by an element or a comment. The interface definition of Text requires familiarity with Node to be useful, because the subtype adds only a single method, isComment(), as shown in Listing 13-17.

Listing 13-17 The javax.xml.soap.Text Interface
package javax.xml.soap;

public interface Text extends Node, org.w3c.dom.Text {
    public boolean isComment();
}

You have to use the methods getValue() and setValue() defined by the Node supertype to retrieve and set the contents of a Text object. To access a Text object from an element use Node.getValue(), which will return a String value if the element contains a Text node, or null if it doesn't. For example, we could retrieve the ISBN number from the isbn element as follows:

SOAPElement isbn = getBookPrice.addChildElement("isbn");
...
isbn.addTextNode("0321146182");
...
Text textValue = isbn.getValue();

For some reason the addTextNode() method returns a SOAPElement rather than the Text node itself. I suspect this was done to aid in changing calls, but its effectiveness is debatable.

There is no obvious way to add a comment to a SOAP message without reverting to the DOM object model. Of course, SOAP messages are not meant for human consumption, so this lack is not a big problem.

13.3.10 The SOAPConstants Class

Certain XML namespaces will not change and will be used over and over again. The values of these namespaces are assigned to constants as a convenience for the developer. Specifically, the SOAPConstants class defines constants for the SOAP 1.1 namespace, the namespace of the standard next actor attribute value, and the namespace of standard SOAP encoding. Figure shows the SOAPConstants fields and their values.

Namespace Constants

SOAPConstants Field Name

XML Namespace String Value

SOAPConstants.URI_NS_SOAP_ENCODING

"http://schemas.xmlsoap.org/soap/encoding/"

SOAPConstants.URI_NS_SOAP_ENVELOPE

"http://schemas.xmlsoap.org/soap/envelope/"

SOAPConstants.URI_SOAP_ACTOR_NEXT

"http://schemas.xmlsoap.org/soap/actor/next"

You can see how SOAPConstants is used in some of the examples in this chapter; for example, SaajExample_4 uses the constant for the next SOAP actor, as shown in this code snippet:

prcssdBy.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
prcssdBy.setMustUnderstand(true);

The URI_NS_SOAP_ENCODING constant is used to set the encoding style to RPC/Encoding. As I noted in Part II, the BP doesn't support this messaging mode, so you should avoid it.

13.3.11 The SOAPException Class

SOAPException is used to report errors encountered by the SOAP toolkit while attempting to perform some operation. Many of the methods that manufacture objects throw this exception because they affect the structure of the SOAP message. Remember: A SOAPException does not represent a SOAP fault generated by the receiver. A SOAP fault will always be received as a SOAPMessage (see Section 13.4).

A SOAPException may contain an embedded exception if the error was caused by a subsystem like I/O or an XML parser. It may also contain a reason message. A skeletal definition of SOAPException is shown in Listing 13-18 (the method bodies have been omitted for brevity).

Listing 13-18 The javax.xml.soap.SOAPException Class
package javax.xml.soap;

public class SOAPException extends Exception {

    public SOAPException(){...}
    public SOAPException(String reason){...}
    public SOAPException(String reason, Throwable cause){...}
    public SOAPException(Throwable cause){...}

    public Throwable getCause(){...}
    public getMessage(){...}
    public Throwable initCause(Throwable cause){...}
}

13.3.12 The SOAPFactory and SOAPElement Types

The SOAPFactory class is provided for the developer's convenience. It's a nice thing to have around because it allows you to create a SOAPElement independent of context. In other words, you can use it to create detached instances of the SOAPElement type. Listing 13-19 shows the definition of SOAPFactory (the method bodies have been omitted for brevity).

Listing 13-19 The javax.xml.soap.SOAPFactory Class
package javax.xml.soap;

public abstract class SOAPFactory {

    
    public abstract  SOAPElement createElement(Name name)
      throws SOAPException{…}
    public abstract  SOAPElement createElement(String localName)
      throws SOAPException{…}
    public abstract  SOAPElement createElement(String localName,
                                               String prefix, String uri)
       throws SOAPException{…}
    
}

SOAPFactory can be used to construct portions of a SOAP message independent of a SOAPMessage object. For example, you might use it to construct a specialized header block in one module, which can be added to a SOAP document in some other module. As a demonstration, in Listing 13-20 SaajExample_6 creates a SOAP message but uses a separate class, the MessageIDHeader class, to construct the message-id header block.

Listing 13-20 Delegating Header-Block Creation to a MessageIDHeader
package com.jwsbook.saaj;
import javax.xml.soap.*;

public class SaajExample_6 {
  public static void main(String [] args) throws SOAPException {

    // Create SOAPMessage
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPHeader header = message.getSOAPHeader();

    // Create message-id header block
    SOAPElement msgId =  MessageIDHeader.createHeaderBlock();
    SOAPHeaderElement msgId_header =
             (SOAPHeaderElement)header.addChildElement(msgId);
    msgId_header.setActor("http://www.Monson-Haefel.com/logger");

    SaajOutputter.writeToScreen(message);
  }
}

The MessageIDHeader class in Listing 13-21 uses the SOAPFactory to create an isolated header block that can be generated for any SOAP application that needs it.

Listing 13-21 Using SOAPFactory to Create a Header Block
package com.jwsbook.saaj;
import javax.xml.soap.*;

public class MessageIDHeader {
  public static SOAPElement createHeaderBlock() throws SOAPException{

    SOAPFactory factory = SOAPFactory.newInstance();
    SOAPElement msgId = factory.createElement("message-id","mi",
                        "http://www.Monson-Haefel.com/jwsbook/message-id");

    String messageid = new java.rmi.dgc.VMID().toString();

    msgId.addTextNode( messageid );
    return msgId;
  }
}

The java.rmi.dgc.VMID does a fairly good job of generating unique identifiers. It's convenient because it's found in the core libraries, but it's not perfect and is not recommended for use in production systems.

The output from SaajExample_6 would look something like the following (the text value of message-id would differ):

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <mi:message-id xmlns:mi="http://www.Monson-Haefel.com/jwsbook/message-id"
                  soap:actor="http://www.Monson-Haefel.com/logger">
        11d1def534ea1be0:194a4e:f3c05ce67a:-8000
    </mi:message-id>
  </soap:Header>
  <soap:Body/>
</soap:Envelope>

SOAPFactory makes it fairly easy to modularize the construction of SOAP messages, especially SOAP headers, which are specialized and often used across a variety of SOAP applications. For example, at Monson-Haefel Books almost every Web service requires that a message-id header block be included for logging purposes. A class like MessageIDHeader can be reused throughout the system to generate the message-id header block.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows