SOAP errors are handled using a specialized envelope known as a Fault Envelope. If an error occurs while the server processes a SOAP message, it constructs a SOAP Fault and sends it back to the client. Here’s a typical SOAP 1.1 Fault:
<?xml version='1.0' encoding='UTF-8'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns: xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/ XMLSchema"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Server</faultcode> <faultstring>Test Fault</faultstring> <faultactor>/soap/servlet/rpcrouter</faultactor> <detail> <stackTrace>[SOAPException: faultCode=SOAP-ENV:Server; msg=Test Fault] at StockQuantity.getQty(StockQuantity.java:21) at java.lang.reflect.Method.invoke(Native Method) at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146) ... at org.apache.tomcat.util.ThreadPool$ControlRunnable.run( ThreadPool.java:501) at java.lang.Thread.run(Thread.java:498) </stackTrace> </detail> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
A SOAP Fault is a special element that must appear as an immediate
child of the SOAP body element. The
<faultcode>
and
<faultstring>
elements are required. The
<faultactor>
and
<detail>
elements are optional. Table 4-1 lists the possible values for the faultcodes
and their meanings.
Table 4-1. SOAP faultcodes
Faultcode |
Meaning |
---|---|
|
The SOAP node processing the request encountered a version mismatch. The namespace identifier of the SOAP envelope determines version compatibility. |
|
An immediate child element of the SOAP header (i.e.,
|
|
Introduced in SOAP 1.2 Working Draft 12/17/2001. It is an error for a SOAP 1.2 envelope to contain a DTD. |
|
The |
|
The content generated by the client is incorrect or malformed.
Therefore, resending the same data will result in the same error. In
SOAP 1.2, this fault is being changed to |
|
The content sent by the client is perfectly acceptable, but the SOAP
processor is unable to process it for some reason, such as an
unavailable service. Resending the message at a later time could
result in success. In SOAP 1.2, this fault is being changed to
|
The body and Fault elements are namespace-qualified to the
envelope’s namespace—for example,
<SOAP-ENV:body>
and
<SOAP-ENV:Fault>
. The
<faultcode>
element uses the local namespace
(it has no namespace prefix), and the
<faultcode>
value that the element contains
is a qualified name using the envelope’s
namespace—for example,
<faultcode>SOAP-ENV:Client</faultcode>
.
The SOAP Fault from the previous listing was achieved by making a
slight modification to the StockQuantity
service.
In Apache SOAP, having the service throw
an exception is all that’s needed to generate a
fault; Apache takes care of the rest:
public class StockQuantity{ public int getQty (String item) throws org.apache.soap.SOAPException { int inStockQty = (int)(java.lang.Math.random( ) * (double)1000); if (item.equalsIgnoreCase("Fail")) throw new org.apache.soap.SOAPException (org.apache.soap.Constants.FAULT_CODE_SERVER, "Test Fault"); return inStockQty; } ... }
In Apache SOAP 2.2, this code is all that is necessary to send a
complete SOAP 1.1 Fault message back to the sender. To view the full
output of the Fault message, redirect the
CheckStock
RPC call through the TunnelGui utility
by using the command:
java CheckStock -url http://localhost:5555/soap/servlet/rpcrouter -item Fail
In this command, 5555
is the port on which the
TunnelGui is listening. The RPC request and the corresponding SOAP
Fault can be viewed in the TunnelGui window, as shown in Figure 4-1.
The sending client can trap the Fault programatically and take
appropriate action. Apache
SOAP has a Fault
object that can be used to access
the pieces of the Fault message, as indicated in this excerpt from
CheckStock
:
//Invoke the service Response resp = call.invoke (url,"urn:go-do-this"); //Check the response if (resp != null) { if (resp.generatedFault ( )) { Fault fault = resp.getFault ( ); System.out.println ("Call failed due to a SOAP Fault: "); System.out.println (" Fault Code = " + fault.getFaultCode ( )); System.out.println (" Fault String = " + fault.getFaultString ( ));
While the ability to generate a fault by throwing an exception is
handy, you may want more control over what goes into a fault message.
For example, Apache SOAP, by default, puts the current stacktrace
into the <detail>
element of the SOAP fault.
That may not be what you want. We will explore how to build your own
Fault message in the context of the mustUnderstand
attribute.
To appreciate the meaning and role of the
mustUnderstand
or misUnderstood
fault codes, one must first understand the intent of the
mustUnderstand
attribute. This attribute can be
placed in any top-level header element. The presence of the
mustUnderstand
attribute with a value of
true
or 1
means that the header
element must be recognizable by the receiving SOAP processor. If the
SOAP processor does not recognize or know how to process the header
element, it must generate a Fault. We can generate a header element
with a mustUnderstand
attribute by adding the
following line of code to our
GenericHTTPSoapClient
:
// Create a header element in a namespace
org.w3c.dom.Element headerElement =
doc.createElementNS(URI,"jaws:MessageHeader");
headerElement.setAttributeNS(URI,"SOAP-ENV:mustUnderstand","1");
// Create subnodes within the MessageHeader
org.w3c.dom.Element ele = doc.createElement("From");
org.w3c.dom.Text textNode = doc.createTextNode("Me");
This code creates a SOAP envelope that looks like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <SOAP-ENV:Header> <jaws:MessageHeader xmlns:jaws="urn:http://oreilly/jaws/samples" SOAP-ENV:MustUnderstand="1" > <From>Me</From> <To>You</To> <MessageId>9999</MessageId> ... </jaws:MessageHeader> </SOAP-ENV:Header> <SOAP-ENV:Body> ... </SOAP-ENV:Body> </SOAP-ENV:Envelope>
This envelope requires the server to understand the
<MessageHeader>
element. Since the server
doesn’t understand these elements, it returns a SOAP
1.1 Fault message:
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:
xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/
XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:MustUnderstand</faultcode>
<faultstring>Unsupported header: jaws:MessageHeader</faultstring>
<faultactor>/examples/servlet/FaultServlet</faultactor>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The code used to generate this fault is in the following listing of
the FaultServlet
class.
FaultServlet
is a variation of our
HTTPReceive
class. As part of the
header’s processing, we look for the existence of a
mustUnderstand
attribute:
public class FaultServlet extends HttpServlet
{
...
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
...
// Get the header and check it for mustunderstand
Header header = env.getHeader( );
java.util.Vector headerEntries = header.getHeaderEntries( );
screenWriter.write("\nHeader==>\n");
for (java.util.Enumeration e = headerEntries.elements( );
e.hasMoreElements( );)
{
org.w3c.dom.Element el = (org.w3c.dom.Element)e.nextElement( );
org.apache.soap.util.xml.DOM2Writer.serializeAsXML(
(org.w3c.dom.Node)el, screenWriter);
// process mustUnderstand
String mustUnderstand=
el.getAttributeNS(Constants.NS_URI_SOAP_ENV,
"mustUnderstand");
screenWriter.write("\nMustUnderstand: "
+ mustUnderstand + "\n");
String tagName = el.getTagName( );
screenWriter.write("Tag Name: " + tagName + "\n");
FaultServlet
doesn’t support the
<MessageHeader>
tag; it supports only the
<IOnlyUnderstandThis>
tag. Therefore, we
must generate a fault when it sees the message header tag combined
with the mustUnderstand
attribute. To construct
the fault, we create a SOAPException
and use it to
create a new Fault
object:
if(!tagName.equalsIgnoreCase("IOnlyUnderstandThis")) { //generate a fault. screenWriter.write("Unsupported header: " + tagName + "\n"); screenWriter.write("Generating Fault....\n"); SOAPException se = new SOAPException(Constants.FAULT_CODE_MUST_UNDERSTAND, "Unsupported header: " + tagName); Fault fault = new Fault(se); fault.setFaultActorURI (request.getRequestURI ( )); String respEncStyle = Constants.NS_URI_SOAP_ENC;
Next, we create a Response
object and supply it
with the Fault
object that we created:
org.apache.soap.rpc.Response soapResponse =
new org.apache.soap.rpc.Response (
null, // targetObjectURI
null, // methodName
fault,
null, // params
null, // headers
respEncStyle, // encodingStyleURI
null); // SOAPContext
Finally, we create an Envelope
from the
Response
object and marshall it into the
PrintWriter
attached to the
servlet’s HTTPResponse
:
Envelope faultEnvelope = soapResponse.buildEnvelope( ); org.apache.soap.encoding.SOAPMappingRegistry smr = new org.apache.soap.encoding.SOAPMappingRegistry( ); PrintWriter resW = response.getWriter( ); faultEnvelope.marshall(resW, smr, soapResponse.getSOAPContext( )); response.setContentType(request.getContentType( )); response.setStatus(response.SC_INTERNAL_SERVER_ERROR); ... } }
Note that in the SOAP 1.2 effort, there has been much debate over
whether mustUnderstand
also means
“MustExecute” or
“MustProcess.”
SOAP 1.2 clarifies the use of the SOAP header in Fault processing.
The general idea is that the body of a Fault message should contain
only the errors that resulted from processing the body of the message
that caused the Fault. Likewise, detailed information about any
errors that occur as the result of processing a header block should
be placed in the header block of the resulting Fault message. The
<Fault>
and
<Faultcode>
elements still appear in the
body. However, the <Misunderstood>
element
in the header carries detailed information about which header element
could not be recognized.
The SOAP 1.2 Fault message (generated from not being able to
understand the <MessageHeader>
element in
our previous example) would look like this:
<env:Envelope xmlns:env='http://www.w3.org/2001/09/soap-envelope' xmlns:f='http://www.w3.org/2001/09/soap-faults' > <env:Header> <f:misunderstood qname="jaws:MessageHeader" xmlns:jaws="urn:http://oreilly/jaws/samples" /> </env:Header> <env:Body> <env:Fault> <faultcode>env:mustUnderstand</faultcode> <faultstring> One or more mandatory headers not understood </faultstring> </env:Fault> </env:Body> </env:Envelope>
SOAP 1.2 adds an additional set of fault codes. These RPC fault codes use the new namespace identifier http://www.w3.org/2001/09/soap-rpc with the namespace prefix of rpc:. The new codes are listed in Table 4-2.
Table 4-2. SOAP 1.2 RPC fault codes
Get Java Web Services now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.