3.4 SOAP Fault Handling

   

3.4 SOAP Fault Handling

A SOAP fault is a specific type of XML element placed in the body of a reply message to convey status information or, more usually, to report a fault while processing the original request. SAAJ represents a fault using the SOAPFault interface that, since it must be a top-level element in the SOAP body, is derived from SOAPBodyElement (see Figure 3-4). The SOAP specification requires that there be no more than one SOAP fault in the message body. Faults can be mixed with other top-level elements if necessary.

SOAP faults have three attributes that can be set to provide information about the condition being reported . Each of these attributes has corresponding methods in the SOAPFault interface to allow its value to be set or retrieved:

Attribute

SOAPFault methods

Description

Fault code

getFaultCode( )

setFaultCode( )

A code that indicates the reason for reporting the fault. Applications may define their own private fault codes or use the set of standard values defined by the SOAP 1.1 specification (described later). All fault codes must be namespace-qualified.

Fault string

getFaultString( )

setFaultString( )

A human-readable description of the reason for the fault. The value of this attribute typically is written to a log file or displayed in a user interface.

Fault actor

getFaultActor( )

setFaultActor( )

The URI of the participant in the SOAP message path (referred to in the SOAP specification as an actor ) that caused the fault element to be generated.

3.4.1 Fault Codes

SOAP defines a small set of fault codes that can be used as appropriate. All of these fault codes belong to the same namespace as the SOAP envelope itself. The SAAJ API does not define constant values for these codes, so the application code must hardcode them.

Fault code

Description

VersionMismatch

This code indicates that a SOAP message was received in which the namespace for the SOAP envelope did not match the version of SOAP that the receiver supports. At the time of this writing, this means that the namespace was something other than http://schemas.xmlsoap.org/soap/envelope/.

MustUnderstand

This error is generated when a SOAP actor receives a message containing a header that contains the mustUnderstand attribute with value 1, which it does not know how to process. For further details, see Section 3.7, later in this chapter.

Client

Indicates that the message was improperly constructed by its originator. It is possible to more closely specify the nature of the problem (albeit in an application-dependent way) by appending one or more qualifiers to the fault code. For example, the book image service uses the fault code Client.UnknownRequest if it receives a message in which the top-level element of the body is not one of the requests that it recognizes.

Server

Indicates that a processing error occurred within the server. This might, for example, be because the resources required within the server to handle the message are not currently available. This code should not be used when the cause of the error is related to content or construction of the message. Like the Client fault code, it is common to return a more specific error code by appending a qualifier.

3.4.2 Fault Actor

Although in this chapter our examples have involved only a client application and the web service that it is using, in many cases, a SOAP message passes through and is processed by intermediate systems before reaching its final destination. These intermediate systems, referred to as actors, usually perform message validation or processing based on the content of SOAP headers attached to the message. If an error is detected during header processing, the actor may return a fault to the message originator, and must identify itself as the source of the report by using the fault actor attribute, which is technically a URI, but is typically the URL of the system concerned .

When an error is detected at the system that is the ultimate destination of the message, the fault actor attribute need not be set. Note, however, that the SAAJ API does not allow you to set this attribute to null ” instead, you need to use an empty string to indicate that the fault actor is not specified.

3.4.3 Fault Details

In addition to the three attributes just described, a SOAP fault also requires a detail element to be present if the fault relates to the content of the message body. No detail element is permitted if the fault relates to header processing. The content of the detail element is application-dependent, but the top-level element must be namespace-qualified.

3.4.4 Creating Fault Elements

The book image web service generates a fault in response to a number of conditions. Later in this chapter, you'll see how the client application and the web service use SOAP attachments to transfer the cover image for one or more books, given a request containing the book titles. If the web service is asked for the image of a book whose title it does not recognize, then it generates a fault. To see an example of this, start the client using the command line:

 ant run-client-debug 

Now select No Such Book in the book title list and press the "Fetch" button. The client requests the book cover for a book whose title the web service does not know. In the command window from the start of this example, you'll see the server's reply message, which contains a SOAP fault entry in the body:

 <?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/
     envelope/"
        xmlns:tns="urn:jwsnut.bookimageservice" 
        xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        soap-env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap-env:Body>  <soap-env:Fault>   <faultcode>soap-env:Client.Title</faultcode>   <faultstring>Unknown title</faultstring>   <faultactor>urn:jwsnut.bookimageservice</faultactor>   <detail>   <tns:BookFaultDetail>No Such Book</tns:BookFaultDetail>   </detail>   </soap-env:Fault>  </soap-env:Body>
</soap-env:Envelope> 

As you can see, the Fault element (which is qualified with the SOAP envelope namespace) contains child elements for the fault code, a fault string, the fault actor, and the fault detail:

Fault code

Since the error in this case results from an invalid book title in the received request, the web service uses a fault code from the soap-env:Client set, and qualifies it with the word "Title" to indicate that the error was with the book title. Obviously, for this to be of any use, the client and the service both need to be aware of the possible error codes and their meanings.

Fault string

This is simply a text message that describes the error, for the purposes of logging.

Fault actor

In this case, the fault was detected by the ultimate destination of the message, so it was not mandatory to specify the fault actor. The value supplied here is the URN of the web service.

Detail

The detail element contains an application-defined child element called BookFaultDetail that provides further information regarding the error. This element must be namespace-qualified, and it is appropriate here to qualify it with the URN of the web service. Child elements (if there are any) of the BookFaultDetail element do not need to be namespace-qualified. Applications are free to include any kind of private content in the detail part.

The code used to generate this fault is shown in Example 3-10.

Example 3-10. Creating a SOAP fault element
 /**
 * Creates a fault in the reply body.
 */
private void createFault(SOAPBody replyBody, String faultCode, 
     String faultString, String faultActor, String detailString)
     throws SOAPException {
                                    
    SOAPFault fault = replyBody.addFault(  );
    fault.setFaultCode(faultCode);
    fault.setFaultString(faultString);
    fault.setFaultActor(faultActor);
    if (detailString != null) {
        Name detailName = soapFactory.createName("BookFaultDetail", 
          SERVICE_PREFIX, SERVICE_URI);
        Detail detail = fault.addDetail(  );
        DetailEntry detailEntry = detail.addDetailEntry(detailName);
        detailEntry.addTextNode(detailString);
    }
} 

The Fault element itself is created using the SOAPBody addFault( ) method:

 public SOAPFault addFault(  ) throws SOAPException; 

Three setter methods are used to set the fault code, fault string, and fault actor. If this method is used to report a fault during header processing, it is not permissible to include a detail element; therefore, the detailString argument is null . In this case, however, the problem is with the message body, so a detail entry is mandatory. To create a detail entry and add it to the SOAPFault object, the SOAPFault addDetail( ) method is used:

 public Detail addDetail(  ) throws SOAPException; 

Alternatively, a Detail object can be obtained from SOAPFactory , in which case it must be explicitly added to the SOAPFault object:

 Detail detail = soapFactory.createDetail(  );
fault.addChildElement(detail); 

Finally, to add a top-level element to the Detail object, use its addDetailEntry( ) method, passing it the fully qualified element name (in this case, tns:BookFaultDetail ):

 public DetailEntry addDetailEntry(Name name) throws SOAPException; 

DetailEntry is a SOAPElement ; therefore, further elements can be nested inside it (and need not be namespace-qualified), or text can be added, as shown in Example 3-10.

It is also permissible to add other application-defined elements directly to the SOAPFault , rather than inside the Detail element. These elements can be created using the addChildElement( ) methods that SOAPFault inherits from SOAPElement (see Figure 3-4 for the inheritance hierarchy of SOAPFault ) or using the SOAPFactory class in the usual way. All elements added to the SOAPFault element are of type SOAPFaultElement (from which Detail is derived) and must be namespace-qualified.

The SOAP specification requires that a SOAP reply message containing a Fault element must, when sent over HTTP, have the HTTP response code 500, which indicates an internal server error. SAAJServlet fulfills this requirement by inspecting the message returned by the onMessage( ) method and setting the appropriate response code if the body contains a fault ” see Example 3-1.

To make it simple to handle SOAP faults, the SOAPBody interface provides two convenience methods:

 public boolean hasFault(  );
public SOAPFault getFault(  ); 

These methods remove the need for application code to search the body looking for a SOAPFault element, which may not be the first element in the SOAP body.