Wednesday, December 21, 2016

SOAP, REST XML and JSON Custom Faults in OSB 12c

One of the amazing and useful feature in 12c is the automatic JSON exposure of REST based services. In 11g, we always had to write custom codes to convert JSON to XML and XML to JSON and pass to requested systems. But 12c makes it easy.

Most of the times, we get requirements to expose a service as SOAP and REST (including JSON), which is easy in 12c world. But how does a single service handle custom errors for all types of protocols.


In this blogpost, we will see how custom errors are returned in 12c for SOAP, REST XML and JSON. 


We will begin with WSDL and XSDs for the OSB service - PGRaiseErrorProject.


The request and response schemas have pretty simple elements. Request takes 2 double fields as input and Response gives a status as output.
































Following Custom Fault xsd will be used.





















The abstract and concrete wsdls looks as follows:

Abstract wsdl - ErrorServiceV1Abstract.wsdl


<?xml version= '1.0' encoding= 'UTF-8' ?>

<wsdl:definitions
     name="ErrorService"
     targetNamespace="http://pgservice.com/services/ErrorService/V1"
     xmlns:tns="http://pgservice.com/services/ErrorService/V1"
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:fault="http://pgservice.com/errortest/customfault/V1"
     xmlns:ser="http://pgservice.com/errortest/V1"
    >
    <wsdl:types>
        <xsd:schema targetNamespace="http://pgservice.com/errortest/V1">
            <xsd:include schemaLocation="../xsd/RequestSchema.xsd"/>
            <xsd:include schemaLocation="../xsd/ResponseSchema.xsd"/>
        </xsd:schema>
        <xsd:schema targetNamespace="http://pgservice.com/errortest/customfault/V1">
            <xsd:include schemaLocation="../xsd/CustomFault.xsd"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="ErrorServiceRequestMsg">
        <wsdl:part name="parameters" element="ser:testRequest"/>
    </wsdl:message>
    <wsdl:message name="CustFaultMsg">
        <wsdl:part name="parameters" element="fault:custFault"/>
    </wsdl:message>
    <wsdl:message name="ErrorServiceResponseMsg">
        <wsdl:part name="parameters" element="ser:testResponse"/>
    </wsdl:message>
    <wsdl:portType name="ErrorServicePortType">
        <wsdl:operation name="checkStatus">
            <wsdl:input message="tns:ErrorServiceRequestMsg"/>
            <wsdl:output message="tns:ErrorServiceRequestMsg"/>
            <wsdl:fault name="custFault" message="tns:CustFaultMsg"/>
        </wsdl:operation>
    </wsdl:portType>

</wsdl:definitions>

Concrete wsdl - ErrorServiceV1SOAP.wsdl


<wsdl:definitions name="ErrorService" targetNamespace="http://pgservice.com/services/ErrorService/V1"

                  xmlns:tns="http://pgservice.com/services/ErrorService/V1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               >
  <wsdl:import location="ErrorServiceV1Abstract.wsdl" namespace="http://pgservice.com/services/ErrorService/V1"/>
  <wsdl:binding name="ErrorServiceSOAP" type="tns:ErrorServicePortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="checkStatus">
      <soap:operation soapAction="http://pgservice.com/services/ErrorService/V1/Test"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="custFault">
        <soap:fault use="literal" name="custFault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ErrorService">
    <wsdl:port name="ErrorServicePort" binding="tns:ErrorServiceSOAP">
      <soap:address location="http://pgservice.com/services/ErrorServiceV1"/>
    </wsdl:port>
  </wsdl:service>

</wsdl:definitions>

Next we would start with the OSB project. All the above schemas and wsdls have been placed in xsd and wsdl folders under Resources in the project as follows - 


















In the xsd folder, you would have observed that there are two more schemas. SOAPEnvelope.xsd is the regular SOAP schema and MessageContext.xsd has the structure of the $fault variable in OSB. In order to create xqueries, these can be used.

The next step is to create SOAP proxy service. Based on ErrorServiceV1SOAP wsdl, create a SOAP proxy and pipeline.  Provide the Proxy service name and pipeline name.




















Once the proxy and pipeline are created. Right click on the pipeline. You will find an option to expose the service as Rest. Click on it.













You will now get a new window to enter the resource path and operation details. By default, the operation from the wsdl will be populated  in the operation bindings sections.  

Click on the first + symbol and create a new resource path - /checkStatus - here.

Then select the operation from Operation bindings and select the pencil symbol to edit.
Rest Binding


This will show the Request payload section. Only XML is selected by default. Select the JSON check box as well. 
JSON Option















Similarly in the response as well, select the JSON checkbox. 
In the response you would see that the custFault variable from the wsdl has got auto-populated in the fault bindings, which means REST proxy will allow faults of custFault type to be returned. 


Select the fault and click on edit, it will allow you to select JSON output for fault as well.


















Once the above changes are done, the project looks as follows.
SOAP and REST Proxy














Now, lets write the logic in the pipeline. Double click the pipeline.
Logic goes as follows - 
If the difference between totalAmount and paidAmount is less than 1000, a success response with message is returned.
If the difference is greater than 1000, a raise error is done.
The pipeline looks as follows after implementing the above logic.
Pipeline


The following code and message will be returned for the business error.
Raise Error
















The next step is to handle the error in a way that it abides by the SOAP wsdl contract and also follows REST XML and JSON convention in the same way.

For error handling, we will add an error handler for the Pipeline pair. 


1. First step would be to add a Reply activity and check the option as "With Failure". This will allow error response to be sent.













2. Second step is to populate the Custom Fault that we have in the wsdl. I added an assign activity in the error handler and populated the custFault var using $fault as follows.




















3. CustFaultVar now has the Custom Fault populated. How do we return it in a way that the consuming system gets appropriate message for SOAP, REST XML and JSON?


As we all know any fault for a SOAP service should be returned in SOAP Fault format. Else, it is not considered to be valid. So, here as well, we are supposed to populate the SOAPFault and return as response body.

Any custom fault mentioned in the wsdl should be placed under detail tag of the SOAPFault.

4. I have added a replace activity and replaced the body with SOAP fault as below. The faultcode has been hardcoded. You can populate any other code.

The detail tag has the CustFaultVar embedded.























Rest adapter is smarter than http adapter. When the above fault response is returned from Pipeline to the Rest adapter, the adapter does multiple validations.


1. It checks if the Fault received from pipeline is SOAP fault or not ?


2. Then it checks if the fault structure mentioned in the wsdl (here custFault) and the fault structure in the detail tag of SOAPFault are same. 


3. If yes, Rest adapter will internal extract the structure present in the detail tag and return as response in XML/JSON formats.


4. It checks if the Custom Fault populated in the detail section of response is adhering to the Custom Fault XSD like mandatory fields, field types, etc.

  
If any of the above mentioned items fail, Rest adapter will throw the following error.
<err:RestError xmlns:err="http://www.bea.com/wli/sb/errors"/>

Note that, they http adapter will still show SOAPFault if the above validations fail. But Rest will not work.


The above highlighted part is very important for custom faults to work.


Now I will show how the response looks like when an error occurs. I am going to provide totalAmount as 34325 and paidAmount as 123. The result is going to be >1000. Hence, "Remaining amount is more than allowed limit" error should be returned.  



SOAP proxy - 


SOAP Proxy returns a SOAP Fault, where the Custom Fault is embedded in the detail tag. This is exactly what the pipeline returns to any proxy.






















Rest proxy with application/xml as Media and Accept type - 

The error response contains just the custFault that we saw in the detail section of SOAP Fault. 
As mentioned earlier, Rest adapter just reads and validates the detail tag structure.

Rest proxy with application/json as Media and Accept type -

The error response here is the JSON format of above Rest xml error response.













Thus, custom errors are handled in OSB 12c for SOAP and REST. 

I hope you find this blog useful.