So far, we have only concerned ourselves with the body of a SOAP message and its attachments, both of which are intended solely for the ultimate destination of the message. A message can also include headers, which contain information that is related to the routing or processing of the message, but which are not part of the payload itself. SOAP itself does not define any standard headers, but there are specific uses of SOAP that do, some of which are discussed in Chapter 4. In this section, we'll look in a generic way at the facilities provided by SAAJ for creating and handling headers, and defer detailed discussion of real-world uses of this facility until Chapter 4.
Although the examples that we have shown so far involve only a client and a web service to which the client sends a SOAP message, in practice, a SOAP message may pass through one or more intermediate systems before reaching its ultimate destination. For the purposes of this discussion, an intermediate system is not like a network router, which is concerned only with passing a transport-level packet from node to node until it reaches a destination, but an application-level entity that receives a SOAP message, examines it, and either handles it or forwards it to another SOAP receiver for further processing.
As an example of the use of an intermediate system, suppose that a business wants to make it possible for other businesses to place stock orders by building a SOAP message that contains a purchase order, in response to which it expects confirmation of the order and payment details. Before the order is processed, it might be necessary to check that the initiator is known to the company and has a purchase account to which the transaction can be billed. In order to implement this, the business might separate the handling of the account check from the handling of the purchase order, as follows:
When the client sends the purchase order, it is required to include a header that identifies it in a way that is determined by the business providing the service. In practice, this would probably involve the use of some secure mechanism, such as public key cryptography and the inclusion of a certificate to identify the client.
The system to which the purchase order is sent looks for this header, extracts the required information, and verifies it. If it is not valid, a fault is returned to the originator. Otherwise, the header is removed and perhaps replaced with one that provides information about the initiator that is in a form that is more useful to the business itself, such as account details.
The message is now forwarded to an internal system that handles the purchase order and ultimately returns a response to the order itself.
In the SAAJ API, each SOAPMessage can have a single SOAPHeader element. By default, this element is empty. Headers are created by adding XML elements to the SOAPHeader. Each such header element must be namespace-qualified and may contain an attribute that identifies the system that is intended to process it, known as the actor attribute. The value of this attribute is defined to be a URI and, in practice, it is often a URL, although it need not be. There are three cases to consider:
When there is no actor attribute, the header is deemed to be intended for the ultimate recipient of the message.
A header containing an actor attribute with this value (which a SAAJ application can conveniently refer to by using the constant value SOAPConstants.URI_SOAP_ACTOR_NEXT) is intended for the first system that receives it.
In this case, the header is intended for the system whose URI matches the value of the attribute.
When a SOAP message is received, the headers must be checked for any that are intended for the recipient. If there are any, the following rules apply:
The header must be removed from the message.
If the receiver understands the header, then it may process the header and act upon it. As a result, it may add one or more new headers to the message (which may be identical to the received header). If the header contains the mustUnderstand attribute from the SOAP envelope namespace, and the value of this attribute is 1, the receiver must process the header � or else return a fault to the originator if it chooses not to do so or cannot do so for some reason.
If the receiver does not understand a header that is addressed to it, it may choose to silently ignore it, unless the mustUnderstand attribute is present and has the value 1, in which case it must return a fault to the originator.
If a fault is returned as a result of the mustUnderstand attribute, then the fault code must have the value soap-env:MustUnderstand and the fault actor must be set to the URI of the system that generated the fault. A system that processes a header may also generate a fault for reasons that are related to the content of the header. In all cases, a fault that arises from header processing may not include a detail element.
Header elements are created using the SOAPHeader addHeaderElement( ) method, which requires a Name object from which the element name and its namespace are determined. The SOAP specification requires that header elements are namespaced-qualified. The addHeaderElement( ) method returns a newly created SOAPHeaderElement, which was added as a direct child of the SOAPHeader. Example 3-17 shows how to add a header that contains child elements for a username and a password.
SOAPHeader header = message.getSOAPPart().getEnvelope( ).getHeader( ); SOAPHeaderElement headerElement = header.addHeaderElement( soapFactory.createName("AuthInfo")); headerElement.addNamespaceDeclaration(null, "urn:headerDemo"); headerElement.setMustUnderstand(true); headerElement.addChildElement("UserName").addTextNode("JWSUserName"); headerElement.addChildElement("Password").addTextNode("JWSPassword");
The header element is called AuthInfo and its namespace is urn:headerDemo, which is an arbitrarily chosen URN. The following method call:
headerElement.addNamespaceDeclaration(null, "urn:headerDemo");
makes this namespace the default for this element and its subelements, so that the element names will appear without a namespace prefix. The use of the default namespace in this way is a convenience and not a requirement.
The SOAPHeaderElement interface has four methods that provide easy access to the SOAP actor and mustUnderstand attributes:
public void setActor(String actorURI); public String getActor( ); public void setMustUnderstand(boolean cond); public boolean getMustUnderstand( );
The code in Example 3-17 sets the mustUnderstand attribute to 1 (by virtue of the fact that its argument has the value true), but does not set the actor attribute at all, which implies that the header is to be processed by the ultimate recipient of the message, and must be understood and properly actioned or a fault returned. Here is what a SOAP message produced by this code looks like:
<soap-env:Envelope xmlns:soap-env= "http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header> <AuthInfo xmlns="urn:headerDemo" soap-env:mustUnderstand="1"> <UserName>JWSUserName</UserName> <Password>JWSPasswordJWSPassword</Password> </AuthInfo> </soap-env:Header> <soap-env:Body/> </soap-env:Envelope>
The default value of the mustUnderstand attribute is 0, which means that the header can be removed without being processed by its target.
When a SOAP message is received, there are three ways to handle the header entries that it contains once a reference to the SOAPHeader has been obtained:
Get an iterator over all of the child elements of the SOAPHeader. This returns all of the SOAPHeaderElement objects in the message.
Get an iterator over all of the headers intended for a given actor. This returns a subset of the SOAPHeaderElement objects in the message.
Get an iterator over all of the headers intended for a given actor and also remove them from the message.
A message recipient must process all headers for which the actor attribute is set to next or to its own URI, and must remove them once they have been processed. The SOAPHeader interface provides two methods that locate the headers intended for a given actor URI:
public Iterator examineHeaderElements(String actorURI); public Iterator extractHeaderElements(String actorURI);
The difference between these two methods is that the second removes all of the headers that appear in the Iterator from the message, whereas the first does not. Calling either of these methods with the argument SOAPConstants.URI_SOAP_ACTOR_NEXT returns all of the headers that have the actor attribute set to next and all of those that do not have an actor attribute.[12] As an alternative to these two methods, you can get an Iterator over all of the header entries without removing any of the them, using the getChildElements( ) method that SOAPHeader inherits from SOAPElement:
[12] It is not clear from the SAAJ specification whether this is the intended behavior or just a bug. In my opinion, it would be better if this call returned only those headers with the actor attribute set to next.
// Gets all the SOAPHeaderElements Iterator iter = soapHeader.getChildElements( );