Manipulating a REST Service Header Using OSB 12.2.1

Version 7

    Oracle SOA Suite 12c includes the ability to use REST more easily than before. One of the complexities that this type of service can have is handling the header; unlike SOAP services, header elements must be specified in the transport header as user attributes. This article by Oracle ACE Associate Sandra Flores explains how to expose a proxy REST service in Oracle Service Bus 12.2.1 (OSB) from one SOAP type, created from WSDL and XSD files.


     

    By Oracle ACE Associate Sandra Floresace-assoc.jpg

     

    REST services have become highly relevant in both mobile and Web applications, because they help maintain light exchange of information. Oracle SOA Suite 12c includes the ability to use REST more easily than before. One of the complexities that this type of service can have is handling the header; unlike SOAP services, header elements must be specified in the transport header as user attributes.

     

    This article will explain how to expose a proxy REST service in Oracle Service Bus 12.2.1 (OSB) from one SOAP type, created from WSDL and XSD files, but with an increased complexity factor: The scenario poses a SOAP service that requires the use of customized elements in the SOAP Envelope Header element; when it is exposed as REST, it must have the same characteristic in some way. Because this particular example requires only one Pipeline for both interfaces (SOAP and REST), the header should be supported in both cases and, as far as possible, treated in the same way. I share my solution for this purpose.

     

    Let's begin with the definition of the WSDL and XSD files of the SOAP service called "Person". It is very important to highlight how the elements involved in the definition for the message header are declared.

     

    Person.wsdl:

     

    <wsdl:definitions name="Person" targetNamespace="http://service.blog.com.mx/Person"
    
    xmlns:tns="http://service.blog.com.mx/Person" xmlns:inp1="http://schema.blog.com.mx/Person"
    
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    
    <wsdl:types>
    
    <xsd:schema>
    
    <xsd:import namespace="http://schema.blog.com.mx/Person" schemaLocation="../XSD/PersonSchema.xsd"/>
    
    </xsd:schema>
    
    </wsdl:types>
    
    
    
    <wsdl:message name="insertPersonRequest">
    
    <wsdl:part name="parameters" element="inp1:insertPersonRequest"/>
    
    </wsdl:message>
    
    <wsdl:message name="insertPersonResponse">
    
    <wsdl:part name="parameters" element="inp1:insertPersonResponse"/>
    
    </wsdl:message>
    
    
    
    <wsdl:message name="serviceSOAFault">
    
    <wsdl:part name="parameters" element="inp1:serviceSOAFault"/>
    
    </wsdl:message>
    
    
    
    <wsdl:message name="requestHeaderMessage">
    
    <wsdl:part name="requestHeader" element="inp1:requestHeader"/>
    
    </wsdl:message>
    
    <wsdl:message name="responseHeaderMessage">
    
    <wsdl:part name="responseHeader" element="inp1:responseHeader"/>
    
    </wsdl:message>
    
    
    
    <wsdl:portType name="PersonPortType">
    
    <wsdl:operation name="insertPerson">
    
    <wsdl:input message="tns:insertPersonRequest"/>
    
    <wsdl:output message="tns:insertPersonResponse"/>
    
    <wsdl:fault name="fault" message="tns:serviceSOAFault"/>
    
    </wsdl:operation>
    
    </wsdl:portType>
    
    
    
    <wsdl:binding name="PersonBinding" type="tns:PersonPortType">
    
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
    
    <wsdl:operation name="insertPerson">
    
    <soap:operation style="document" soapAction="insertPerson"/>
    
    <wsdl:input>
    
    <soap:body use="literal" namespace="http://service.blog.com.mx/Person"/>
    
    <soap:header message="tns:requestHeaderMessage" part="requestHeader" use="literal"/>
    
    </wsdl:input>
    
    <wsdl:output>
    
    <soap:body use="literal" namespace="http://service.blog.com.mx/Person"/>
    
    <soap:header message="tns:responseHeaderMessage" part="responseHeader" use="literal"/>
    
    </wsdl:output>
    
    </wsdl:operation>
    
    </wsdl:binding>
    
    
    <wsdl:service name="PersonService">
    
    <wsdl:port name="PersonPort" binding="tns:PersonBinding">
    
    <soap:address location="http://localhost:27007/Person"/>
    
    </wsdl:port>
    
    </wsdl:service>
    
    </wsdl:definitions>
    
    

     

    PersonSchema.xsd:

     

    <xsd:schema elementFormDefault="qualified" targetNamespace="http://schema.blog.com.mx/Person"
    
    xmlns:tns="http://schema.blog.com.mx/Person" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    
    <!-- ========================= Elements ============================== -->
    
    <!-- Elements to insert a Person registry -->
    
    <xsd:element name="insertPersonRequest" type="tns:insertPersonRequestType"/>
    
    <xsd:element name="insertPersonResponse" type="tns:InsertPersonResponseType"/>
    
    
    
    <!-- Elements for Header and Fault -->
    
    <xsd:element name="requestHeader" type="tns:RequestHeaderType"/>
    
    <xsd:element name="responseHeader" type="tns:ResponseHeaderType"/>
    
    <xsd:element name="serviceSOAFault" type="tns:ServiceSOAFaultType"/>
    
    
    
    <!-- ========================== Types ================================== -->
    
    <!-- Types required to insert a Person-->
    
    <xsd:complexType name="insertPersonRequestType">
    
    <xsd:sequence>
    
    <xsd:element name="key" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="lastName" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="age" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    <xsd:element name="occupation" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    </xsd:sequence>
    
    </xsd:complexType>
    
    <xsd:complexType name="InsertPersonResponseType">
    
    <xsd:sequence>
    
    <xsd:element name="idPerson" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="estatus" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    </xsd:sequence>
    
    </xsd:complexType>
    
    
    
    <!-- Types for Header and Fault -->
    
    <xsd:complexType name="RequestHeaderType">
    
    <xsd:sequence>
    
    <xsd:element name="idSystem" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="token" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="idTransaction" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    </xsd:sequence>
    
    </xsd:complexType>
    
    <xsd:complexType name="ResponseHeaderType">
    
    <xsd:sequence>
    
    <xsd:element name="idTransaccion" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    <xsd:element name="message" type="xsd:string" minOccurs="1" maxOccurs="1"/>
    
    </xsd:sequence>
    
    </xsd:complexType>
    
    <xsd:complexType name="ServiceSOAFaultType">
    
    <xsd:sequence>
    
    <xsd:element name="code" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    <xsd:element name="description" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    <xsd:element name="detail" type="xsd:string" minOccurs="0" maxOccurs="1"/>
    
    </xsd:sequence>
    
    </xsd:complexType>
    
    </xsd:schema>
    

     

    The most relevant thing in the WSDL is that messages should have only one part--otherwise, when required to expose the pipeline as REST, the Expose As REST option will not be shown, because such services do not support multipart messages. For example, if we did something like this, it would be wrong:

     

    <wsdl:message name="insertPersonRequest">
    
    <wsdl:part name="parameters" element="inp1: insertPersonRequest"/>
    
    <wsdl:part name="requestHeader" element="inp1:requestHeader"/>
    
    </wsdl:message>
    

     

    The next step is to create the proxy service from WSDL and XSD files. In JDev -- once the OSB project, folders structure and both files have been created -- right click on Person.wsdl, Service Bus, Generate Proxy Service.

    ManipulatingHeaderREST_1.png

     

    This will open the window where we set the name of the SOAP proxy service, select the binding (previously created in the WSDL), and select the Generate Pipeline option. Click Next.

     

    ManipulatingHeaderREST_2.png

     

    Select transport and specify the desired Endpoint URI. Click Finish.

     

    ManipulatingHeaderREST_3.png

    The requested files will be created. I changed the location of the files to the appropriate folders for better visual control.

    ManipulatingHeaderREST_4.png

    Now let's open the file named Person (equivalent to Composite.xml in BPEL), located at the bottom left of the project. Right-click the Pipeline and Expose As REST.

     

    ManipulatingHeaderREST_5.png

    In the creation window we will set the name of the REST proxy service, in this case PersonRest. Click Next.

     

    ManipulatingHeaderREST_6.png

    In the resources configuration window, we need to establish the operations specified in the WSDL and link them to a resource and its corresponding path associated with the REST service.

    ManipulatingHeaderREST_7.png

    Double-click on the empty record in the Resources table (it has just a slash '/'). This will open a window to set the path of the resource. In this case, I called it /insert. Click OK.

    ManipulatingHeaderREST_8.png

    Double click on the record of the Operation Bindings table.

    ManipulatingHeaderREST_9.png

    The configuration window will be opened. Here, we choose the resource and the HTTP verb. In this case I chose POST because my fictitious transaction is an insert on the server. In the Request tab, it is automatically mapped XSD element, and we can choose whether the REST service will receive messages in JSON and XML format, among other things.

     

    ManipulatingHeaderREST_10.png

     

    Now we must set the response. To do this, click the Response tab and select the format of the payload. Here we can view the example of the message in multiple formats.

     

    ManipulatingHeaderREST_11.png

    Once we have finished linking operations with resources, we see the full screen as shown below. Click OK.

    ManipulatingHeaderREST_11.png

    After finishing the automatic creation, we’ll see the SOAP and REST proxy interface associated to the Pipeline. Now we need to edit the Pipeline to add a bit of functionality and get the attributes of the header that interest us. Double-click the Pipeline.

    ManipulatingHeaderREST_13.png

     

    I added an Operational Branch activity and within it a PipelinePair for insertPerson operation. For the Response Stage I used a Replace activity to simulate the mock response in order to test it.

     

    ManipulatingHeaderREST_14.png

    The Replace activity is:

     

    ManipulatingHeaderREST_15.png

     

    I manually wrote the string text of the XML response as expected for the SOAP service, and I used the inLinedXML function to turn it into an XML format.

     

    ManipulatingHeaderREST_16.png

     

    Now, we can deploy and check that the service works well in both proxies, SOAP and REST.

     

    ManipulatingHeaderREST_17.png

     

    ManipulatingHeaderREST_18.png

     

    ManipulatingHeaderREST_19.png

     

    ManipulatingHeaderREST_20.png

     

    The most interesting part of this article is related to reading the header of the REST service. So let’s get down to the nitty-gritty.

     

    I've changed the xml response for both header elements (idSystem and token) and concatenated them to the message output to be readily visible in the test, so the expression stays as follows:

     

    ManipulatingHeaderREST_21.png

    Notice that the values of the REST service header elements are obtained from the inbound variable:

     

    $inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSystem']/@value

     

    Deploy and test the service by sending the idSystem and token values in the HTTP Transport Header. This can be done using the OSB test console in the User - Transport Headers section.

     

    ManipulatingHeaderREST_22.png

     

    ManipulatingHeaderREST_23.png

     

    ManipulatingHeaderREST_24.png

    As you can see in the picture, the response shows the values ??that we sent in the header of the HTTP transport.

     

    As an additional step, I decided to assign the values ??of our user header obtained from inbound to the header variable. With this action we can work with a single structure in the pipeline, without distinction as to whether it has been an invocation via REST or SOAP.

     

    For this step, I implemented an XQuery transformation that receives the header and inbound variables, makes validations in either case (REST and SOAP), and sets the values in the header variable. The XQuery source code file is:

     

    xquery version "1.0" encoding "utf-8";
    
    (:: OracleAnnotationVersion "1.0" ::)
    
    declare namespace soap="http://schemas.xmlsoap.org/soap/envelope/";
    
    (:: import schema at "../XSD/soap-env.xsd" ::)
    
    declare namespace ctx="http://www.bea.com/wli/sb/context";
    
    (:: import schema at "../XSD/MessageContext.xsd" ::)
    
    declare namespace tp="http://www.bea.com/wli/sb/transports";
    
    declare namespace http = "http://www.bea.com/wli/sb/transports/http";
    
    declare namespace per="http://schema.blog.com.mx/Person";
    
    (:: import schema at "../XSD/PersonSchema.xsd" ::)
    
    
    
    declare variable $requestHeader as element() (:: schema-element(soap:Header) ::) external;
    
    declare variable $inbound as element() (:: schema-element(ctx:endpoint) ::) external;
    
    
    
    declare function local:setRequestHeader($requestHeader as element() (:: schema-element(soap:Header) ::),
    
    $inbound as element() (:: schema-element(ctx:endpoint) ::)) {
    
    <soap:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    
    <per:requestHeader xmlns:per="http://schema.blog.com.mx/Person">
    
    {
    
    if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSystem']/@value))
    
    then
    
    <per:idSystem>{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSystem']/@value)}</per:idSystem>
    
    else if (exists($requestHeader/per:requestHeader/per:idSystem))
    
    then
    
    <per:idSystem>{$requestHeader/per:requestHeader/per:idSystem/text()}</per:idSystem>
    
    else
    
    ()
    
    }
    
    {
    
    if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='token']/@value))
    
    then
    
    <per:token>{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='token']/@value)}</per:token>
    
    else if (exists($requestHeader/per:requestHeader/per:token))
    
    then
    
    <per:token>{$requestHeader/per:requestHeader/per:token/text()}</per:token>
    
    else()
    
    }
    
    {
    
    if (exists($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idTransaction']/@value))
    
    then
    
    <per:idTransaction>{data($inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idTransaction']/@value)}</per:idTransaction>
    
    else if (exists($requestHeader/per:requestHeader/per:idTransaction))
    
    then
    
    <per:idTransaction>{$requestHeader/per:requestHeader/per:idTransaction/text()}</per:idTransaction>
    
    else()
    
    }
    
    </per:requestHeader>
    
    </soap:Header>
    
    };
    
    
    
    local:setRequestHeader($requestHeader, $inbound)
    

     

    In the Pipeline flow, I added a Replace activity to replace the header variable with the output of the XQuery. The Pipeline is as follows:

     

    ManipulatingHeaderREST_25.png

     

    Replace activity properties are the following:

     

    ManipulatingHeaderREST_26.png

     

    ManipulatingHeaderREST_27.png

    Finally, I changed the Replace activity expression to take a header value of REST and another one of SOAP, just to illustrate that once the transformation has passed, it is feasible to use the header structure.

     

    The new expression is:

     

    fn-bea:inlinedXML(fn:concat('<insertPersonResponse xmlns="http://schema.blog.com.mx/Person">
    
    <idPerson>', $inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='idSystem']/@value, '</idPerson>
    
    <estatus>', $header/per:requestHeader/per:token/text(), '</estatus>
    
    </insertPersonResponse>'))
    
    
    

     

    An error will likely be shown when editing the XQuery function, because the namespace prefix called "per" is not declared. If this happens, you only need to add it in the namespaces tab.

     

    To validate the result, deploy and test the proxy REST, sending token and idSystem headers.

    ManipulatingHeaderREST_28.png

    As shown in the picture, we get the value of the headers, whether from a REST or SOAP request type; these are combined in the header variable and thereafter it is possible to use them as if they had been sent by a SOAP request.

     

    With this we end the example. I hope you find it helpful for your implementations.

     

     

    About the Author

    Oracle ACE Associate Sandra Flores is a SOA architect at S&P Solutions, based in Mexico, where her focus is on services and process solutions.

     


    This article represents the expertise, findings, and opinion of the author.  It has been published by Oracle in this space as part of a larger effort to encourage the exchange of such information within this Community, and to promote evaluation and commentary by peers. This article has not been reviewed by the relevant Oracle product team for compliance with Oracle's standards and practices, and its publication should not be interpreted as an endorsement by Oracle of the statements expressed therein.