RSS

Using custom SOAP Faults in Oracle Service Bus

28 Jul

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

Replace Action

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

mapping the variables

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:

  1. faultcode is from backend system
  2. faultstring is send from backend
  3. faultactor url is retrieved from the outbound variable
  4. <detail> contains a <business> which is the original <detail> from the backend
  5. <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:
  1. faultcode generated in XQuery due to BEA-38205 errorcode (means a Validate error). Check SOAP specifications for reason and other possible codes.
  2. faultstring generated in XQuery
  3. faultactor generated in XQuery and shows OSB hostname and which managed server (port)
  4. <detail> contains no <business/>
  5. <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:
About these ads
 
13 Comments

Posted by on 28-07-2011 in Oracle, OSB, XQuery

 

Tags: , ,

13 responses to “Using custom SOAP Faults in Oracle Service Bus

  1. Amar

    04-12-2011 at 17:23

    Nice Article.

     
  2. DJ Kaiping

    08-12-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.

     
  3. jvzoggel

    14-12-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 :)

     
  4. Francisco

    29-04-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?

     
  5. Francisco

    29-04-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

      29-04-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

        29-04-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

         
  6. Jan-Willem Goedheer

    17-07-2013 at 16:51

    Hi Francisco,

    I had the same “expected exactly one item, got 0 items” error. Problem sees to be the osbinbound variable. This variable is not always set which caused my error. I resolved this by marking the osboutbound as optional. This requires two changes:

    1) declare variable $osboutbound as element(ctx:endpoint)? external;
    (mind the ? after the element

    2) 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)?)

    (again mind the ?)

     
    • Francisco

      18-07-2013 at 14:21

      Jan-Willem Goedheer, I did not know about optional parameters! Lol, I’m a big xquery newb. Great tip, thanks!

       
  7. Nishant

    23-08-2013 at 14:47

    hey i have a scenario where if any fault happen, proxy pipeline processing stop to that stage. but my requirment is to continue with processing….. any one have idea. thanks in advance.

     
    • Francisco

      26-08-2013 at 13:35

      Hey Nishant!
      Slap an error handler on the stage, and at the handler, a resume activity (blue play icon)

      More generic would be putting the handler at pipeline, business or even proxy level, but i dont know what the resume point would be in that case. Experiment!

       
  8. felmerien

    25-03-2014 at 18:19

    Interesting but we still have problem with fault handling in OSB and this solution don’t resolve one of them:
    when a proxy make a service callout with Payload Document on another proxy wich use this soluce option, you loose the body content so you loose the fault or soapfault access information on the fisrt proxy….any idea ?

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 354 other followers

%d bloggers like this: