Tutorial
This tutorial walks you through how to use the SAAJ API. First, it covers the basics of creating and sending a simple SOAP message. Then you will learn more details about adding content to messages, including how to create SOAP faults and attributes. Finally, you will learn how to send a message and retrieve the content of the response. After going through this tutorial, you will know how to perform the following tasks:
In the section Code Examples, you will see the code fragments from earlier parts of the tutorial in runnable applications, which you can test yourself.
A SAAJ client can send request-response messages to web services that are implemented to do request-response messaging. This section demonstrates how you can do this.
Creating and Sending a Simple Message
This section covers the basics of creating and sending a simple message and retrieving the content of the response. It includes the following topics:
Creating a Message
The first step is to create a message using a
MessageFactoryobject. The SAAJ API provides a default implementation of theMessageFactoryclass, thus making it easy to get an instance. The following code fragment illustrates getting an instance of the default message factory and then using it to create a message.MessageFactory factory = MessageFactory.newInstance(); SOAPMessage message = factory.createMessage();As is true of the
newInstancemethod forSOAPConnectionFactory, thenewInstancemethod forMessageFactoryis static, so you invoke it by callingMessageFactory.newInstance.If you specify no arguments to the
newInstancemethod, it creates a message factory for SOAP 1.1 messages. To create a message factory that allows you to create and process SOAP 1.2 messages, use the following method call:To create a message factory that can create either SOAP 1.1 or SOAP 1.2 messages, use the following method call:
This kind of factory enables you to process an incoming message that might be of either type.
Parts of a Message
A
SOAPMessageobject is required to have certain elements, and, as stated previously, the SAAJ API simplifies things for you by returning a newSOAPMessageobject that already contains these elements. When you callcreateMessagewith no arguments, the message that is created automatically has the following:I. A
SOAPPartobject that containsA. A
SOAPEnvelopeobject that contains1. An empty
SOAPHeaderobject2. An empty
SOAPBodyobjectThe
SOAPHeaderobject is optional and can be deleted if it is not needed. However, if there is one, it must precede theSOAPBodyobject. TheSOAPBodyobject can hold either the content of the message or a fault message that contains status information or details about a problem with the message. The section Using SOAP Faults walks you through how to useSOAPFaultobjects.Accessing Elements of a Message
The next step in creating a message is to access its parts so that content can be added. There are two ways to do this. The
SOAPMessageobjectmessage, created in the preceding code fragment, is the place to start.The first way to access the parts of the message is to work your way through the structure of the message. The message contains a
SOAPPartobject, so you use thegetSOAPPartmethod ofmessageto retrieve it:Next you can use the
getEnvelopemethod ofsoapPartto retrieve theSOAPEnvelopeobject that it contains.You can now use the
getHeaderandgetBodymethods ofenvelopeto retrieve its emptySOAPHeaderandSOAPBodyobjects.The second way to access the parts of the message is to retrieve the message header and body directly, without retrieving the
SOAPPartorSOAPEnvelope. To do so, use thegetSOAPHeaderandgetSOAPBodymethods ofSOAPMessage:This example of a SAAJ client does not use a SOAP header, so you can delete it. (You will see more about headers later.) Because all
SOAPElementobjects, includingSOAPHeaderobjects, are derived from theNodeinterface, you use the methodNode.detachNodeto deleteheader.Adding Content to the Body
The
SOAPBodyobject contains either content or a fault. To add content to the body, you normally create one or moreSOAPBodyElementobjects to hold the content. You can also add subelements to theSOAPBodyElementobjects by using theaddChildElementmethod. For each element or child element, you add content by using theaddTextNodemethod.When you create any new element, you also need to create an associated
javax.xml.namespace.QNameobject so that it is uniquely identified.
Note: You can use
Nameobjects instead ofQNameobjects.Nameobjects are specific to the SAAJ API, and you create them using eitherSOAPEnvelopemethods orSOAPFactorymethods. However, theNameinterface may be deprecated at a future release.
TheSOAPFactoryclass also lets you create XML elements when you are not creating an entire message or do not have access to a completeSOAPMessageobject. For example, JAX-RPC implementations often work with XML fragments rather than completeSOAPMessageobjects. Consequently, they do not have access to aSOAPEnvelopeobject, and this makes using aSOAPFactoryobject to createNameobjects very useful. In addition to a method for creatingNameobjects, theSOAPFactoryclass provides methods for creatingDetailobjects and SOAP fragments. You will find an explanation ofDetailobjects in Overview of SOAP Faults and Creating and Populating a SOAPFault Object.
QNameobjects associated withSOAPBodyElementorSOAPHeaderElementobjects must be fully qualified; that is, they must be created with a namespace URI, a local part, and a namespace prefix. Specifying a namespace for an element makes clear which one is meant if more than one element has the same local name.The following code fragment retrieves the
SOAPBodyobjectbodyfrommessage, constructs aQNameobject for the element to be added, and adds a newSOAPBodyElementobject tobody.SOAPBody body = message.getSOAPBody(); QName bodyName = new QName("http://wombat.ztrade.com", "GetLastTradePrice", "m"); SOAPBodyElement bodyElement = body.addBodyElement(bodyName);At this point,
bodycontains aSOAPBodyElementobject identified by theQNameobjectbodyName, but there is still no content inbodyElement. Assuming that you want to get a quote for the stock of Sun Microsystems, Inc., you need to create a child element for the symbol using theaddChildElementmethod. Then you need to give it the stock symbol using theaddTextNodemethod. TheQNameobject for the newSOAPElementobjectsymbolis initialized with only a local name because child elements inherit the prefix and URI from the parent element.QName name = new QName("symbol"); SOAPElement symbol = bodyElement.addChildElement(name); symbol.addTextNode("SUNW");You might recall that the headers and content in a
SOAPPartobject must be in XML format. The SAAJ API takes care of this for you, building the appropriate XML constructs automatically when you call methods such asaddBodyElement,addChildElement, andaddTextNode. Note that you can call the methodaddTextNodeonly on an element such asbodyElementor any child elements that are added to it. You cannot calladdTextNodeon aSOAPHeaderorSOAPBodyobject because they contain elements and not text.The content that you have just added to your
SOAPBodyobject will look like the following when it is sent over the wire:<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <m:GetLastTradePrice xmlns:m="http://wombat.ztrade.com"> <symbol>SUNW</symbol> </m:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope>Let's examine this XML excerpt line by line to see how it relates to your SAAJ code. Note that an XML parser does not care about indentations, but they are generally used to indicate element levels and thereby make it easier for a human reader to understand.
Here is the SAAJ code:
SOAPMessage message = messageFactory.createMessage(); SOAPHeader header = message.getSOAPHeader(); SOAPBody body = message.getSOAPBody();Here is the XML it produces:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> . . . </SOAP-ENV:Body> </SOAP-ENV:Envelope>The outermost element in this XML example is the SOAP envelope element, indicated by
SOAP-ENV:Envelope. Note thatEnvelopeis the name of the element, andSOAP-ENVis the namespace prefix. The interfaceSOAPEnveloperepresents a SOAP envelope.The first line signals the beginning of the SOAP envelope element, and the last line signals the end of it; everything in between is part of the SOAP envelope. The second line is an example of an attribute for the SOAP envelope element. Because a SOAP envelope element always contains this attribute with this value, a
SOAPMessageobject comes with it automatically included.xmlnsstands for "XML namespace," and its value is the URI of the namespace associated withEnvelope.The next line is an empty SOAP header. We could remove it by calling
header.detachNodeafter thegetSOAPHeadercall.The next two lines mark the beginning and end of the SOAP body, represented in SAAJ by a
SOAPBodyobject. The next step is to add content to the body.Here is the SAAJ code:
QName bodyName = new QName("http://wombat.ztrade.com", "GetLastTradePrice", "m"); SOAPBodyElement bodyElement = body.addBodyElement(bodyName);Here is the XML it produces:
These lines are what the
SOAPBodyElementbodyElementin your code represents.GetLastTradePriceis its local name,mis its namespace prefix, andhttp://wombat.ztrade.comis its namespace URI.Here is the SAAJ code:
QName name = new QName("symbol"); SOAPElement symbol = bodyElement.addChildElement(name); symbol.addTextNode("SUNW");Here is the XML it produces:
The
String"SUNW"is the text node for the element<symbol>. ThisStringobject is the message content that your recipient, the stock quote service, receives.The following example shows how to add multiple
SOAPElementobjects and add text to each of them. The code first creates theSOAPBodyElementobjectpurchaseLineItems, which has a fully qualified name associated with it. That is, theQNameobject for it has a namespace URI, a local name, and a namespace prefix. As you saw earlier, aSOAPBodyElementobject is required to have a fully qualified name, but child elements added to it, such asSOAPElementobjects, can haveNameobjects with only the local name.SOAPBody body = message.getSOAPBody(); QName bodyName = new QName("http://sonata.fruitsgalore.com", "PurchaseLineItems", "PO"); SOAPBodyElement purchaseLineItems = body.addBodyElement(bodyName); QName childName = new QName("Order"); SOAPElement order = purchaseLineItems.addChildElement(childName); childName = new QName("Product"); SOAPElement product = order.addChildElement(childName); product.addTextNode("Apple"); childName = new QName("Price"); SOAPElement price = order.addChildElement(childName); price.addTextNode("1.56"); childName = new QName("Order"); SOAPElement order2 = purchaseLineItems.addChildElement(childName); childName = new QName("Product"); SOAPElement product2 = order2.addChildElement(childName); product2.addTextNode("Peach"); childName = soapFactory.new QName("Price"); SOAPElement price2 = order2.addChildElement(childName); price2.addTextNode("1.48");The SAAJ code in the preceding example produces the following XML in the SOAP body:
<PO:PurchaseLineItems xmlns:PO="http://sonata.fruitsgalore.com"> <Order> <Product>Apple</Product> <Price>1.56</Price> </Order> <Order> <Product>Peach</Product> <Price>1.48</Price> </Order> </PO:PurchaseLineItems>Getting a SOAPConnection Object
The SAAJ API is focused primarily on reading and writing messages. After you have written a message, you can send it using various mechanisms (such as JMS or JAXM). The SAAJ API does, however, provide a simple mechanism for request-response messaging.
To send a message, a SAAJ client can use a
SOAPConnectionobject. ASOAPConnectionobject is a point-to-point connection, meaning that it goes directly from the sender to the destination (usually a URL) that the sender specifies.The first step is to obtain a
SOAPConnectionFactoryobject that you can use to create your connection. The SAAJ API makes this easy by providing theSOAPConnectionFactoryclass with a default implementation. You can get an instance of this implementation using the following line of code.Now you can use
soapConnectionFactoryto create aSOAPConnectionobject.You will use
connectionto send the message that you created.Sending a Message
A SAAJ client calls the
SOAPConnectionmethodcallon aSOAPConnectionobject to send a message. Thecallmethod takes two arguments: the message being sent and the destination to which the message should go. This message is going to the stock quote service indicated by theURLobjectendpoint.java.net.URL endpoint = new URL( "http://wombat.ztrade.com/quotes"); SOAPMessage response = connection.call(message, endpoint);The content of the message you sent is the stock symbol SUNW; the
SOAPMessageobjectresponseshould contain the last stock price for Sun Microsystems, which you will retrieve in the next section.A connection uses a fair amount of resources, so it is a good idea to close a connection as soon as you are finished using it.
Getting the Content of a Message
The initial steps for retrieving a message's content are the same as those for giving content to a message: Either you use the
Messageobject to get theSOAPBodyobject, or you access theSOAPBodyobject through theSOAPPartandSOAPEnvelopeobjects.Then you access the
SOAPBodyobject'sSOAPBodyElementobject, because that is the element to which content was added in the example. (In a later section you will see how to add content directly to theSOAPPartobject, in which case you would not need to access theSOAPBodyElementobject to add content or to retrieve it.)To get the content, which was added with the method
SOAPElement.addTextNode, you call the methodNode.getValue. Note thatgetValuereturns the value of the immediate child of the element that calls the method. Therefore, in the following code fragment, thegetValuemethod is called onbodyElement, the element on which theaddTextNodemethod was called.To access
bodyElement, you call thegetChildElementsmethod onsoapBody. PassingbodyNametogetChildElementsreturns ajava.util.Iteratorobject that contains all the child elements identified by theNameobjectbodyName. You already know that there is only one, so calling thenextmethod on it will return theSOAPBodyElementyou want. Note that theIterator.nextmethod returns a JavaObject, so you need to cast theObjectit returns to aSOAPBodyElementobject before assigning it to the variablebodyElement.SOAPBody soapBody = response.getSOAPBody(); java.util.Iterator iterator = soapBody.getChildElements(bodyName); SOAPBodyElement bodyElement = (SOAPBodyElement)iterator.next(); String lastPrice = bodyElement.getValue(); System.out.print("The last price for SUNW is");System.out.println(lastPrice);If more than one element had the name
bodyName, you would have to use awhileloop using theIterator.hasNextmethod to make sure that you got all of them.while (iterator.hasNext()) { SOAPBodyElement bodyElement = (SOAPBodyElement)iterator.next(); String lastPrice = bodyElement.getValue(); System.out.print("The last price for SUNW is");System.out.println(lastPrice); }At this point, you have seen how to send a very basic request-response message and get the content from the response. The next sections provide more detail on adding content to messages.
Adding Content to the Header
To add content to the header, you create a
SOAPHeaderElementobject. As with all new elements, it must have an associatedQNameobject.For example, suppose you want to add a conformance claim header to the message to state that your message conforms to the WS-I Basic Profile. The following code fragment retrieves the
SOAPHeaderobject frommessageand adds a newSOAPHeaderElementobject to it. ThisSOAPHeaderElementobject contains the correct qualified name and attribute for a WS-I conformance claim header.SOAPHeader header = message.getSOAPHeader(); QName headerName = new QName( "http://ws-i.org/schemas/conformanceClaim/", "Claim", "wsi"); SOAPHeaderElement headerElement = header.addHeaderElement(headerName); headerElement.addAttribute(new QName("conformsTo"), "http://ws-i.org/profiles/basic/1.1/");At this point,
headercontains theSOAPHeaderElementobjectheaderElementidentified by theQNameobjectheaderName. Note that theaddHeaderElementmethod both createsheaderElementand adds it toheader.A conformance claim header has no content. This code produces the following XML header:
<SOAP-ENV:Header> <wsi:Claim xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/" conformsTo="http://ws-i.org/profiles/basic/1.1/"/> </SOAP-ENV:Header>For more information about creating SOAP messages that conform to WS-I, see the Conformance Claim Attachment Mechanisms document described in the Conformance section of the WS-I Basic Profile.
For a different kind of header, you might want to add content to
headerElement. The following line of code uses the methodaddTextNodeto do this.Now you have the
SOAPHeaderobjectheaderthat contains aSOAPHeaderElementobject whose content is"order".Adding Content to the SOAPPart Object
If the content you want to send is in a file, SAAJ provides an easy way to add it directly to the
SOAPPartobject. This means that you do not access theSOAPBodyobject and build the XML content yourself, as you did in the preceding section.To add a file directly to the
SOAPPartobject, you use ajavax.xml.transform.Sourceobject from JAXP (the Java API for XML Processing). There are three types ofSourceobjects:SAXSource,DOMSource, andStreamSource. AStreamSourceobject holds an XML document in text form.SAXSourceandDOMSourceobjects hold content along with the instructions for transforming the content into an XML document.The following code fragment uses the JAXP API to build a
DOMSourceobject that is passed to theSOAPPart.setContentmethod. The first three lines of code get aDocumentBuilderFactoryobject and use it to create theDocumentBuilderobjectbuilder. Because SOAP messages use namespaces, you should set theNamespaceAwareproperty for the factory to true. Thenbuilderparses the content file to produce aDocumentobject.DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); dbFactory.setNamespaceAware(true); DocumentBuilder builder = dbFactory.newDocumentBuilder(); Document document = builder.parse("file:///music/order/soap.xml"); DOMSource domSource = new DOMSource(document);The following two lines of code access the
SOAPPartobject (using theSOAPMessageobjectmessage) and set the newDocumentobject as its content. TheSOAPPart.setContentmethod not only sets content for theSOAPBodyobject but also sets the appropriate header for theSOAPHeaderobject.The XML file you use to set the content of the
SOAPPartobject must includeEnvelopeandBodyelements:<SOAP-ENV:Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> ... </SOAP-ENV:Body> </SOAP-ENV:Envelope>You will see other ways to add content to a message in the sections Adding a Document to the SOAP Body and Adding Attachments.
Adding a Document to the SOAP Body
In addition to setting the content of the entire SOAP message to that of a
DOMSourceobject, you can add a DOM document directly to the body of the message. This capability means that you do not have to create ajavax.xml.transform.Sourceobject. After you parse the document, you can add it directly to the message body:Manipulating Message Content Using SAAJ or DOM APIs
Because SAAJ nodes and elements implement the DOM
NodeandElementinterfaces, you have many options for adding or changing message content:The first three of these cause no problems. After you have created a message, whether or not you have imported its content from another document, you can start adding or changing nodes using either SAAJ or DOM APIs.
But if you use DOM APIs and then switch to using SAAJ APIs to manipulate the document, any references to objects within the tree that were obtained using DOM APIs are no longer valid. If you must use SAAJ APIs after using DOM APIs, you should set all your DOM typed references to null, because they can become invalid. For more information about the exact cases in which references become invalid, see the SAAJ API documentation.
The basic rule is that you can continue manipulating the message content using SAAJ APIs as long as you want to, but after you start manipulating it using DOM, you should no longer use SAAJ APIs.
Adding Attachments
An
AttachmentPartobject can contain any type of content, including XML. And because the SOAP part can contain only XML content, you must use anAttachmentPartobject for any content that is not in XML format.Creating an AttachmentPart Object and Adding Content
The
SOAPMessageobject creates anAttachmentPartobject, and the message also must add the attachment to itself after content has been added. TheSOAPMessageclass has three methods for creating anAttachmentPartobject.The first method creates an attachment with no content. In this case, an
AttachmentPartmethod is used later to add content to the attachment.You add content to
attachmentby using theAttachmentPartmethodsetContent. This method takes two parameters: a JavaObjectfor the content, and aStringobject for the MIME content type that is used to encode the object. Content in theSOAPBodypart of a message automatically has aContent-Typeheader with the value"text/xml"because the content must be in XML. In contrast, the type of content in anAttachmentPartobject must be specified because it can be any type.Each
AttachmentPartobject has one or more MIME headers associated with it. When you specify a type to thesetContentmethod, that type is used for the headerContent-Type. Note thatContent-Typeis the only header that is required. You may set other optional headers, such asContent-IdandContent-Location. For convenience, SAAJ providesgetandsetmethods for the headersContent-Type,Content-Id, andContent-Location. These headers can be helpful in accessing a particular attachment when a message has multiple attachments. For example, to access the attachments that have particular headers, you can call theSOAPMessagemethodgetAttachmentsand pass it aMIMEHeadersobject containing the MIME headers you are interested in.The following code fragment shows one of the ways to use the method
setContent. The JavaObjectin the first parameter can be aString, a stream, ajavax.xml.transform.Sourceobject, or ajavax.activation.DataHandlerobject. The JavaObjectbeing added in the following code fragment is aString, which is plain text, so the second argument must be"text/plain". The code also sets a content identifier, which can be used to identify thisAttachmentPartobject. After you have added content toattachment, you must add it to theSOAPMessageobject, something that is done in the last line.String stringContent = "Update address for Sunny Skies " + "Inc., to 10 Upbeat Street, Pleasant Grove, CA 95439"; attachment.setContent(stringContent, "text/plain"); attachment.setContentId("update_address"); message.addAttachmentPart(attachment);The
attachmentvariable now represents anAttachmentPartobject that contains the stringstringContentand has a header that contains the string"text/plain". It also has aContent-Idheader with"update_address"as its value. Andattachmentis now part ofmessage.The other two
SOAPMessage.createAttachmentmethods create anAttachmentPartobject complete with content. One is very similar to theAttachmentPart.setContentmethod in that it takes the same parameters and does essentially the same thing. It takes a JavaObjectcontaining the content and aStringgiving the content type. As withAttachmentPart.setContent, theObjectcan be aString, a stream, ajavax.xml.transform.Sourceobject, or ajavax.activation.DataHandlerobject.The other method for creating an
AttachmentPartobject with content takes aDataHandlerobject, which is part of the JavaBeans Activation Framework (JAF). Using aDataHandlerobject is fairly straightforward. First, you create ajava.net.URLobject for the file you want to add as content. Then you create aDataHandlerobject initialized with theURLobject:URL url = new URL("http://greatproducts.com/gizmos/img.jpg"); DataHandler dataHandler = new DataHandler(url); AttachmentPart attachment = message.createAttachmentPart(dataHandler); attachment.setContentId("attached_image"); message.addAttachmentPart(attachment);You might note two things about this code fragment. First, it sets a header for
Content-IDusing the methodsetContentId. This method takes aStringthat can be whatever you like to identify the attachment. Second, unlike the other methods for setting content, this one does not take aStringforContent-Type. This method takes care of setting theContent-Typeheader for you, something that is possible because one of the things aDataHandlerobject does is to determine the data type of the file it contains.Accessing an AttachmentPart Object
If you receive a message with attachments or want to change an attachment to a message you are building, you need to access the attachment. The
SOAPMessageclass provides two versions of thegetAttachmentsmethod for retrieving itsAttachmentPartobjects. When it is given no argument, the methodSOAPMessage.getAttachmentsreturns ajava.util.Iteratorobject over all theAttachmentPartobjects in a message. WhengetAttachmentsis given aMimeHeadersobject, which is a list of MIME headers,getAttachmentsreturns an iterator over theAttachmentPartobjects that have a header that matches one of the headers in the list. The following code uses thegetAttachmentsmethod that takes no arguments and thus retrieves all theAttachmentPartobjects in theSOAPMessageobjectmessage. Then it prints the content ID, the content type, and the content of eachAttachmentPartobject.java.util.Iterator iterator = message.getAttachments(); while (iterator.hasNext()) { AttachmentPart attachment = (AttachmentPart)iterator.next(); String id = attachment.getContentId(); String type = attachment.getContentType(); System.out.print("Attachment " + id + " has content type " + type); if (type.equals("text/plain")) { Object content = attachment.getContent(); System.out.println("Attachment contains:\n" + content); } }Adding Attributes
An XML element can have one or more attributes that give information about that element. An attribute consists of a name for the attribute followed immediately by an equal sign (
=) and its value.The
SOAPElementinterface provides methods for adding an attribute, for getting the value of an attribute, and for removing an attribute. For example, in the following code fragment, the attribute namedidis added to theSOAPElementobjectperson. Becausepersonis aSOAPElementobject rather than aSOAPBodyElementobject orSOAPHeaderElementobject, it is legal for itsQNameobject to contain only a local name.These lines of code will generate the first line in the following XML fragment.
The following line of code retrieves the value of the attribute whose name is
id.If you had added two or more attributes to
person, the preceding line of code would have returned only the value for the attribute namedid. If you wanted to retrieve the values for all the attributes forperson, you would use the methodgetAllAttributes, which returns an iterator over all the values. The following lines of code retrieve and print each value on a separate line until there are no more attribute values. Note that theIterator.nextmethod returns a JavaObject, which is cast to aQNameobject so that it can be assigned to theQNameobjectattributeName. (The examples in DOMExample.java and DOMSrcExample.java use code similar to this.)Iterator iterator = person.getAllAttributesAsQNames(); while (iterator.hasNext()){ QName attributeName = (QName) iterator.next(); System.out.println("Attribute name is " + attributeName.toString()); System.out.println("Attribute value is " + element.getAttributeValue(attributeName)); }The following line of code removes the attribute named
idfromperson. The variablesuccessfulwill betrueif the attribute was removed successfully.In this section you have seen how to add, retrieve, and remove attributes. This information is general in that it applies to any element. The next section discusses attributes that can be added only to header elements.
Header Attributes
Attributes that appear in a
SOAPHeaderElementobject determine how a recipient processes a message. You can think of header attributes as offering a way to extend a message, giving information about such things as authentication, transaction management, payment, and so on. A header attribute refines the meaning of the header, whereas the header refines the meaning of the message contained in the SOAP body.The SOAP 1.1 specification defines two attributes that can appear only in
SOAPHeaderElementobjects:actorandmustUnderstand.The SOAP 1.2 specification defines three such attributes:
role(a new name foractor),mustUnderstand, andrelay.The next sections discuss these attributes.
See HeaderExample.java for an example that uses the code shown in this section.
The Actor Attribute
The
actorattribute is optional, but if it is used, it must appear in aSOAPHeaderElementobject. Its purpose is to indicate the recipient of a header element. The default actor is the message's ultimate recipient; that is, if no actor attribute is supplied, the message goes directly to the ultimate recipient.An actor is an application that can both receive SOAP messages and forward them to the next actor. The ability to specify one or more actors as intermediate recipients makes it possible to route a message to multiple recipients and to supply header information that applies specifically to each of the recipients.
For example, suppose that a message is an incoming purchase order. Its
SOAPHeaderobject might haveSOAPHeaderElementobjects with actor attributes that route the message to applications that function as the order desk, the shipping desk, the confirmation desk, and the billing department. Each of these applications will take the appropriate action, remove theSOAPHeaderElementobjects relevant to it, and send the message on to the next actor.
Note: Although the SAAJ API provides the API for adding these attributes, it does not supply the API for processing them. For example, the actor attribute requires that there be an implementation such as a messaging provider service to route the message from one actor to the next.
An actor is identified by its URI. For example, the following line of code, in which
orderHeaderis aSOAPHeaderElementobject, sets the actor to the given URI.Additional actors can be set in their own
SOAPHeaderElementobjects. The following code fragment first uses theSOAPMessageobjectmessageto get itsSOAPHeaderobjectheader. Thenheadercreates fourSOAPHeaderElementobjects, each of which sets itsactorattribute.SOAPHeader header = message.getSOAPHeader(); SOAPFactory soapFactory = SOAPFactory.newInstance(); String nameSpace = "ns"; String nameSpaceURI = "http://gizmos.com/NSURI"; QName order = new QName(nameSpaceURI, "orderDesk", nameSpace); SOAPHeaderElement orderHeader = header.addHeaderElement(order); orderHeader.setActor("http://gizmos.com/orders"); QName shipping = new QName(nameSpaceURI, "shippingDesk", nameSpace); SOAPHeaderElement shippingHeader = header.addHeaderElement(shipping); shippingHeader.setActor("http://gizmos.com/shipping"); QName confirmation = new QName(nameSpaceURI, "confirmationDesk", nameSpace); SOAPHeaderElement confirmationHeader = header.addHeaderElement(confirmation); confirmationHeader.setActor( "http://gizmos.com/confirmations"); QName billing = new QName(nameSpaceURI, "billingDesk", nameSpace); SOAPHeaderElement billingHeader = header.addHeaderElement(billing); billingHeader.setActor("http://gizmos.com/billing");The
SOAPHeaderinterface provides two methods that return ajava.util.Iteratorobject over all theSOAPHeaderElementobjects that have an actor that matches the specified actor. The first method,examineHeaderElements, returns an iterator over all the elements that have the specified actor.The second method,
extractHeaderElements, not only returns an iterator over all theSOAPHeaderElementobjects that have the specified actor attribute but also detaches them from theSOAPHeaderobject. So, for example, after the order desk application did its work, it would callextractHeaderElementsto remove all theSOAPHeaderElementobjects that applied to it.Each
SOAPHeaderElementobject can have only one actor attribute, but the same actor can be an attribute for multipleSOAPHeaderElementobjects.Two additional
SOAPHeadermethods--examineAllHeaderElementsandextractAllHeaderElements--allow you to examine or extract all the header elements, whether or not they have an actor attribute. For example, you could use the following code to display the values of all the header elements:Iterator allHeaders = header.examineAllHeaderElements(); while (allHeaders.hasNext()) { SOAPHeaderElement headerElement = (SOAPHeaderElement)allHeaders.next(); QName headerName = headerElement.getElementQName(); System.out.println("\nHeader name is " + headerName.toString()); System.out.println("Actor is " + headerElement.getActor()); }The role Attribute
The
roleattribute is the name used by the SOAP 1.2 specification for the SOAP 1.2actorattribute. TheSOAPHeaderElementmethodssetRoleandgetRoleperform the same functions as thesetActorandgetActormethods.The mustUnderstand Attribute
The other attribute that must be added only to a
SOAPHeaderElementobject ismustUnderstand. This attribute says whether or not the recipient (indicated by theactorattribute) is required to process a header entry. When the value of themustUnderstandattribute istrue, the actor must understand the semantics of the header entry and must process it correctly to those semantics. If the value isfalse, processing the header entry is optional. ASOAPHeaderElementobject with nomustUnderstandattribute is equivalent to one with amustUnderstandattribute whose value isfalse.The
mustUnderstandattribute is used to call attention to the fact that the semantics in an element are different from the semantics in its parent or peer elements. This allows for robust evolution, ensuring that a change in semantics will not be silently ignored by those who may not fully understand it.If the actor for a header that has a
mustUnderstandattribute set totruecannot process the header, it must send a SOAP fault back to the sender. (See Using SOAP Faults.) The actor must not change state or cause any side effects, so that, to an outside observer, it appears that the fault was sent before any header processing was done.For example, you could set the
mustUnderstandattribute totruefor theconfirmationHeaderin the code fragment in The Actor Attribute:QName confirmation = new QName(nameSpaceURI, "confirmationDesk", nameSpace); SOAPHeaderElement confirmationHeader = header.addHeaderElement(confirmation); confirmationHeader.setActor( "http://gizmos.com/confirmations"); confirmationHeader.setMustUnderstand(true);This fragment produces the following XML:
<ns:confirmationDesk xmlns:ns="http://gizmos.com/NSURI" SOAP-ENV:actor="http://gizmos.com/confirmations" SOAP-ENV:mustUnderstand="1"/>You can use the
getMustUnderstandmethod to retrieve the value of themustUnderstandattribute. For example, you could add the following to the code fragment at the end of the preceding section:The relay Attribute
The SOAP 1.2 specification adds a third attribute to a
SOAPHeaderElement,relay. This attribute, likemustUnderstand, is a boolean value. If it is set totrue, it indicates that the SOAP header block must not be processed by any node that is targeted by the header block, but must only be passed on to the next targeted node. This attribute is ignored on header blocks whosemustUnderstandattribute is set to true or that are targeted at the ultimate receiver (which is the default). The default value of this attribute isfalse.For example, you could set the
relayelement totruefor thebillingHeaderin the code fragment in The Actor Attribute (also changingsetActortosetRole):QName billing = new QName(nameSpaceURI, "billingDesk", nameSpace); SOAPHeaderElement billingHeader = header.addHeaderElement(billing); billingHeader.setRole("http://gizmos.com/billing"); billingHeader.setRelay(true);This fragment produces the following XML:
<ns:billingDesk xmlns:ns="http://gizmos.com/NSURI" env:relay="true" env:role="http://gizmos.com/billing"/>To display the value of the attribute, call
getRelay:Using SOAP Faults
In this section, you will see how to use the API for creating and accessing a SOAP fault element in an XML message.
Overview of SOAP Faults
If you send a message that was not successful for some reason, you may get back a response containing a SOAP fault element, which gives you status information, error information, or both. There can be only one SOAP fault element in a message, and it must be an entry in the SOAP body. Furthermore, if there is a SOAP fault element in the SOAP body, there can be no other elements in the SOAP body. This means that when you add a SOAP fault element, you have effectively completed the construction of the SOAP body.
A
SOAPFaultobject, the representation of a SOAP fault element in the SAAJ API, is similar to anExceptionobject in that it conveys information about a problem. However, aSOAPFaultobject is quite different in that it is an element in a message'sSOAPBodyobject rather than part of thetry/catchmechanism used forExceptionobjects. Also, as part of theSOAPBodyobject, which provides a simple means for sending mandatory information intended for the ultimate recipient, aSOAPFaultobject only reports status or error information. It does not halt the execution of an application, as anExceptionobject can.If you are a client using the SAAJ API and are sending point-to-point messages, the recipient of your message may add a
SOAPFaultobject to the response to alert you to a problem. For example, if you sent an order with an incomplete address for where to send the order, the service receiving the order might put aSOAPFaultobject in the return message telling you that part of the address was missing.Another example of who might send a SOAP fault is an intermediate recipient, or actor. As stated in the section Adding Attributes, an actor that cannot process a header that has a
mustUnderstandattribute with a value oftruemust return a SOAP fault to the sender.A
SOAPFaultobject contains the following elements:
- A fault code: Always required. The fault code must be a fully qualified name: it must contain a prefix followed by a local name. The SOAP specifications define a set of fault code local name values, which a developer can extend to cover other problems. (These are defined in section 4.4.1 of the SOAP 1.1 specification and in section 5.4.6 of the SOAP 1.2 specification.) Table 18-1 lists and describes the default fault code local names defined in the specifications.
A SOAP 1.2 fault code can optionally have a hierarchy of one or more subcodes.
- A fault string: Always required. A human-readable explanation of the fault.
- A fault actor: Required if the
SOAPHeaderobject contains one or moreactorattributes; optional if no actors are specified, meaning that the only actor is the ultimate destination. The fault actor, which is specified as a URI, identifies who caused the fault. For an explanation of what an actor is, see The Actor Attribute.- A Detail object: Required if the fault is an error related to the
SOAPBodyobject. If, for example, the fault code isClient, indicating that the message could not be processed because of a problem in theSOAPBodyobject, theSOAPFaultobject must contain aDetailobject that gives details about the problem. If aSOAPFaultobject does not contain aDetailobject, it can be assumed that theSOAPBodyobject was processed successfully.Creating and Populating a SOAPFault Object
You have seen how to add content to a
SOAPBodyobject; this section walks you through adding aSOAPFaultobject to aSOAPBodyobject and then adding its constituent parts.As with adding content, the first step is to access the
SOAPBodyobject.With the
SOAPBodyobjectbodyin hand, you can use it to create aSOAPFaultobject. The following line of code creates aSOAPFaultobject and adds it tobody.The
SOAPFaultinterface provides convenience methods that create an element, add the new element to theSOAPFaultobject, and add a text node, all in one operation. For example, in the following lines of SOAP 1.1 code, the methodsetFaultCodecreates afaultcodeelement, adds it tofault, and adds aTextnode with the value"SOAP-ENV:Server"by specifying a default prefix and the namespace URI for a SOAP envelope.QName faultName = new QName(SOAPConstants.URI_NS_SOAP_ENVELOPE, "Server"); fault.setFaultCode(faultName); fault.setFaultActor("http://gizmos.com/orders"); fault.setFaultString("Server not responding");The SOAP 1.2 code would look like this:
QName faultName = new QName(SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE, "Receiver"); fault.setFaultCode(faultName); fault.setFaultRole("http://gizmos.com/order"); fault.addFaultReasonText("Server not responding", Locale.US);To add one or more subcodes to the fault code, call the method
fault.appendFaultSubcode, which takes aQNameargument.The
SOAPFaultobjectfault, created in the preceding lines of code, indicates that the cause of the problem is an unavailable server and that the actor athttp://gizmos.com/ordersis having the problem. If the message were being routed only to its ultimate destination, there would have been no need to set a fault actor. Also note thatfaultdoes not have aDetailobject because it does not relate to theSOAPBodyobject. (If you use SOAP 1.2, you can use thesetFaultRolemethod instead ofsetFaultActor.)The following SOAP 1.1 code fragment creates a
SOAPFaultobject that includes aDetailobject. Note that aSOAPFaultobject can have only oneDetailobject, which is simply a container forDetailEntryobjects, but theDetailobject can have multipleDetailEntryobjects. TheDetailobject in the following lines of code has twoDetailEntryobjects added to it.SOAPFault fault = body.addFault(); QName faultName = new QName(SOAPConstants.URI_NS_SOAP_ENVELOPE, "Client"); fault.setFaultCode(faultName); fault.setFaultString("Message does not have necessary info"); Detail detail = fault.addDetail(); QName entryName = new QName("http://gizmos.com/orders/", "order", "PO"); DetailEntry entry = detail.addDetailEntry(entryName); entry.addTextNode("Quantity element does not have a value"); QName entryName2 = new QName("http://gizmos.com/orders/", "order", "PO"); DetailEntry entry2 = detail.addDetailEntry(entryName2); entry2.addTextNode("Incomplete address: no zip code");See SOAPFaultTest.java for an example that uses code like that shown in this section.
The SOAP 1.1 and 1.2 specifications define slightly different values for a fault code. Table 18-1 lists and describes these values.
Retrieving Fault Information
Just as the
SOAPFaultinterface provides convenience methods for adding information, it also provides convenience methods for retrieving that information. The following code fragment shows what you might write to retrieve fault information from a message you received. In the code fragment,newMessageis theSOAPMessageobject that has been sent to you. Because aSOAPFaultobject must be part of theSOAPBodyobject, the first step is to access theSOAPBodyobject. Then the code tests to see whether theSOAPBodyobject contains aSOAPFaultobject. If it does, the code retrieves theSOAPFaultobject and uses it to retrieve its contents. The convenience methodsgetFaultCode,getFaultString, andgetFaultActormake retrieving the values very easy.SOAPBody body = newMessage.getSOAPBody(); if ( body.hasFault() ) { SOAPFault newFault = body.getFault(); QName code = newFault.getFaultCodeAsQName(); String string = newFault.getFaultString(); String actor = newFault.getFaultActor();To retrieve subcodes from a SOAP 1.2 fault, call the method
newFault.getFaultSubcodes.Next the code prints the values it has just retrieved. Not all messages are required to have a fault actor, so the code tests to see whether there is one. Testing whether the variable
actorisnullworks because the methodgetFaultActorreturnsnullif a fault actor has not been set.System.out.println("SOAP fault contains: "); System.out.println(" Fault code = " + code.toString()); System.out.println(" Local name = " + code.getLocalPart()); System.out.println(" Namespace prefix = " + code.getPrefix() + ", bound to " + code.getNamespaceURI()); System.out.println(" Fault string = " + string); if ( actor != null ) { System.out.println(" Fault actor = " + actor); }The final task is to retrieve the
Detailobject and get itsDetailEntryobjects. The code uses theSOAPFaultobjectnewFaultto retrieve theDetailobjectnewDetail, and then it usesnewDetailto call the methodgetDetailEntries. This method returns thejava.util.Iteratorobjectentries, which contains all theDetailEntryobjects innewDetail. Not allSOAPFaultobjects are required to have aDetailobject, so the code tests to see whethernewDetailisnull. If it is not, the code prints the values of theDetailEntryobjects as long as there are any.Detail newDetail = newFault.getDetail(); if (newDetail != null) { Iterator entries = newDetail.getDetailEntries(); while ( entries.hasNext() ) { DetailEntry newEntry = (DetailEntry)entries.next(); String value = newEntry.getValue(); System.out.println(" Detail entry = " + value); } }In summary, you have seen how to add a
SOAPFaultobject and its contents to a message as well as how to retrieve the contents. ASOAPFaultobject, which is optional, is added to theSOAPBodyobject to convey status or error information. It must always have a fault code and aStringexplanation of the fault. ASOAPFaultobject must indicate the actor that is the source of the fault only when there are multiple actors; otherwise, it is optional. Similarly, theSOAPFaultobject must contain aDetailobject with one or moreDetailEntryobjects only when the contents of theSOAPBodyobject could not be processed successfully.See SOAPFaultTest.java for an example that uses code like that shown in this section.