Advanced Validation in Oracle Service Bus 11g

When implementing Proxy Services in Oracle Service Bus (old BEA AquaLogic Service Bus), it’s important to think about validation of request data.

The OSB provides a Validate action that is a good mechanism to validate when the incoming request is conformant to the XML Schema defined in the service contract. However, using only this action is a very poor mechanism to effectively validate a request because of the following reasons:

  • The Validate action accepts validation only against XML Schema (xsd), that has a limited syntax and cannot support more complex validations or rules;
  • XML Schema cannot support business oriented validations, only structural validations (and very limited);
  • A good choice for better support in validation would be Schematron, but Oracle Service Bus 11g does not provide Schematron support yet (SOA Suite Mediator already supports, but OSB not);
  • If your company uses Canonical Model, is very common to use on all your xsd entities elements with minOccurs=”0” to get full reusability on the model, however is hard to validate when a specific service operation needs that an element be mandatory, and it weakens the service contract.

In this article I will show a good pattern that I use to do more advanced, business oriented validation in Oracle Service Bus using XQuery. Note that deep XQuery knowledge is not in the scope of this article, for more in-depth knowledge about XQuery you need to google (a good start can be found at http://www.w3schools.com/xquery/default.asp). I will predict that the reader has basic XPath concepts to find some nodes in a XML instance (more at http://www.w3schools.com/xpath/default.asp).

In this sample I will focus on the Proxy Service, so I will ignore any business-proxy-business transformations and the sample will show a simple Business Service virtualization through data by-pass, but implementing some complex validation on the request input that is the main objective of this article. You can download the example artifacts in the links bellow this article.

Let’s take a look into the sample request input message that we’ll use in this scenario:

<xsd:element name="OrderRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="FirstName" type="xsd:string" minOccurs="0" />
<xsd:element name="LastName" type="xsd:string" minOccurs="0" />
<xsd:element name="DocumentNumber" type="xsd:string" minOccurs="0" />
<xsd:element name="Email" type="xsd:string" minOccurs="0" />
<xsd:element name="Age" type="xsd:int" minOccurs="0"/>
<xsd:element name="Password" type="xsd:string" minOccurs="0" />
<xsd:element name="PasswordConfirmation" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Product">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ProductID" type="xsd:string" minOccurs="0" />
<xsd:element name="ProductRestriction" minOccurs="0">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="EVERYONE"/>
<xsd:enumeration value="OVER18"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Quantity" type="xsd:int" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

Observations:

  • Obviously this dummy contract is not real world and has no business relevance in an real order scenario;
  • The sample simulates the use of Canonical Model entities which sets minOccurs=”0” all over the place, but we know that some information is mandatory to our execute order operation.

The starting point of this sample is the structure shown below, that consists of:

  • One OSB Project named “AdvancedValidation” in the default configuration
  • The “OrderService-v1.wsdl” that represents the contract used in the sample
  • One Business Service named “OrderBusiness” created from the OrderService WSDL (I recommend you to generate a Mock Service in SoapUI tool to point this Business Service to and run your tests)
  • One Proxy Service named “OrderProxy” created from the OrderService WSDL, with a basic Message Flow that only routes the original request to the OrderBusiness in the execute operation.

By now the focus will be the Proxy Service message flow.

To start our robust validation let’s add the main pipeline pair and put the simple “Validate” action into the request branch, because the minimum expected is that the request message does comply to the XML Schema, and if don’t, the flow does not need to work anymore. In the XPath select the OrderRequest input element in the wizard, in variable body against its XML Schema definition present into the OrderService WSDL.

– XPath: ./ord:OrderRequest
– In Variable: body
– Against Resource: OrderService-v1.wsdl/OrderRequest
– Raise Error

Now comes the great part, lets prepare our advanced XQuery validation. Create a XSD named “validation.xsd” into xsd folder. This schema will hold the validation data though the flow. Use the following structure:

<xsd:complexType name="Validation">
<xsd:sequence>
<xsd:element name="Payload">
<xsd:complexType mixed="true">
<xsd:sequence>
<xsd:any/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ValidationErrorList" type="tns:ValidationErrorList"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ValidationErrorList">
<xsd:sequence>
<xsd:element name="ValidationError" type="tns:ValidationError" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ValidationError">
<xsd:sequence>
<xsd:element name="code" type="xsd:int"/>
<xsd:element name="message" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

The next step is create the XQuery transformation file to do the validation and fill the previous structure. The input of transformation is the OrderRequest schema and the output is the Validation schema.

– Source Types: OrderService-v1.wsdl/OrderRequest
– Target Types: validation.xsd/Validation

Now I suggest you to forget the visual editor. Drag-and-drop fields will not help you in more complex transformations (it’s not .NET rs) and will make a mess in the source code, so the better to do is reading some concepts of XQuery and then make your code by the hands. In this sample we’ll validate the following:
– The mandatory fields to the operation, that are: FirstName, DocumentNumber, Email, Age, ProductID and Quantity
– DocumentNumber: must contain only numbers
– Email: must comply to the regex (^[a-z0-9]+([\._-][a-z0-9]+)*@[a-z0-9_-]+(\.[a-z0-9]+){0,4}\.[a-z0-9]{1,4}$)
– Password and PasswordConfirmation: if filled, must be the same value
– ProductRestriction: if filled and value is “OVER18”, the customer must be 18+ years old
– Echo the full request message in the payload tag to use in the flow if necessary

(:: pragma bea:global-element-parameter parameter="$orderRequest" element="ns1:OrderRequest" location="../wsdl/OrderService-v1.wsdl" ::)
(:: pragma bea:schema-type-return type="ns0:Validation" location="../xsd/validation.xsd" ::)

declare namespace xf = "http://tempuri.org/AdvancedValidation/xq/RequestValidation/";
declare namespace ns1 = "https://gibaholms.wordpress.com/samples/wsdl/Order-v1.0";
declare namespace ns0 = "https://gibaholms.wordpress.com/samples/xsd/2011/11/validation";

declare function xf:RequestValidation($orderRequest as element(ns1:OrderRequest)) as element() {
<ns0:Validation>
<ns0:Payload>{$orderRequest/.}</ns0:Payload>
<ns0:ValidationErrorList>{
(: BEGIN - Required Field Validations 🙂
if (empty($orderRequest/ns1:Customer/ns1:FirstName/text())) then
<ns0:ValidationError>
<ns0:code>1</ns0:code>
<ns0:message>FirstName: Required Field</ns0:message>
</ns0:ValidationError>
else if (empty($orderRequest/ns1:Customer/ns1:DocumentNumber/text())) then
<ns0:ValidationError>
<ns0:code>2</ns0:code>
<ns0:message>DocumentNumber: Required Field</ns0:message>
</ns0:ValidationError>
else if (empty($orderRequest/ns1:Customer/ns1:Email/text())) then
<ns0:ValidationError>
<ns0:code>3</ns0:code>
<ns0:message>Email: Required Field</ns0:message>
</ns0:ValidationError>
else if (empty($orderRequest/ns1:Customer/ns1:Age/text())) then
<ns0:ValidationError>
<ns0:code>4</ns0:code>
<ns0:message>Age: Required Field</ns0:message>
</ns0:ValidationError>
else if (empty($orderRequest/ns1:Product/ns1:ProductID/text())) then
<ns0:ValidationError>
<ns0:code>5</ns0:code>
<ns0:message>ProductID: Required Field</ns0:message>
</ns0:ValidationError>
else if (empty($orderRequest/ns1:Product/ns1:Quantity/text())) then
<ns0:ValidationError>
<ns0:code>6</ns0:code>
<ns0:message>Quantity: Required Field</ns0:message>
</ns0:ValidationError>
(: END - Required Field Validations 🙂

else

(: BEGIN - Field Specific Validations 🙂
if (not(matches($orderRequest/ns1:Customer/ns1:DocumentNumber/text(), '^[0-9]+$'))) then
<ns0:ValidationError>
<ns0:code>7</ns0:code>
<ns0:message>DocumentNumber: Must Contain Only Numbers</ns0:message>
</ns0:ValidationError>
else if (not(matches($orderRequest/ns1:Customer/ns1:Email/text(), '^[a-z0-9]+([\._-][a-z0-9]+)*@[a-z0-9_-]+(\.[a-z0-9]+){0,4}\.[a-z0-9]{1,4}$'))) then
<ns0:ValidationError>
<ns0:code>8</ns0:code>
<ns0:message>Email: Not a Valid Email Format</ns0:message>
</ns0:ValidationError>
else if (not(empty($orderRequest/ns1:Customer/ns1:Password/text()))
and $orderRequest/ns1:Customer/ns1:Password/text() != $orderRequest/ns1:Customer/ns1:PasswordConfirmation/text()) then
<ns0:ValidationError>
<ns0:code>9</ns0:code>
<ns0:message>Password: Must Match PasswordConfirmation</ns0:message>
</ns0:ValidationError>
else if (not(empty($orderRequest/ns1:Product/ns1:ProductRestriction/text()))
and $orderRequest/ns1:Product/ns1:ProductRestriction/text() = 'OVER18'
and xs:int($orderRequest/ns1:Customer/ns1:Age/text()) < 18) then
<ns0:ValidationError>
<ns0:code>10</ns0:code>
<ns0:message>Customer Must Be OVER 18 Years</ns0:message>
</ns0:ValidationError>
(: END - Field Specific Validations 🙂

else ''
}</ns0:ValidationErrorList>
</ns0:Validation>
};

declare variable $orderRequest as element(ns1:OrderRequest) external;

xf:RequestValidation($orderRequest)

Now add an “Assign” activity to evaluate the validation XQuery against a newly created validation variable. We’ll use this variable in the error handler to gain access to the validation codes and messages.

– Expression: $body/ord:OrderRequest
– Variable: validation

We put now an “IF-ELSE” control to check if exists any validation errors. Don’t forget to add the validation xsd target namespace into “val” prefix.

– Add Namespace Definition: val – https://gibaholms.wordpress.com/samples/xsd/2011/11/validation
– Condition: count($validation/val:ValidationErrorList/val:ValidationError)

Then we raise a custom error with the code of our choice “BUS-1”. Note that this code will be used to capture the error in the handler, just remember that code.

– Code: BUS-1
– Message: Request Validation Error

Now the pipeline is finished. We now concentrate the fault treatment in a single error handler associated to the entire flow, adding a “IF-ELSE” action to act as a switch, verifying which of the code is present in the “fault” variable and manually throwing the typed faults declared in our service contract (OrderBusinessFault and OrderTechnicalFault). Obs.: the code “BEA-382505” refers to the internal OSB code that is used for “Validate” action errors.

– Condition: $fault/ctx:errorCode = ‘BEA-382505’


– Condition: $fault/ctx:errorCode = ‘BUS-1’

Now just add an Assign activity in the “if” branches that apply the typed soap faults. Just remember that the built-in $fault variable refers to the OSB infrastructure fault, and not to soap fault. So if you try to assign a soap fault directly to this variable, it just doesn’t work. To throw a soap fault you must assign the entire soap body. Don’t forget to add the “ord” prefix namespace pointing to “https://gibaholms.wordpress.com/samples/wsdl/Order-v1.0”. First let’s add the technical faults, that apply in case of schema validation failure (BEA-382505) and case else (any infrastructure error that we are not treating):

– Add Namespace Definition: ord – https://gibaholms.wordpress.com/samples/wsdl/Order-v1.0
– Variable: body
– Expression:

<soap-env:Body>
<soap-env:Fault>
<faultcode>soap-env:Server</faultcode>
<faultstring/>
<detail>
<ord:OrderTechnicalFault>
<ord:message>Schema Validation Failure</ord:message>
</ord:OrderTechnicalFault>
</detail>
</soap-env:Fault>
</soap-env:Body>

– Add Namespace Definition: ord – https://gibaholms.wordpress.com/samples/wsdl/Order-v1.0
– Variable: body
– Expression:

<soap-env:Body>
<soap-env:Fault>
<faultcode>soap-env:Server</faultcode>
<faultstring/>
<detail>
<ord:OrderTechnicalFault>
<ord:message>SOA Infrastructure Error</ord:message>
</ord:OrderTechnicalFault>
</detail>
</soap-env:Fault>
</soap-env:Body>

Now to add the treatment to our “BUS-1” fault, we can use the message applied by the validation XQuery (notice that any information that might be of interest can be propagated through the $validation variable). Don’t forget to declare the “val” namespace prefix to “https://gibaholms.wordpress.com/samples/xsd/2011/11/validation”:

– Add Namespace Definition: ord – https://gibaholms.wordpress.com/samples/wsdl/Order-v1.0
– Variable: body
– Expression:

<soap-env:Body>
<soap-env:Fault>
<faultcode>soap-env:Server</faultcode>
<faultstring/>
<detail>
<ord:OrderBusinessFault>
<ord:message>{$validation/val:ValidationErrorList/val:ValidationError[1]/val:message/text()}</ord:message>
</ord:OrderBusinessFault>
</detail>
</soap-env:Fault>
</soap-env:Body>

To finish our sample, just add the “Reply” activities to each “if” branch indicating that invocation returned “With Failure” (causes http 500 return code):

– Reply: With Failure

Well done! You can open the OSB console and do the tests in the Proxy Service. Input some data and observe the results. Don’t forget to start the Mock Service into SoapUI and point the Business Service endpoint to the mock address. An easy way to simulate a generic technical fault is stopping the mock service, crashing the business service like it was “unavailable”.Now I hope this article has helped you to doing more complex validations beyond the limits of xsd into Oracle Service Bus 11g.

Attachments

Source Code: https://github.com/gibaholms/articles/tree/master/Advanced_Validation_in_OSB

2 thoughts on “Advanced Validation in Oracle Service Bus 11g

  1. Fabian Gomez 09/25/2012 / 6:39 PM

    Great reference! thanks!

  2. pm stg 10/19/2016 / 7:00 AM

    Thank you very much for this topic. This is very useful for me. I tried step by step as the explanation. Even I still found some errors but this document made my beginning (about validation) is easier than trying alone.

    Thank you very much! 🙂

Leave a comment