To many times in Oracle Service Bus projects the design and creation of a generic soap fault error handling mechanism which is re-usable by all proxy services is forgotten. With this post I first want to show quickly what can happen without a decent soap fault structure in a project. And then show a basic soap fault mechanism which I often re-use in projects.
First some examples (also explained in more detail by Eric Elzinga here).
A proxy service with this basic Error Handler will log the $fault and reply-with-failure.

This solution however will inform for functional faults returned by the backend system (later on defined as Business Faults):
REQUEST:
<soapenv:Body>
<v1:getPersonRequest>
<v11:Person>
<v11:ID>2905</v11:ID>
<v11:Type>Customer</v11:Type>
</v11:Person>
</v1:getPersonRequest>
</soapenv:Body>
RESPONSE (returned by backend system, not Error Handler):
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>no person found</faultstring>
<detail>
<ns2:CRMSystem>
<ns2:ErrorCode>CRM0012</ns2:ErrorCode>
</ns2:CRMSystem>
</detail>
</soap:Fault>
</soap:Body>
However for technical (I will define these later on as runtime) faults in the OSB, the Error Handler will not alter the $body content and simply returns the same request message $body content. See below example for a response with a slightly altered and sabotaged request.
RESPONSE (returned by Error Handler due to OSB error):
<soapenv:Body>
<v1:getPersonRequest>
<v11:Person>
<v11:ID>2905</v11:ID>
<v11:Type>Customerrrrrrr</v11:Type>
</v11:Person>
</v1:getPersonRequest>
</soapenv:Body>
This means that the service consumer gets no SOAP Fault structure from the OSB and would only be able to determine due to the HTTP 500 transport error that something is wrong. Weblogic logfiles will show the runtime $fault which is logged by the Error Handler, but this usually can only be monitored by Administrators.
Fault in logfile:
<con:fault>
<con:errorCode>BEA-382505</con:errorCode>
<con:reason>OSB Validate action failed validation</con:reason>
<con:details>
<con1:ValidationFailureDetail>
<con1:message>string value 'Customerrrrrrr' is not a valid enumeration value .. </con1:message>
</con1:ValidationFailureDetail>
</con:details>
<con:location>
<con:node>RouteGetPersoon</con:node>
<con:path>request-pipeline</con:path>
</con:location>
</con:fault>
Getting started:
So enough with the examples and lets continue with a solution.
In OEPE we need add 3 files in the OSB Project.
- SOAP 1.1 Envelop schema (download here)
- MessageContext.xsd (extract from /WLHOME/Oracle_OSB1/lib/sb-schemas.jar)
- XQuery to construct the soap:Fault (create SoapFault.xq)
Your project should look like this:

The content of the XQuery:
xquery version "1.0" encoding "UTF-8";
(:: pragma bea:global-element-parameter parameter="$soapbody" element="soap-env:Body" location="soap-env.xsd" ::)
(:: pragma bea:global-element-parameter parameter="$osbfault" element="ctx:fault" location="MessageContext.xsd" ::)
(:: pragma bea:global-element-parameter parameter="$osbinbound" element="ctx:endpoint" location="MessageContext.xsd" ::)
(:: pragma bea:global-element-parameter parameter="$osboutbound" element="ctx:endpoint" location="MessageContext.xsd" ::)
(:: pragma bea:global-element-return element="soap-env:Fault" location="soap-env.xsd" ::)
declare namespace xf = "http://rubix.nl/common/Soap11Fault/";
declare namespace soap-env = "http://schemas.xmlsoap.org/soap/envelope/";
declare namespace ctx = "http://www.bea.com/wli/sb/context";
declare namespace tp = "http://www.bea.com/wli/sb/transports";
declare namespace http = "http://www.bea.com/wli/sb/transports/http";
declare function xf:SoapFault($soapbody as element(soap-env:Body),
$osbfault as element(ctx:fault),
$osbinbound as element(ctx:endpoint),
$osboutbound as element(ctx:endpoint))
as element(soap-env:Fault)
{
<soap-env:Fault>
{
if ($osbfault/ctx:errorCode="BEA-382505")
then <faultcode xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">soap-env:Client</faultcode>
else <faultcode xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">soap-env:Server</faultcode>
}
{
if ($soapbody/soap-env:Fault)
then <faultstring>{$soapbody/soap-env:Fault/faultstring/text()}</faultstring>
else <faultstring>Error in operation: {$osbinbound/ctx:service/ctx:operation/text()}</faultstring>
}
{
if ($osboutbound/ctx:transport/ctx:uri)
then <faultactor>{$osboutbound/ctx:transport/ctx:uri/text()}</faultactor>
else <faultactor>{fn:concat($osbinbound/ctx:transport/ctx:request/tp:headers/http:Host,$osbinbound/ctx:transport/ctx:uri)}</faultactor>
}
<detail>
{
if ($soapbody/soap-env:Fault)
then <business>{$soapbody/soap-env:Fault/detail}</business>
else <business/>
}
<runtime>{$osbfault}</runtime>
</detail>
</soap-env:Fault>
};
declare variable $soapbody as element(soap-env:Body) external;
declare variable $osbfault as element(ctx:fault) external;
declare variable $osbinbound as element(ctx:endpoint) external;
declare variable $osboutbound as element(ctx:endpoint) external;
xf:SoapFault($soapbody,
$osbfault,
$osbinbound,
$osboutbound)
As you can see the input variables for the XQuery are:
- soapbody = $body variable
- osbfault = $fault variable (remember that the osb $fault is not a valid soap:Fault structure)
- osbinbound = $inbound variable
- osboutbound = $outbound variable
The reason the 4 variables are completely send to the XQuery as input is due to the fact that it will be used as a generic component for all our proxy servics. With future enhancements you will have all information available to add more custom logic.
Added 03-FEB-2012:
There is some small remark to the namespace used in the <faultcode> element. If you validate the response you can detect that if your response message uses a <soap-env> namespace and you static use “soap:Server” in your response it will not validate. The prefix should be a valid namespace prefix in the message. For that reason I use a full namespace definitition in the <faultcode> element just to be sure. Another option is the use of this code, but I haven’t tested it yet myself in production.
<faultcode>{fn:concat(fn:substring-before(fn:name($soapbody),':'),':Client')}</faultcode>
/Added 03-FEB-2012:
In our Proxy Service we will add a Replace function in the Error Handler that will execute the SoapFault.xq

And the configuration of the XQuery will look something like this:

So we deploy/publish the configuration to our OSB server and execute the request again.
RESPONSE (wth Business Fault):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">soap-env:Server</faultcode>
<faultstring>no person found</faultstring>
<faultactor>http://mycrmserver:8888/personService/v1</faultactor>
<detail>
<business>
<ns2:CRMSystem>
<ns2:ErrorCode>CRM0012</ns2:ErrorCode>
</ns2:CRMSystem>
</business>
<runtime>
<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
<con:errorCode>BEA-380001</con:errorCode>
<con:reason>Internal Server Error</con:reason>
<con:location>
<con:node>RouteGetPerson</con:node>
<con:path>response-pipeline</con:path>
</con:location>
</con:fault>
</runtime>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Overview Business Fault:
- faultcode is from backend system
- faultstring is send from backend
- faultactor url is retrieved from the outbound variable
- <detail> contains a <business> which is the original <detail> from the backend
- <detail> contains a <runtime> which holds the OSB $fault
RESPONSE (wth Runtime Fault):
<soapenv:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<soapenv:Fault>
<faultcode xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">soap-env:Client</faultcode>
<faultstring>Error in operation: getPerson</faultstring>
<faultactor>osbserver:8011/getPersonService</faultactor>
<detail>
<business/>
<runtime>
<con:fault>
<con:errorCode>BEA-382505</con:errorCode>
<con:reason>OSB Validate action failed validation</con:reason>
<con:details>
<con1:ValidationFailureDetail>
<con1:message>string value 'Customerrrr' is not a valid enumeration .....</con1:message>
<con1:xmlLocation>
<v11:Type>Customerrrr</v11:Type>
</con1:xmlLocation>
</con1:ValidationFailureDetail>
</con:details>
<con:location>
<con:node>RouteGetPerson</con:node>
<con:path>request-pipeline</con:path>
</con:location>
</con:fault>
</runtime>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
Overview Runtime Fault:
- faultcode generated in XQuery due to BEA-38205 errorcode (means a Validate error). Check SOAP specifications for reason and other possible codes.
- faultstring generated in XQuery
- faultactor generated in XQuery and shows OSB hostname and which managed server (port)
- <detail> contains no <business/>
- <detail> contains a <runtime> which holds the OSB $fault
Summary:
- According to your needs you can alter the SoapFault XQuery and extend it with all sort of additional logic.
- You don’t necessary need the MessageContext.xsd, these variables can also be defined as anyXml
- I’m interested in feedback so leave a comment if you see issues, improvements, etc.
Updates:
- 2012-02-03: corrected namespaces in response message, see comment DJ Kaiping + new XQuery function used for creating FaultCode (see additional source code under XQuery source code)
Reference: