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
<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>
- 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
- 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.
- 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)
- W3C – Simple Object Access Protocol 1.1 / SOAP Fault
- Oracle - Modeling Message Flow in Oracle Service Bus
- Eric Elzinga – OSB11g handling soap faults





Amar
12/04/2011 at 17:23
Nice Article.
jvzoggel
12/04/2011 at 22:51
Thanx, hope it helps.
DJ Kaiping
12/08/2011 at 20:46
What you have as the response is not a valid response. There are no namespaces defined.
Lets assume all the prefixes are defined properly for example:
…
Here soapenv is your prefix for the soap Envelope. When you are declaring your soap fault code it is using a different prefix: (soap-env)
soap-env:Client
which is done by your XQuery
declare namespace soap-env = “http://schemas.xmlsoap.org/soap/envelope/”;
When OSB Builds the outgoing XML response it is replacing all the soap-env prefixes with soap. Therefore it no longer matches.
To make it a valid response the prefix in the faultcode element should match the prefix declared for the soap envelope.
jvzoggel
12/14/2011 at 08:01
Hi DJ Kaiping,
I haven’t been able to test it yet, but I think what happened with the sourcecode in the blog is that the soapenv:Body namespace you see in the last response is actually declared in the element. I used SOAPui for testing but just copied the structure to my blog.
Besides that, one of the corrections I made to this blogpost was actually the XQuery where I added the namespace definition for faultcode (xmlns:soap-env=”http://schemas.xmlsoap.org/soap/envelope/”). In the beginning I just hardcoded soap:Server as a string. Until I detected that validation failed exactly for that reason: the receiver did not recognize the “soap” namespace used in the faultcode element and only was aware of “soap-env” or “soapenv”. What I think happened in the response here, is that I forgot to correct the response with the new XQuery output in faultcode.
So I think you are correct regarding the fact that the faultcode response output pasted here might not be correct, and that you need a “http://schemas.xmlsoap.org/soap/envelope/” namespace definition for both the soap envelop, and the soap-faultcode unless you make sure it’s using the identical namespace. Plus I agree that it would be great (and better looking) if you could use the same namespace for both of them. I haven’t looked into the fact if there is a function available for this in the OSB, I think there is but it’s on my 2do list. :)
However I do think that you are allowed to use different namespace definitions for body and faultcode, and the message would still validate after using my XQuery. But your comment made me doubt this, so I want to test this again … soon :)
Francisco
04/29/2013 at 20:14
Reaaaaally nice article
But im having problems:
replace in body “expected exactly one item, got 0 items” when I send $body as parameter (along the other 3)
when I send $body/* or $body/myNamespace:myRequestElement
I get ” bad value for type element {http://schemas.xmlsoap.org/soap/envelope/}Body”
because it was expecting a … but instead got
my setup is as shown in your images: error handler at proxy level before a branch
my osb version is Service Bus 11gR1
help please?
Francisco
04/29/2013 at 20:17
(second question with angle brackets encoded)
Reaaaaally nice article
But im having problems:
replace in body “expected exactly one item, got 0 items” when I send $body as parameter (along the other 3)
when I send $body/* or $body/myNamespace:myRequestElement
I get ” bad value for type element {http://schemas.xmlsoap.org/soap/envelope/}Body”
because it was expecting a <body><myRequestElement>… but instead got <myRequestElement>
my setup is as shown in your images: error handler at proxy level before a branch
my osb version is Service Bus 11gR1
help please?
Francisco
04/29/2013 at 20:46
manage to “make it work” the following way:
in the replace bind variables I send the following:
<root>{$body/*}</root>
<root>{$fault/*}</root>
<root>{$outbound/*}</root>
<root>{$inbound/*}</root>
in the xquery i did not use any schema:
declare variable $soapbody as element(*) external;
declare variable $osbfault as element(*) external;
declare variable $osbinbound as element(*) external;
declare variable $osboutbound as element(*) external;
everything “works” nicely, but I feel it’s needlessly untidy and that the osb makes my life hard just because it wants to do so
sending a <soapenv:Body> just fails cuz osb detects “static content”, so i send a generic root node.
If someone can explain to me why this stuff happens, ill be happy. ty!
Francisco
04/29/2013 at 22:11
solved by sending <nill/> in the binding of $osboutbound, since $outbound did not exist yet (validating request)
to do that i used anyType parameters instead of using schemas
managed to user the variables without the %lt;root> by sting variable xpath to . instead of ./myElement