Chapter 13. SAAJ

To read and manipulate SOAP header blocks in JAX-RPC you have to use message handlers, which are covered in Chapter 14. Message handlers, however, use SOAP with Attachments API for Java (SAAJ), version 1.2, to represent an XML SOAP message. Therefore, you need to understand how to use SAAJ before you can learn how to use message handlers. It's the purpose of this chapter to teach you how to create, read, and manipulate SOAP messages using SAAJ. While SAAJ is central to the JAX-RPC Message Handler API, it's also useful by itself. In fact, it's easier to understand SAAJ by discussing it outside the context of JAX-RPC. This chapter explains the SAAJ programming model and explains how to use it as a standalone API. Chapter 14: Message Handlers explains how to use SAAJ with JAX-RPC.

SAAJ is an API-based SOAP toolkit, which means that it models the structure of SOAP messages. SAAJ models SOAP Messages with Attachments (SwA) in Java. SwA is the MIME message format for SOAP.[1] For all practical purposes SwA is a standard and is used throughout the Web services industry. While SAAJ models plain SOAP messages as well as SwA, the WS-I Basic Profile does not endorse SwA, so it's not covered in this part of the book. You're very likely to encounter SwA messaging at some time, though, so this book provides detailed coverage of SAAJ's support for SwA in Appendix F.

SAAJ (rhymes with “page”) is an API you can use to create, read, or modify SOAP messages using Java. It includes classes and interfaces that model the SOAP Envelope, Body, Header, and Fault elements, along with XML namespaces, elements, and attributes, and text nodes and MIME attachments. SAAJ is similar to JDBC, in that it's a hollow API. You can't use it by itself; you need a vendor implementation. Each J2EE vendor will provide its own SAAJ implementation. They should all function the same way, although some may be more efficient than others.

You can use SAAJ to manipulate simple SOAP messages (just the XML without any attachments) or more complex SOAP messages with MIME attachments. SAAJ is used in combination with JAX-RPC (Java API for XML-based RPC), which is the J2EE standard API for sending and receiving SOAP messages. SAAJ can also be used independently of JAX-RPC and has its own, optional facilities for basic Request/ Response-style messaging over HTTP or other protocols.

SAAJ's network communication is based on the java.net.URL class, which can be extended to support any network protocol, not just HTTP. HTTP is the only protocol actually sanctioned by the Basic Profile, however.

Java developers can use SAAJ to work with SOAP messages within any SOAP application, including initial senders, intermediaries, and ultimate receivers. For example, you might develop a SOAP intermediary that processes a specific header block before sending the message on to the next receiver. Using SAAJ you can easily examine a SOAP message, extract the appropriate header block, then send the message along to the next node in the message path. Similarly, an ultimate receiver can use SAAJ to process the application-specific content of the SOAP body.

SAAJ is based on the Abstract Factory Pattern,[2] which means SAAJ is a family of types in which each type of object is manufactured by another type in the SAAJ family. The root of the Abstract Factory Pattern in SAAJ is the MessageFactory class. It's responsible for manufacturing an instance of itself, which can in turn be used to manufacture a SOAPMessage. A SOAPMessage contains a SOAPPart, which represents the SOAP document, and zero or more AttachmentPart objects, which represent MIME attachments (such as GIFs and PDFs).

The SOAPPart contains a family of objects that model the SOAP document, including the Envelope, Header, and Body elements. You can obtain a clearer understanding of SAAJ by examining the basic structure of the SAAJ API alongside a diagram of an SwA message. As you can see from Figure 13-1, the SAAJ API models the exact structure of an SwA message.

Comparing the SAAJ Types to an SwA Message

Figure 13-1. Comparing the SAAJ Types to an SwA Message

SAAJ 1.2 is also based, in part, on the W3C Document Object Model (DOM), version 2. This relationship is explained in more detail in Section 13.6: SAAJ 1.2 and DOM 2.

A Simple SAAJ Example

The best way to learn SAAJ is to jump right in and build a simple SOAP message using the API. Listing 13-1 shows the BookQuote request message you've seen before.

Example 13-1. A Simple SOAP Message

<?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>0321146182</isbn>
      </mh:getBookPrice>
   </soap:Body>
</soap:Envelope>

SaajExample_1 in Listing 13-2 uses the SAAJ API to create the SOAP message, which is semantically equivalent to the SOAP message shown in Listing 13-1.

Example 13-2. Using SAAJ to Create a Simple SOAP Message

package com.jwsbook.saaj;
import javax.xml.soap.*;

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

    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    message.getSOAPHeader().detachNode();
    SOAPBody body = message.getSOAPBody();
    SOAPElement getBookPrice = body.addChildElement(
                            "getBookPrice",
                            "mh",
                            "http://www.Monson-Haefel.com/jwsbook/BookQuote");
    getBookPrice.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
    SOAPElement isbn = getBookPrice.addChildElement("isbn");
    isbn.addTextNode("0321146182");

    SaajOutputter.writeToScreen(message);
  }
}

To run SaajExample_1 and the rest of the examples in this book, you'll need to have a J2EE platform installed, the proper classpaths set up, and supporting Web services deployed. You'll know if SaajExample_1 is working properly by the lack of error messages—if there is a problem a SOAPException will be thrown and displayed on the output screen.

The rest of this chapter will use SaajExample_1 and similar examples to discuss the SAAJ API and how to use its various classes and interfaces. Although you will frequently use the SAAJ API inside J2EE message handlers for JAX-RPC service endpoints and EJB endpoints, the examples in this chapter are standalone applications. This approach spares you the arduous process of deploying a J2EE component every time you want to test an example.

Creating a SOAP Message

To create a simple SOAP document, you obtain a new SOAPMessage object from a MessageFactory object, as shown in this snippet from SaajExample_1 (Listing 13-2):

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

Although you will usually work with basic SOAP messages, without attachments, the SAAJ API models SwA, not just SOAP. Appendix F: SAAJ Attachments shows how to create an SwA message in which the SOAP document is treated as a MIME part, and accessed via the SOAPMessage.getSOAPPart() method.

The MessageFactory Class

The MessageFactory is the root factory of SAAJ. It's the class you will start with each time you create a SOAP message. MessageFactory is an abstract class that contains three methods, as shown in Listing 13-3 (the implementations are omitted).

Example 13-3. The javax.xml.soap.MessageFactory Class

package javax.xml.soap;

public abstract class MessageFactory {

  private static final String DEFAULT_MESSAGE_FACTORY =
          "com.sun.xml.messaging.saaj.soap.MessageFactoryImpl";

  private static final String MESSAGE_FACTORY_PROPERTY =
          "javax.xml.soap.MessageFactory";

  public static MessageFactory newInstance() throws SOAPException;

  public SOAPMessage createMessage() throws SOAPException;

  public SOAPMessage createMessage(MimeHeaders headers,java.io.InputStream in)
  throws SOAPException;
}

The newInstance() Method

The MessageFactory class itself is abstract and cannot be instantiated. Its new Instance() method creates an object that is actually a subtype of MessageFactory. By default, the instance is of a proprietary type provided by Sun Microsystems, when SAAJ is employed as a standalone API.

The createMessage() Method

In addition to the newInstance() method, the MessageFactory has two create Message() methods. The first takes no arguments, and is the one used in SaajExample_1. It simply creates a new SOAPMessage. The following code line from Listing 13-2 shows how the createMessage() method is used in an application.

SOAPMessage message = msgFactory.createMessage();

In this case the SOAPMessage object is generated from scratch, and contains only the framework of a SOAP message. If you were to dump the contents of the SOAPMessage instance to the screen, it would look something like the following.

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header/>
  <soap:Body/>
</soap:Envelope>

Notice that the main message elements, Envelope, Header, and Body, are present, the last two empty. These are supplied as a convenience for the developer. Once the SOAPMessage is created, all you need to do is fill in the blanks using the SAAJ API.

The createMessage Method with Parameters

The MessageFactory's second create method can construct a SAAJ representation of an existing SOAP message, instead of building a new one from scratch. The following snippet from Listing 13-3 shows the createMessage() method declaration.

public abstract class MessageFactory {
  ...
  public SOAPMessage createMessage(MimeHeaders headers,java.io.InputStream in)
  throws SOAPException;
}

The MimeHeaders parameter holds one or more MIME headers. A MIME header is a name-value pair that describes the contents of a MIME block. For example, a SOAP document might have a MIME header with a name-value pair of "Content-Type = text/xml".

The InputStream parameter can be any kind of stream. For example it could be a network stream from a socket connection, an IO stream from a JDBC connection, or a simple file stream. The data obtained from the InputStream parameter must be a valid SOAP or SwA message. For example, suppose a file called soap.xml contains a SOAP document like the one shown in Listing 13-4.

Example 13-4. The soap.xml File

<?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>0321146182</isbn>
      </mh:getBookPrice>
   </soap:Body>
</soap:Envelope>

Using a FileInputStream, the MessageFactory class can read the soap.xml file and generate a SAAJ object graph of the SOAP message it contains. In Listing 13-5 SaajExample_2 uses the MessageFactory class to read the file and generate a SOAPMessage.

Example 13-5. Building a SAAJ Object Graph from a File

package com.jwsbook.saaj;
import java.io.FileInputStream;
import javax.xml.soap.*;

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

        MessageFactory msgFactory = MessageFactory.newInstance();

        MimeHeaders mimeHeaders = new MimeHeaders();
        mimeHeaders.addHeader("Content-Type","text/xml; charset=UTF-8");

        FileInputStream file = new FileInputStream("soap.xml");

        SOAPMessage message = msgFactory.createMessage(mimeHeaders, file);

        file.close();
        SaajOutputter.writeToScreen(message);
    }
}

In SaajExample_2 the MIME header "Content-Type=text/xml;charset =UTF-8" is added to an instance of MimeHeaders, which is the first parameter of the createMessage() method. The second parameter is an instance of FileInput Stream that points to the soap.xml file.

To summarizeYou can use MessageFactory to create new SOAP messages from scratch or from an existing SOAP message obtained from some type of input stream. In many cases, however, you will use SAAJ in combination with JAX-RPC, as explained in Chapter 14, and receive a complete SAAJ message automatically. In such cases you will not need to use either of the createMessage() methods.

SaajOutputter Classes

To become skilled with SOAP, you must be able to read the SOAP messages generated by SAAJ, so this book uses a custom-built class called SaajOutputter to write SOAPMessage objects out in a nice, readable format. The following snippet shows how SaajOutputter is used.

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
SaajOutputter.writeToScreen(message);

SaajOutputter is not a part of the SAAJ API. It was developed specifically for this book and belongs to the com.jwsbook.saaj package, the example code for this chapter. As an alternative you can simply use the SOAPMessage.writeTo() method (using System.out as the parameter), but the output will have no line breaks and won't be as easy to read.

SaajOutputter is a simple hack that works only when the SOAPMessage does not contain attachments. If the SOAPMessage contains attachments, use SOAPMessage.writeTo() instead of SaajOutputter.

The SOAPMessage Class

In many cases, the SOAP message you're working with will not have attachments and will not need the MIME message format. The SAAJ implementations are smart enough to recognize when no attachments are added, and omit the MIME packaging when writing the message. If you look closely at SaajExample_1 (Listing 13-2), you'll notice that no MIME attachments are added, and that the output of the program is strictly XML and doesn't include any MIME headers or boundaries. When SaajExample_1 executed, the SAAJ toolkit realized that no attachments had been added, so it didn't enforce the use of the MIME message format.

Most of the methods defined by the SOAPMessage class are related to SwA MIME parts; these are covered in Appendix F: SAAJ Attachments. The only methods that are relevant to this chapter are writeTo(), getSOAPBody(), getSOAPHeader(), getProperty(), and setProperty().

The writeTo() Method

The SOAPMessage.writeTo() method simply writes the message represented by the SOAPMessage object to an output stream. If the SOAPMessage has no attachments, writeTo() will write only the XML SOAP part of the message to the stream. For example, in the following code snippet a SOAPMessage writes its contents to System.out (the screen) using writeTo().

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = msgFactory.createMessage();
soapMessage.writeTo(System.out);

The output from the SOAPMessage.writeTo() method is the default content of the SOAP message without line breaks. The lack of line breaks and white space (except between the XML declaration and the SOAP message) creates a tight stream of text. Because SOAP message are, in practice, processed by software and not read by people, readability is not important; eliminating line breaks and unnecessary white space is considered more efficient.

The getSOAPBody() and getSOAPHeader() Methods

The root MIME part of any SwA message is always the XML SOAP document; this is covered in more detail in Appendix F. The getSOAPPart() method allows you to access the SOAP MIME part directly, as shown in this snippet.

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
SOAPPart soap = message.getSOAPPart();

Accessing the SOAPBody and SOAPHeader via the SOAPPart is not necessary, however, if the SOAP message does not use attachments, as will be the case with BP-conformant Web services. When there are no attachments, you can simply use the getSOAPBody() and getSOAPHeader() methods to access those elements in the SOAP message directly. The following snippet from SaajExample_1 shows how these methods are used.

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
message.getSOAPHeader().detachNode();
SOAPBody body = message.getSOAPBody();

The examples in this chapter use the getSOAPBody() and getSOAPHeader() methods. In Appendix F: SAAJ Attachments, the getSOAPPart() and getSOAPEnvelope() methods are used. The detachNode() method simply removes the Header element from the SOAP message—SAAJ always includes the Header by default. Header blocks are not used in this example, so the Header element is not needed.

The getProperty() and setProperty() Methods

The SOAPMessage type also defines the setProperty() and getProperty() methods, which are used to set and obtain standard and vendor-specific properties of a SOAPMessage object. There are two standard properties:

  • javax.xml.soap.CHARACTER_SET_ENCODING can be either "UTF-8" or "UTF-16" in applications that conform with the BP. The default is "UTF-8".BP

  • javax.xml.soap.WRITE_XML_DECLARATION can have a value of "true" or "false". If it's "true", then the XML declaration is included in the SOAP message; if "false", it is not. The default is "false". Web service endpoints must accept SOAP 1.1 messages with or without an XML declaration.BP

As an example, the following snippet modifies SaajExample_2 (Listing 13-5) so that it sets the values of CHARACTER_SET_ENCODING and WRITE_XML_DECLARATION to "UTF-16" and "true" respectively.

FileInputStream file = new FileInputStream("soap.xml");
SOAPMessage message = msgFactory.createMessage(mimeHeaders, file);

message.setProperty("javax.xml.soap.CHARACTER_SET_ENCODING", "UTF-16");
message.setProperty("javax.xml.soap.WRITE_XML_DECLARATION", "true");

With these properties set, the output for SaajExample_2 would be an XML document in UTF-16 with an XML document declaration, as follows:

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

This SOAP message is exactly the same as the one in Listing 13-4, except that it includes the XML declaration shown in bold. XML declarations are explained in more detail in Section 2.1.2.1 and Section 4.1. The fact that the SOAP message is encoded using UTF-16 rather than UTF-8 is not visible to the eye.

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 13-2 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

Figure 13-2. Inheritance Diagram of SAAJ SOAP Types

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 13-3 shows the correlation between SOAP elements and SAAJ types.

Comparing the SAAJ Type System to a SOAP Document

Figure 13-3. Comparing the SAAJ Type System to a SOAP Document

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.

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.

Example 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.

Example 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.

Example 13-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>

Table 13-1. 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 Table 13-1.

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. Table 13-2 shows the String values that will be returned when invoking the methods of the Name object that represents the isbn element.

Table 13-2. Name Properties for the isbn Element in Listing 13-8

Method

Return Value

getLocalName()

"isbn"

getPrefix()

null

getQualifiedName()

"isbn"

getURI()

null

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 13-2, 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.

Example 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.

Example 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 );

The Node Type

The supertype of the SOAPElement type, and therefore all of its subtypes, is Node (see Figure 13-2). 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.

Example 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.

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.

Example 13-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.

Example 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.

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).

Example 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.

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.

Example 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.

The SOAPBodyElement Type

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

Example 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);

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.

Example 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.

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. Table 13-3 shows the SOAPConstants fields and their values.

Table 13-3. 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.

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).

Example 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){...}
}

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).

Example 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.

Example 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.

Example 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.

Working with SOAP Faults

As you know from Chapter 4, faults are a special kind of SOAP message that contains error information. SOAP faults are always delivered from the receiver back to the sender. In SAAJ, SOAP fault messages are constructed in basically the same way as a plain SOAP message, except the SOAPBody object contains a SOAPFault instead of a SOAPBodyElement. SOAPFault is actually a subtype of SOAP BodyElement that specializes the behavior to support the structure of a SOAP Fault element. Several interfaces play a role in creating SOAP fault messages with SAAJ. Figure 13-4 shows these interfaces in the context of all the other SAAJ types.

Inheritance Diagram of SAAJ SOAP Fault Types (Fault types are in gray.)

Figure 13-4. Inheritance Diagram of SAAJ SOAP Fault Types (Fault types are in gray.)

The SOAPFault Type

Every instance of the SOAPFault type is contained by a SOAPBody element. It describes an error generated by the receiver while processing a SOAP message.

The SOAPFault interface (Listing 13-22) defines several methods for setting and getting the faultactor, faultcode, and faultstring (description) elements of the Fault element, as well as creating and accessing detail elements.

Example 13-22. The javax.xml.soap.SOAPFault Interface

package javax.xml.soap;
import java.util.Locale;

public interface SOAPFault extends SOAPBodyElement {

    public Detail addDetail() throws SOAPException;
    public Detail getDetail();
    public String getFaultActor();
    public String getFaultCode();
    public Name getFaultCodeAsName();
    public String getFaultString();
    public Locale getFaultStringLocale();
    public void setFaultActor(String faultActor) throws SOAPException;
    public void setFaultCode(String faultCode) throws SOAPException;
    public void setFaultCode(Name faultCode) throws SOAPException;
    public void setFaultString(String faultString) throws SOAPException;
    public void setFaultString(String faultString, Locale local)
     throws SOAPException;

}

As an example, imagine that the ultimate receiver of the BookQuote SOAP message determines that the ISBN number declared in the Body of an incoming message is invalid. The receiver will generate a SOAP Fault message and deliver it to the sender immediately before it in the message path. The Fault message might look like the one in Listing 13-23.

Example 13-23. A SOAP Fault Message

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Client</faultcode>
      <faultstring>The ISBN contains an invalid character(s)</faultstring>
      <faultactor>
        http://www.Monson-Haefel.omc/BookQuote_WebService
      </faultactor>
      <detail>
        <mh:InvalidIsbnFaultDetail
               xmlns:mh="http://www.Monson-Haefel.com/jwsbook/BookQuote">
          <offending-value>19318224-D</offending-value>
          <conformance-rules>
               The first nine characters must be digits. The last
               character may be a digit or the letter 'X'. Case is not
               important.
          </conformance-rules>
        </mh:InvalidIsbnFaultDetail>
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

This fault message can be constructed fairly easily using SAAJ, as shown in Listing 13-24, SaajExample_7.

Example 13-24. Building a Fault Message with SAAJ

package com.jwsbook.saaj;
import javax.xml.soap.*;

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

    // Create Fault message
    SOAPBody body = message.getSOAPBody();

    SOAPFault fault = body.addFault();
    fault.setFaultCode("soap:Client");
    fault.setFaultString("The ISBN contains an invalid character(s)");
    fault.setFaultActor("http://www.Monson-Haefel.org/BookQuote_WebService");

    Detail detail = fault.addDetail();
    SOAPFactory soapFactory = SOAPFactory.newInstance();
    Name errorName = soapFactory.createName(
                     "InvalidIsbnFaultDetail","mh",
                     "http://www.Monson-Haefel.com/jwsbook/BookQuote");

    DetailEntry detailEntry = detail.addDetailEntry(errorName);
    SOAPElement offendingValue =
      detailEntry.addChildElement("offending-value");
    offendingValue.addTextNode("19318224-D");
    SOAPElement conformanceRules =
      detailEntry.addChildElement("conformance-rules");
    conformanceRules.addTextNode(
       "The first nine characters must be digits.  The last character "+
       "may be a digit or the letter 'X'. Case is not important.");

    SaajOutputter.writeToScreen(message);
  }
}

Version 1.2 of SAAJ modified the SOAPFault interface and added a couple of methods that are QName-oriented. For example, when you call the setFault Code() method, you can pass it a Name parameter that represents a proper QName, instead of a String. The following snippet illustrates.

SOAPFault fault = body.addFault();
SOAPFactory soapFactory = SOAPFactory.newInstance();

Name faultCode = soapFactory.createName("Client","soap",
                             SOAPConstants.URI_NS_SOAP_ENVELOPE);
fault.setFaultCode(faultCode);
fault.setFaultString("The ISBN contains an invalid character(s)");

You can also access fault codes as either String values or Name objects. The getFaultCodeAsName() method returns the fault code as a SAAJ Name object, rather than a String value. This book tends to use the String methods because they are easier to read in example code, but use the Name object methods if you wish.

To support international applications, the getFaultString() and set FaultString() methods are complemented by java.util.Locale style methods, namely getFaultStringLocale() and an overloading of setFaultString() that expects both a String and a Locale. These methods access or assign an xml:lang attribute to the faultstring value. If you do not use a Locale method, the default locale is used. A java.util.Locale represents a geographic, cultural, or political region (for example, the French-speaking area of Canada, or Simplified Chinese).

The legal values for the xml:lang attribute are specified in the IETF standard RFC 1766 and its successors (currently RFC 3066), which are based in part on ISO 639:1988. Language values begin with a primary two-character language identifier, optionally followed by a series of hyphen-delimited sub-ids for country or dialect identification; the ids are not case-sensitive. Examples include "en-us" for United States English and "fr-ca" for Canadian French. You don't have to use the sub-ids if the nature of the application doesn't require you to; "en" or "fr" by itself is fine.

The simplest way to add a SOAPFault to a message is to use the overloaded addFault() methods, which allow you to initialize the fault code, fault string, and language (Locale) of the fault string when it's created. For example, the following snippet shows how to create a complete SOAPFault object in one operation.

SOAPFactory soapFactory = SOAPFactory.newInstance();

Name faultCode = soapFactory.createName("Client","soap",
                             SOAPConstants.URI_NS_SOAP_ENVELOPE);
SOAPFault fault = body.addFault(faultCode,
                                "The ISBN contains an invalid character(s)",
                                Locale.US);

If you're using the default Locale, you can just invoke the addFault(Name,String) overloading of the method instead of passing the default Locale object explicitly.

The Detail Type

A SOAPFault object contains an object of type Detail, which in turn contains one or more DetailEntry objects. As shown in Listing 13-25, the Detail type defines two methods: one for adding new DetailEntry objects and one for accessing existing ones.

Example 13-25. The javax.xml.soap.Detail Interface

package javax.xml.soap;
import java.util.Iterator;

public interface Detail extends SOAPFaultElement {
    public DetailEntry addDetailEntry(Name name) throws SOAPException;
    public Iterator getDetailEntries()
}

The Detail object was used in SaajExample_7, as shown in the following snippet from Listing 13-24.

Detail detail = fault.addDetail();
Name errorName = envelope.createName("InvalidIsbnFaultDetail","mh",
                          "http://www.Monson-Haefel.com/jwsbook/BookQuote");

DetailEntry detailEntry = detail.addDetailEntry(errorName);

The SOAPFaultElement Type

SOAPFaultElement is the supertype of SOAPDetail type (see Figure 13-4). It defines no methods or fields; it's an empty interface. It provides the same kind of typing benefit that SOAPBodyElement does: some assurance of backward-compatibility if the SOAP protocol changes, and some type safety. Its definition appears in Listing 13-26.

Example 13-26. The javax.xml.soap.SOAPFaultElement Interface

package javax.xml.soap;
public interface SOAPFaultElement extends SOAPElement {}

SOAPFaultElement is not usually used directly in your code, but it has utility because it extends SOAPElement, and thus inherits all the methods of that interface.

The DetailEntry Type

DetailEntry, defined in Listing 13-27, is another empty interface. It has no methods or fields, and is useful only for type safety (the Detail object may contain DetailEntry objects only). Its supertype, SOAPElement, defines all the methods you need to work with detail entries. It also offers some flexibility for the future, if new versions of SOAP add new restrictions on detail elements.

Example 13-27. The javax.xml.soap.DetailEntry Interface

package javax.xml.soap;
public interface DetailEntry extends SOAPElement {}

Sending SOAP Messages with SAAJ

Building SOAP messages with SAAJ wouldn't be very useful if you didn't send them anywhere. Usually SAAJ is used in combination with JAX-RPC, but it's not dependent on JAX-RPC.

SAAJ comes with its own, fairly simple and limited, message-delivery system, which is a part of the basic API. Using SAAJ you can exchange Request/Response-style SOAP messages with a Web service over HTTP. You can also use SAAJ's native message delivery system with any other kind of URL-based protocol, depending on what your vendor supports—but remember that HTTP is the only protocol sanctioned by the Basic Profile.

SAAJ's native message-delivery system is so simple that it takes only a few lines of code to send and receive SOAP messages. In fact it's so simple that there is not much to explain. You simply create a SOAPConnection and send the message. SaajExample_8 in Listing 13-28 demonstrates this procedure.

Example 13-28. Using SAAJ to Send and Receive SOAP Messages

package com.jwsbook.saaj;
import javax.xml.soap.*;
import java.net.URL;
import java.io.FileInputStream;

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

    // Build a SOAPMessage from a file
    MessageFactory msgFactory = MessageFactory.newInstance();
    MimeHeaders mimeHeaders = new MimeHeaders();
    mimeHeaders.addHeader("Content-Type","text/xml; charset=UTF-8");
    FileInputStream file = new FileInputStream("soap.xml");
    SOAPMessage requestMsg = msgFactory.createMessage(mimeHeaders, file);
    file.close();

    // Send the SOAP message to the BookQuote Web service
    SOAPConnectionFactory conFactry = SOAPConnectionFactory.newInstance();
    SOAPConnection connection = conFactry.createConnection();
    URL url = new URL(args[0]);
    SOAPMessage replyMsg =connection.call(requestMsg, url);

    // Print out the reply message
    SaajOutputter.writeToScreen(replyMsg);
  }
}

SaajExample_8 builds a SOAP message from a file (soap.xml), then sends the message to the URL you provide as an argument. The SOAPConnection object creates an HTTP connection to the specified URL and sends the SOAP message to the Web service as an HTTP POST message. The HTTP reply that's sent from the Web service back to the SOAPConnection object will contain a SOAP message, a SOAP fault, or an HTTP error code; in the last case a SOAPException is thrown.

The SOAP reply message is returned by the SOAPConnection.call() method as a SOAPMessage object, which can be accessed using the SAAJ API. SaajExample_8 writes the reply SOAP message to the screen. It will look something like this:

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

SAAJ's native messaging service is optional, so your vendor may not support it; if so, the SOAPConnection.call() method may not work. Check your vendor's documentation to determine whether it's supported.

SAAJ 1.2 and DOM 2

SAAJ 1.1, the first official version of SAAJ, was not as flexible as it could have been. Vendors and developers complained that it should have been based on DOM 2. They claimed—rightly—that a DOM-based SAAJ would be a more flexible API, would make it easier to work with arbitrary XML document fragments, and would allow SAAJ to work well with DOM. In recognition of version 1.1's shortcomings, SAAJ 1.2 redefines the API so that it's an extension of the DOM 2 Java object model. SAAJ is now a lot more powerful, because you can use it to create and manipulate SOAP messages, but you can also take advantage of low-level DOM 2 functionality as the need arises. In addition, you can import Nodes from a DOM 2 document into a SOAP message, which is useful when working with JAX-RPC message handlers and Document/Literal payloads.

This section assumes you are already familiar with DOM 2. If you're not, then you should take time out to read Chapter 21: DOM 2 now, or this section is not going to make much sense to you. Chapter 21 will teach some of the basics about the DOM 2 programming API and how it's used. To find out where you can learn still more about DOM, see the introduction to Part VI: JAXP.

Aligning SAAJ 1.2 with DOM 2 didn't really complicate the SAAJ API too much. In most cases interfaces were simply redefined to extend DOM 2 interface types like Document, Element, and Text. Figure 13-5 shows how SAAJ 1.2 types inherit from DOM 2 types.

SAAJ 1.2 and DOM 2 Inheritance Diagram (DOM types are in gray, SAAJ types in black.)

Figure 13-5. SAAJ 1.2 and DOM 2 Inheritance Diagram (DOM types are in gray, SAAJ types in black.)

SaajExample_9 in Listing 13-29 is a simple example of how you can use SAAJ 1.2 and DOM 2 in concert to construct a Document/Literal SOAP message.

Example 13-29. Importing a DOM Element into a SAAJ 1.2 SOAPMessage

package com.jwsbook.saaj;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.soap.*;

public class SaajExample_9 {
  public static void main(String [] args) throws Exception{

    // Read an XML document from a file into a DOM tree using JAXP.
    String filePath = args[0];
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder parser = factory.newDocumentBuilder();
    Document xmlDoc = parser.parse(filePath);

    // Create a SAAJ SOAPMessage object. Get the SOAPBody and SOAPPart.
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPPart soapPart = message.getSOAPPart();
    SOAPBody soapBody = soapPart.getEnvelope().getBody();

    // Import the root element and append it to the body of the SOAP message.
    Node elementCopy =
      soapPart.importNode(xmlDoc.getDocumentElement(), true);
    soapBody.appendChild(elementCopy);

    // Output SOAP message.
    SaajOutputter.writeToScreen(message);
  }
}

SaajExample_9 reads an XML document from a file and has a DOM provider parse it into a Document object. It also creates a new SOAPMessage object. The application imports the root of the XML document into the SOAPPart (using the Document.importNode() method) and subsequently appends the imported copy to the SOAPBody (using the Node.appendNode() method).

As an alternative to using the importNode() method, you can use the SOAP Body.addDocument() method, which requires a lot less code, but has a significant side effect. When you call SOAPBody.addDocument(), the root element of the Document is actually moved (not copied) to become a child of the SOAPBody. In other words, the root element is physically reassigned to the SOAPMessage object. As you learned earlier in chapter 21, a Node cannot refer to more than one Document object, so moving the root element to the SOAPMessage (specifically to the SOAPPart) invalidates the source Document—there is no longer a root element that refers to it. In Listing 13-30, SaajExample_10 shows how you can use SOAPBody.addDocument().

Example 13-30. Moving a DOM Root Element to a SAAJ 1.2 SOAPMessage

package com.jwsbook.saaj;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.soap.*;

public class SaajExample_10 {
  public static void main(String [] args) throws Exception{

    // Read an XML document from a file into a DOM tree using JAXP.
    String filePath = args[0];
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder parser = factory.newDocumentBuilder();
    Document xmlDoc = parser.parse(filePath);

    // Create a SAAJ SOAPMessage object. Get the SOAPBody.
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPBody soapBody = message.getSOAPPart().getEnvelope().getBody();

    // Append the root element of the XML doc to the body of the SOAP message.
    soapBody.addDocument(xmlDoc);

    // Output SOAP message.
    jwsed1.SaajOutputter.writeToScreen(message);
  }
}

Wrapping Up

Using SAAJ makes it fairly easy to construct SOAP messages from scratch, and to peruse SOAP messages received from other nodes. While SAAJ is normally used in combination with JAX-RPC or some other API, it can also be used independently, which is why it's a separate API from JAX-RPC.

JAX-RPC's Message Handler API depends heavily on SAAJ to represent incoming and outgoing SOAP messages so that you can manipulate SOAP header blocks. The next chapter explains in detail how to use SAAJ with JAX-RPC message handlers.

This chapter covered the construction of simple SOAP documents using SAAJ, but SAAJ is also designed to model SOAP Messages with Attachments (SwA), the MIME format used to deliver SOAP messages that refer to XML and non-XML data. Because the Basic Profile doesn't support SwA, this subject is addressed in Appendix F: SAAJ Attachments.



[1] The SwA specification is actually a Note maintained by the World Wide Web Consortium (W3C). A W3C “Note” is not a finished specification (which W3C calls a “Recommendation”), but simply a suggestion or a work in progress.

[2] Erich Gamma, et al. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995, p. 87.

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

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