Advertisements
RSS

Category Archives: XQuery

Use XQuery with fn-bea:lookupBasicCredentials

This is an example XQuery which uses 2 simple custom functions and the fn-bea:lookupBasicCredentials to retreive information from a Service Account in the Oracle Service Bus. This to prevent the usage of cleartext passwords in the Oracle Service Bus.


declare namespace funcRBX = "http://www.rubix.nl/local"

declare function funcRBX:getUsername($inputPath as xs:string) as xs:string
{
  let $data := fn-bea:lookupBasicCredentials($inputPath)
  return
    if (exists($data/con:username/text()))
    then $data/con:username/text()
    else ""
};

declare function funcRBX:getPassword($inputPath as xs:string) as xs:string
{
  let $data := fn-bea:lookupBasicCredentials($inputPath)
  return
    if (exists($data/con:password/text()))
    then $data/con:password/text()
    else ""
};

declare function xf:defineConnection() as element(ns0:ConnectionHeader)
{
  <ns0:Connection>
    <ns0:host>ftp.myserver.locaL</ns0:host>
    <ns0:port>21</ns0:port>
    <ns0:username>{funcRBX:getUsername("MyOSBProject/serviceaccounts/SA_FTP")}</ns0:username>
    <ns0:password>{funcRBX:getPassword("MyOSBProject/serviceaccounts/SA_FTP")}</ns0:password>
    <ns0:protocol>FTP</ns0:protocol>
  </ns0:Connection>
};

hope it helps

Advertisements
 
1 Comment

Posted by on 02-02-2012 in OSB, XQuery

 

Tags: , ,

Using custom SOAP Faults in Oracle Service Bus

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:
 
14 Comments

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

 

Tags: , ,

OSB Xquery function if-absent example

Since I’m on a roll with Xquery functions let’s add an addition. Especially since the earlier example is a bit large for people looking for a simple example.

So maybe this helps better to get an understanding. After working with Tibco BusinessWorks for some years I used a lot off the internal tib:functions supplied out-of-the-box and always found it a disappointment that Oracle (read BEA here) didn’t offer such a nice catalog to help you out. So over time I added a lot off these functions to my own library.

For example the function if-absent helps me to prevent all these “is-value1-there-then-use-it-and-if-not-i-want-you-to-use-value2” checks.

declare function funcRBX:if-absent($arg as item()* , $value as item()* ) as item()*
{
  if (exists($arg)) then $arg else $value
};

Works like a charm … ;-)

 
Leave a comment

Posted by on 17-03-2011 in Oracle, OSB, XQuery

 

Tags: , ,

OSB Xquery function DateTime conversion

>Yesterday at the Oracle SOA and E2.0 Partner Community Forum in Utrecht I had a good talk with Edwin, Guido and Eric about OSB and one of the topics was our experience with Xquery functions in the OSB. So I mentioned some examples I used in the past and decided to post them on my blog when I had the time.

So here is one to start.

First we declare a nice namespace for our function ‘library’ in the top of our XQuery file.

declare namespace funcRBX = "http://www.rubix.nl";

Then we create our function, the following example is used for altering the format of Date and Time values to the specific format that Siebel expects. The code should be clear enough I hope with the comments in them. This is off course just one of the possible ways to do it and contains some specific environment checks I had to use. For instance this function was also used when the source is not XML but files (the old BEA MFL way of working) where day and month could have a value of 00 and we had to alter this to 01 as a business requirement.

declare function funcRBX:siebelDateTime ( $inputDate as xs:string?, $inputTime as xs:string?) as xs:string
{
 if (($inputDate = "" or empty($inputDate)) and ($inputTime = "" or empty($inputTime)))     (: no input :)
 then ""
 else if ($inputTime = "" or empty($inputTime)) then              (: only inputDate received :)
  concat(
  if (substring(data($inputDate),5,2) = "00") then ("01") else substring(data($inputDate),5,2)  (: check if day is not 00 :)
  ,"/",if (substring(data($inputDate),7,2) = "00") then ("01") else substring(data($inputDate),7,2) (: check if month is not 00 :)
  ,"/",substring(data($inputDate),1,4))
 else if ($inputDate = "" or empty($inputDate)) then              (: only inputTime received :)
  concat(
  substring(data($inputTime),1,2)
        ,":",substring(data($inputTime),3,4)
        ,":00")
 else                          (: both inputDate and inputTime received :)
  concat(
  if (substring(data($inputDate),5,2) = "00") then ("01") else substring(data($inputDate),5,2)  (: check if day is not 00 :)
        ,"/",if (substring(data($inputDate),7,2) = "00") then ("01") else substring(data($inputDate),7,2) (: check if month is not 00 :)
        ,"/",substring(data($inputDate),1,4)
        ," ",substring(data($inputTime),1,2)
        ,":",substring(data($inputTime),3,4)
        ,":00")
 };

And then down in the XQuery mapping you can call your function like this:

{
 funcRBX:siebelDateTime($someInputDate)
}

Due to the the fact that the input parameters are optional (the ? character after the declaration) you can leave the second input for time out or use ” as input.

 
5 Comments

Posted by on 17-03-2011 in Oracle, OSB, Siebel, XQuery

 

Tags: , , ,

Oracle Service Bus and Siebel UserNameToken

In this case we need to publish messages from the OSB 10.3.1 to Siebel 8.1.1.2 where Siebel supports different options for authentication of incoming HTTP webservice requests.

  • passing the user name and password in the URL (basic authentication)
  • A sort of custom Siebel Soap Header token like:
<soap:Header>
     <UsernameToken xmlns="http://siebel.com/webservices">user</UsernameToken>
     <PasswordText xmlns="http://siebel.com/webservices">hello123</PasswordText>
     <SessionType xmlns="http://siebel.com/webservices">None</SessionType>
</soap:Header>
  • And last but not least; Siebel claims to support the WS-Security UserName Token mechanism. However Siebel only supports the PasswordText option so the password will still be send in clear text.

We concluded that the URL or the custom header option was not acceptable due to the customers security requirements. The only acceptable solution for them would be the WS-security UsernameToken. Within the OSB this can be arranged by using a WS-policy and a Service Account and connect them to the outgoing business service. Since the password will still be in cleartext, we will use SSL to protect the password on it’s journey over the dark trenches of our WAN.

So here we start off:

1st the Siebel configuration for inbound services. Make sure the authentication type is set to username/password clear text.


2nd the WS-policy used (very basic):


<wsp:Policy wsu:Id="WS-Policy-Siebel"
  xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <wssp:Identity xmlns:wssp="http://www.bea.com/wls90/security/policy">
    <wssp:SupportedTokens>
      <wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken">
        <wssp:UsePassword Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"/>
      </wssp:SecurityToken>
    </wssp:SupportedTokens>
  </wssp:Identity>
</wsp:Policy>

Attach the policy to the OSB business service together with the service account and go !!! 
With tcpmon we catch the SOAP-header and it looks like:

<soapenv:Header>
  <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
    <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="unt_hzo9Xh2IiCqSYGaH">
      <wsse:Username>SADMIN</wsse:Username>
      <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">SADMIN</wsse:Password>
    </wsse:UsernameToken>
  </wsse:Security>
</soapenv:Header>

Bummer, we get our 1st error. Well let’s say that good things always take some extra effort.
The error response from Siebel:
<Body>
  <Fault>
    <faultcode>SOAP-ENV:MustUnderstand</faultcode>
    <faultstring>Unable to process SOAP Header child element 'wsse:Security' with 'mustUnderstand="1"'((SBL-EAI-08000)</faultstring>
      <detail>
          <siebelf:siebdetail xmlns:siebelf="http://www.siebel.com/ws/fault">
          <siebelf:logfilename>EAIObjMgr_nld_0014_14680077.log</siebelf:logfilename>
          <siebelf:errorstack>
          <siebelf:error>
          <siebelf:errorcode>SBL-EAI-08000</siebelf:errorcode>
          <siebelf:errorsymbol/>
          <siebelf:errormsg>Unable to process SOAP Header child element 'wsse:Security' with 'mustUnderstand="1"'((SBL-EAI-08000)</siebelf:errormsg>
          </siebelf:error>
          </siebelf:errorstack>
          </siebelf:siebdetail>
      </detail>
    </Fault>
</Body>

The error might let you think that Siebel simply fails on the mustUnderstand attribute which would force it to understand the SOAP header. However this is not the whole case.

After some debugging and using the Assign action in the OSB to manually build the SOAP-header with the WS-security definition it was possible to send out a UserName token Siebel accepted.

After this my conclusion is that Siebel apparently expects another wsse namespace then the OSB is generating.

Siebel requires the old wsse IBM/Microsoft/VeriSign definition of http://schemas.xmlsoap.org/ws/2002/04/secext&#8221; while the OSB uses the newer namespace http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd&#8221; which was changed to match the OASIS specifications after their approval of WS-security as a standard in 2004.

After contact with Oracle Support I received confirmation from the Siebel Team that this is a limitation from Siebel, as it is not accepting the latest WS-Security definition. The option they give you is that for now it’s the caller’s responsibility to change the header to conform to the namespace Siebel is expecting.

So a new challenge for the OSB. The project looks something like this:

The solution for it is the fn-bea:lookupBasicCredentials function. We use an Assign Action to get the expression
fn-bea:lookupBasicCredentials(‘myRubixProject/security/SA.Siebel.MyRubixProject’)
result in a variable named $varBasicCredentials with the following content:

<con:UsernamePasswordCredential xmlns:con="http://www.bea.com/wli/sb/services/security/config">
  <con:username>SADMIN</con:username>
  <con:password>SADMIN</con:password>
 </con:UsernamePasswordCredential></pre>
We edit the Replace action to use the username and password elements.
<pre class="brush: xml"><soapenv:Header xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/04/secext" soapenv:mustUnderstand="1">
   <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsse:Username>{$varBasicCredentials/con:username/text()}</wsse:Username>
     <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{$varBasicCredentials/con:password/text()}</wsse:Password>
    </wsse:UsernameToken>
   </wsse:Security>
 </soapenv:Header>

Don’t forget to add the con namespace to the Replace action.

Finally, succes at last !!! :)
Off course this is just a workaround to make sure that Siebel and OSB speak te same “namespace language”. Let’s hope that Siebel 11g fixes this. After all, when it runs on Weblogic it must be good ;-)

Special thanks to Anuj Dwivedi for pointing me out the posibilities of the  fn-bea:lookupBasicCredentials function

 
Leave a comment

Posted by on 01-04-2010 in Oracle, OSB, Siebel, WS-Security, XQuery

 

Tags: , , ,