Creating a Mobile-Optimized REST API Using Oracle Service Bus – Part 2

Introduction

To build functional and performant mobile apps, the back-end data services need to be optimized for mobile consumption. RESTful web services using JSON as payload format are widely considered as the best architectural choice for integration between mobile apps and back-end systems. At the same time, most existing enterprise back-end systems provide a SOAP-based web service application programming interface (API) or proprietary file-based interfaces. In this article series we will discuss how Oracle Service Bus (OSB) 12c can be used to transform these enterprise system interfaces into a mobile-optimized REST-JSON API. This architecture layer is sometimes referred to as Mobile Oriented Architecture (MOA) or Mobile Service Oriented Architecture (MOSOA). A-Team has been working on a number of projects with OSB 12c to build this architecture layer. We will explain step-by-step how to build this layer, and we will  share tips, lessons learned and best practices we discovered along the way. In part 1 we discussed the design of the REST API, in this second part we will discuss the implementation of the “read” (GET) RESTful services in service bus by transforming ADF BC SDO SOAP service methods

Main Article

Getting Started

As of release 12.1.3 you can develop and test service bus applications inside JDeveloper. For this you need to download and install a separate “SOA Suite Quick Start” version of JDeveloper. Download page is here, installation instructions can be found here.

The SOA Suite Quick Start release of JDeveloper 12.1.3 has the same version number as the “vanilla” JDeveloper 12.1.3 release. This means that by default they will use the same system directory. This can cause weird and unexpected behavior. You need to make sure both JDeveloper releases use their own system directory by setting the JDEV_USER_HOME environment variable in the executable file that you use to launch JDeveloper (custom .bat file on Windows, JDeveloper Unix executable file inside package contents on Mac).

After starting the JDeveloper 12.1.3 release that comes with the SOA Suite Quick Start, you go to the File -> New -> Application gallery and choose Service Bus Application with Service Bus Project.

NewSBApp

We name the application and its project “HrApi”.

The input data for the HR Rest API is primarily coming from an ADF Business Components application where we exposed some of the view objects as SOAP web services using the application module service interface wizards. So, before we start building out the service bus application, we need to have our ADF BC SDO SOAP service up and running. We can test the service using SOAP UI, or using the Http Analyzer inside JDeveloper. As shown in screen shot below, the findDepartments method return a list of departments, including a child list of employees for each department.

HttpAnalyzer

There is also a getDepartments method that takes a departmentId as parameter, this method returns one department and its employees. We will use these two methods as the data provider for the two RESTful resources that we are going to create in this article:

Resource Method Description Req/Resp Payload
/departments
?expandDetails=true/false
GET Department list (query param is optional) Sample
/departments/{departmentId} GET Department details and list of employees Sample

Creating the External HR SOAP Business Service

We start opening the service bus overview diagram by double-clicking on the HrApi overview icon in the navigator window.

SBOverviewEmpty

In the components palette at the right, we now select the HTTP icon (Technology category) and drag and drop this onto the External Services area. The Create Business Service wizard is launched. We name the service “HRBusiness Service” and we change the location to ensure the WSDL file and XML Schema files are stored in a dedicated directory for this business service, This avoids confusion later on when we start developing other business services and proxy services.

CBS1

After clicking Next, we select the WSDL option and we use the icon at the right of the WSDL field to launch the Select WSDL wizard. In this wizard, we click on the Application Server icon and enter the WSDL of our ADF BC SOAP service in the Selection field.

CBS2

After clicking OK, the Import Service Bus Resources wizard appears. On the first page we change the location again to store the resources in a business service specific sub-directory.

CBS2-1

When we click Next, we get an overview of the resources that will be imported. Looking at the long list and meaningless names, you can see why it is handy to store them in their own directory so you at least know where they are coming from.

CBS2-2

After clicking Finish, we return to page 2 of the Create Business Service wizard which is now showing our WSDL information including the port information.

CBS2-3

We now advance to the last wizard page which shows the transport and endpoint information which we can leave as is.

CBS3

After clicking Finish, the business service appears in the External Services area in our overview diagram.

SBOverviewES

It might happen that the ADF BC SOAP service changes after you created the external business service on the diagram. Service bus doesn’t provide an out-of-the-box refresh or refactoring option, however, here is how you can update the WSDl and associated schemas afterwards: create a new external HTTP service using the same WSDL by repeating the above steps, except for the last step: after returning from the Import Service Bus Resources wizard, you click Cancel to prevent creation of the business service on the diagram. Since you used the same import location, the existing business service will now use the latest ADF BC WSDL and schemas.

Creating the REST Proxy Service

We start with dragging and dropping the REST icon from the components palette (Technology category) onto the Proxy Services area of the overview diagram. In the Create Rest Binding dialog that appears we add a resource path /departments.

CRB1

Then we click on the plus icon in the Operaton Bindings section to add an operation binding. The REST Operation Binding dialog appears. We give the operation a meaningful name, for example getDepartments. We set the HTTP Verb to GET, and specify the expandDetails query parameter as specified in the design. We can leave the Expression field blank. This field will automatically be filled when we exit the dialog later on by clicking OK.

CRB1.1

Next, we click on the Response tab. We mark the response as a JSON payload and then we need to define the XML schema element that defines the structure of the response. You might wonder “Why do we need an XML schema when we are returning a JSON payload?” The reason is that service bus internally works with XML only, we need to supply it with a schema element so service bus knows how to construct the JSON payload. Note that in a future release service bus engine will have native support for REST and JSON allowing us to specify the payload structure in emerging REST standards like RAML and SWAGGER.

But don’t worry if you are not familiar with writing XML schemas, service bus comes with a Native Format Builder wizard which allows you to create an XSD using a sample JSON payload. To launch this wizard, click the red-circled icon as shown in the screen shot below.

CRB2

On the first page, we enter a meaningful name for the XSD and we ensure the schema will be stored in the default Resources directory. Do not change this directory as it will cause errors in the .wsdl and .wadl files that are generated based on this definition (known issue).

NFB1

On the next page, we leave the type to JSON Interchange Format.

NFB2

On the third page we modify the target namespace, and root element name, and we use the sample payload that we created as part of our design in part 1 of this article series.

NFB3

The last page of the wizard shows the XSD as it will be created. Here is the full content of the HRRestService.xsd:

<?xml version = '1.0' encoding = 'UTF-8'?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns="http://oracle.com/mobile/HRRestService_getDepartmentList_response" 
targetNamespace="http://oracle.com/mobile/HRRestService_getDepartmentList_response" 
elementFormDefault="qualified" xmlns:nxsd="http://xmlns.oracle.com/pcbpel/nxsd" 
nxsd:version="JSON" nxsd:jsonTopLevelArray="true" nxsd:encoding="US-ASCII">
   <xsd:element name="DepartmentListResponse">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="topLevelArray" maxOccurs="unbounded">
               <xsd:complexType>
                  <xsd:sequence>
                     <xsd:element name="id" type="xsd:integer"/>
                     <xsd:element name="name" type="xsd:string"/>
                  </xsd:sequence>
               </xsd:complexType>
            </xsd:element>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>
<xsd:annotation xmlns="">
      <xsd:appinfo>NXSDSAMPLE=</xsd:appinfo>
      <xsd:appinfo>USEHEADER=false</xsd:appinfo>
   </xsd:annotation>
</xsd:schema>

There are two important things to note when looking at this XSD:

  • The maxOccurs property with value “unbounded” is added because in the sample payload we included two departments in the array. If the sample included only one department, then this property would have been left out, and we would have to add it manually afterwards. This property is important to get a loop over the departments when mapping the SOAP response to the JSON format using XQuery as we will see later.
  • The element named “topLevelArray” is not included in the REST response because inside the schema header element, the property jsonTopLevelArray is set to “true”.

When we click Finish in the Native Format Builder wizard, we return to the Rest Operation Binding dialog which now shows the XSD element that should be used for the response:

ROB1

We can click OK and we return to the Create Rest Binding dialog which shows our first complete resource definition:

CRB4

When we click OK we return to the overview diagram which now includes our REST proxy service.

OD2

The red cross icon indicates that we are not done yet, we still need to wire up the proxy service with a pipeline which in turn links to the business service. We will do this in the next section.

To create the department details resource, we can largely follow the same procedure. However, instead of creating a separate schema using the Native Format Builder dialog, we now manually add the DepartmentDetailsResponse element to the HRRestService.xsd we created with the department list resource. It is cleaner to group all elements related to one proxy service in one schema, and it eases refactoring of the schema, enabling reuse of element definitions, as we will see later on in part 3 of this article series.

However, there is one problem here: when setting the jsonTopLevelArray property to true in the header of the schema definition, we can only have one top-level element in the schema. So, we need to set this property to false if we want to add the DepartmentDetailsResponse element. When we do this we should also rename the top-level element as it will now appear in the JSON payload:

TopLevelArray

The JSON payload that will be returned will now look like this:

{"departments" : [ 
  { "id" : 10,
  "name" : "Administration"
  }, 
  {"id" : 20,
  "name" : "Marketing"
  }
]}

This is a slight deviation from our initial design but should not cause any real problems for the mobile developer consuming this resource.

We can now add the DepartmentDetailsResponse element definition to HRRestService.xsd:

 <xsd:element name="DepartmentDetailsResponse">
    <xsd:complexType>
       <xsd:sequence>
          <xsd:element name="id" type="xsd:integer"/>
          <xsd:element name="name" type="xsd:string"/>
          <xsd:element name="location" type="xsd:string"/>
          <xsd:element name="managerId" type="xsd:integer"/>
          <xsd:element name="managerName" type="xsd:string"/>
          <xsd:element name="employees" maxOccurs="unbounded">
             <xsd:complexType>
                <xsd:sequence>
                   <xsd:element name="employeeId" type="xsd:integer"/>
                   <xsd:element name="firstName" type="xsd:string"/>
                   <xsd:element name="lastName" type="xsd:string"/>
                </xsd:sequence>
             </xsd:complexType>
          </xsd:element>
       </xsd:sequence>
    </xsd:complexType>
 </xsd:element>

Although we add this new element to the existing XSD, we still can use the Native Format Builder to save us some tedious typing: we can launch the wizard, use the sample payload as specified for the department details resource and copy the generated XSD element from the last wizard page to the clipboard, and then cancel the wizard. We then can paste the element into the existing XSD.

We now re-enter the Rest Proxy Binding dialog by right-mouse-clicking on the proxy service and then choose Edit REST.

EditRest

We add resource path: /departments/{departmentId} and specify the operation binding name getDepartmentDetails. Rather than clicking the icon to launch the Native Format Builder to create a new CSD, we now click the lamp icon which allows us to select the DepartmentDetailsResponse element type from the HRRestService.xsd that we just added manually.

ChooseResponseElement

We click OK twice, and we return to the Create Rest Binding  dialog which now shows two completed resource definitions.

CRB5

Creating the HR Pipeline

We are ready for the last and most important step: adding a pipeline that links the Rest proxy service which the SOAP business service and performs the SOAP-to-JSON transformation for both resources.We start with dragging and dropping the Pipeline icon (under Resources category) onto the Pipelines/Split Joins area of the overview diagram. This launches the Create Pipeline Service dialog. On the first page we set the name to HRPipeline.

CPS1

On the second page, we select  the WSDL radio button, and then using the Browse WSDL’s icon at the right, we can pick the HRRestService.wsdl which was auto-generated for us when we defined the REST resources in the proxy service. You might be confused at this point why we need to select the proxy service WSDL and not the business service WSDL. Well, to keep it simple: we want to create a mobile-optimized REST API, which means we take the proxy service as a starting point. Which means we are working from left to right in service bus.

CPS2-1

We also need to uncheck the checkbox Expose as a Proxy Service, since we already defined our proxy service, and we want to take our proxy service WSDL as the starting point for the pipeline transformation.

CPS2-2

After clicking Finish, the pipeline is added to the diagram, and we can connect both the proxy service and the business service with the pipeline by dragging a line between the arrows in the three services. The diagram now looks like this:

SBOReady

We double-click on the pipeline which opens up the design view of the pipeline:

PL1

We start with dragging and dropping an Operational Branch from the component palette onto the diagram. We release the mouse on the circle in between the top-level HRPipeline icon and RouteNode1,and the diagram now looks like this.

PL2

The default branch will be executed when no REST operation is specified. This will never happen in our scenario, so we don’t need it. We cannot completely remove the default branch, but nothing should happen. So, we move the RouteNode2 with nested Routing element to the getDepartmentList branch. We can do this by clicking on the icon in the upper left corner of RouteNode 2 and then drag and drop it onto the circle that appears just below the getDepartmentList.We also need another branch for the getDepartmentDetails method. We can add a branch by clicking on the upper right icon inside the BranchNode1 element. A new branch appears and we can use the Operation dropdown list in the properties window of the branch to select the correct operation.

PL3

We need a route node with nested routing inside the getDepartmentDetails branch just like we have inside the getDepartmentList branch. We can do this by dragging and dropping the Route icon from the component palette, and then drag and drop the Routing icon inside it. Or, we can copy RouteNode2 using the popup menu that appears when we right-mouse-click on the RouteNode2 icon, and then paste it onto the GetDepartmentDetails branch.

CopyNode

The diagram now looks like this

PL4

When we click on the Routing element inside RouteNode2, we can specify to which business service method this request must be routed using the Operation dropdown list in the property inspector. In our sample, this is the findDepartments method.

Routing

Likewise, we specify method getDepartments as the Operation for the Routing element inside the getDepartmentDetails branch.

Before we continue it is time for a little explanation. Depending on the REST operation requested through the proxy service, the corresponding operation branch will be executed. Within a branch we have a routing consisting of a Request Action and a Response Action. The Request Action route is executed when the proxy service operation is invoked. The Response Action route is executed when the business service method returns a response. In other words:

  • in the Request Action route we need to transform the JSON query and path parameters and/or JSON payload (not applicable for GET requests) to a SOAP request body that is understood by the ADF BC SOAP Service.
  • in the Response Action route we need to transform the SOAP response body to the JSON payload.

Now, with the risk of loosing you, I am going to sharpen the above explanation because it is not entirely in line with how service bus really works. As mentioned before, service bus uses only XML internally. So, the moment a REST-JSON request arrives, the payload is immediately converted to XML, using the WSDL/WADL and schema created while defining the proxy service. This XML payload together with the query and path parameters is the input for creating the SOAP request body.

=> In the Request Action path, we need to tell service bus how the proxy service XML can be transformed into business service SOAP request body XML.

Likewise, the SOAP response is not  directly converted to JSON, it is first transformed into an XML structure that maps to the XML schema element type used to define the required JSON payload. Only just before the response “leaves” service bus it is tranformed into JSON format.

=> In the Response Action, we need to tell service bus how the SOAP response body XML can be transformed to the proxy service response XML payload.

So, all transformations are from XML to XML and we “tell” service bus how to do these transformations using XQuery. There are other transformation options in service bus, we will explain later why using XQuery is such a good idea.

Let’s start with transforming the getDepartmentDetails SOAP response. The actual transformation is done using a “Replace” action, and in the properties of the Replace action we can specify the XQuery resource that should be used for the transformation. We first create the XQuery file. We can do this using the File -> New menu, choosing XQuery File ver 1.0.

NewQRY

The Create XQuery Map Main module dialog appears. Since you will typically get a lot of XQuery files, it is important to give them meaningful names and store them in their own sub-directory (“transformations” in this sample). Any naming convention is fine, we typically use a logical name suffixed with “BS2PS” or “PS2BS” indicating whether the transformation direction is from business service to proxy service or vice versa.

MAP1

An XQuery file performs a transformation and for this transformation it needs an input and an output. The actual value of the input and output is defined when we use the XQuery file for a specific transformation, for example inside a Replace action. Making an analogy with Java, you could see the XQuery file as a reusable Java method with one or more input parameters and one output parameter, and the actual parameter values are passed in when calling the method. So, in this dialog, we only define the parameter types, or in the vocabulary of XQUery, we define the source parameter types and the target type.We will start defining the source parameter type. We click on the plus icon at the right of the (empty) Sources list, and a Function Parameter dialog appears.We can give the source parameter a name, for example “input”

MAP2

Then we click on the edit icon at the right of the Sequence Type field. The Function parameter type dialog appears.In this dialog we click the Type Chooser icon at the right of the Schema Object Reference field, so we can pick our ADF BC response element type. You might wonder which XSD node to open, but you can simply expand the top-one, it will automatically expand the XSD node that contains all the HR element types. Since we are invoking the getDepartments method, we select getDepartmentsResponse element type.

MAP3

We click OK three times which brings us back to the base XQuery file dialog which now shows the input parameter together with its type definition.

MAP4

We define the output (target) type in a similar fashion as the input parameter type. In the Type Chooser dialog we now open the HRRestService.xsd and select the DepartmentDetailsResponse element type.

MAP5

After clicking OK twice we return to the base dialog.

MAP6

We are done setting up source and target, so we click OK which brings up the XQuery Mapper diagram. This diagram provides a nice visual way to define the mappings by dragging and dropping lines from source elements to target elements.

QueryMapper

If you are curious abut the XQuery language, you can click the XQuery Source tab to see the code that is generated while using the visual mapper.

We now drag and drop a Replace action from the component palette (Message Processing category)  onto the circle that appears just below the Response Action.

Replace

In the Replace – Properties window, we set the Location to Body, and the Replace Option should be set to Replace Node Contents. To use our XQuery mapping file we just created, we click at the icon at the right of the Value field, and choose XQuery Resources.

ReplaceProps

In the XQuery Transformation Expression Builder dialog that appears, we use the search icon to select our DepartmentDetailsBS2PS mapping file into the XQuery Resource field.

UseMapper1

Our source parameter that we named “input” automatically shows up in the XQuery Variables section. We now need to set the “binding” of this variable. To enter a binding expression we click the icon to launch the XQuery Expression Builder. Unfortunately, the builder only provides help with selecting the body from the proxy service: if you expand the body, you see the two possible body elements getDepartmentList and getDepartmentDetails. That is helpful later on when we specify the Request Action later on, but is not useful, and even confusing while specifying the Response Action.  So, how do we know what to enter as expression? The easiest way is to run the getDepartments method in SOAP UI or in the HTTP Analyzer of JDeveloper:

getDeaprtmentsResponse

We need to return the first element inside the body, element, which results into the following expression:

$body/ns0:getDepartmentsResponse

When we enter this expression in the builder dialog, you will notice the red underlining of the expression, as shown below:

UseMapper2

This is caused by a missing namespace definition. We need to click on the Namespaces tab, click the plus icon, and add our namespace as defined in the SOAP response we got in in the HTTP Analyzer.

AddNamespace

After clicking OK, the new namespace is added and the red underlining of the expression is gone.

UseMapper3

We click OK twice and the Replace – Properties window now shows our XQuery transformation file used in the Value property of the Replace action.

ReplaceProps2

Admittedly, this was not a trivial step to do, however, once this issues with the builder dialog showing the wrong body elements is fixed in a future release of service bus, it will be faster and more intuitive.

OK, onto the Request Action path. Here we need to do a transformation in the opposite way. The input of the transformation is the departmentId path parameter and the output is the business service request body, which , as we learned from looking at the HTTP Analyzer, should look something like this:

   <env:Body>
      <ns1:getDepartments>
         <ns1:departmentId>20</ns1:departmentId>
      </ns1:getDepartments>
   </env:Body>

You might wonder, do we really need to use an XQuery mapping file for this simple transformation? It is just fixed text, only the value of departmentId is dynamic, The answer is no, we can also use a straight XPath expression to construct the request body. To illustrate this, we first drag and drop a new Replace action onto the circle just below the Request Action of the getDepartmentDetails branch. We select Body as the value for Location again, and we select the Replace Node Contents radio button. Selecting this radio button ensures that we only replace the content of the request, being the elements inside the <env:Body> element.

To set the Value field, we click on the icon and this time we choose Expression. In the expression builder, we first copy the sample body from above, we remove the value 20 which we need to replace with an XPath expression to rertrieve the departmentId path parameter. This time the expression builder is of great help, we can nicely expand the getDepartmentDetails request body to get to the departmentId parameter. We click Insert Into Expression and then we surround the expression with data(). This is because the expression returns the departmentId XML element, not the value itself. Surrounding it with data() will return the value which is what we need here.

We are almost done now, we only have to add a namespace to get rid of the red underlining. However, the required namespace is the same as we needed in the Response Action, and since namespaces are shared within a routing, we can simply reuse that namespace definition. All we need to do is change ns1 to ns0 since that was the prefix we used in the Response Action. So, the correct expression looks like this:

DataFix

Now, this was easier and faster than using an XQuery file transformation, right? Still, we believe it is better to use XQuery transformation for serious service bus applications as is explained in the next tip.

Although service bus supports other options for XML transformations, we recommend to always use XQuery transformation files. By exclusively using XQuery files, refactoring as a result of changes to business or proxy services can be done locally within the XQuery files. The pipeline definitions can remain unchanged, and since there are no transformations “hardcoded” inside XPath expressions, there is no (or at least much less) risk of incomplete refactoring exercises.

Let’s follow the recommendation and create an XQuery mapping file instead.We create a new XQuery file and name it “DepartmentDetailsPS2BS”. This time the source departmentId parameter is not a complex type but a simple string.  We select string by expanding the XML Schema Simple Types node in the Type Chooser:

MAPSimple1

For the target, we select the getDepartments element:

MAPSimple2

The XQuery dialog looks like this

MAPSimple3

We click OK to get the mapper diagram. We first need to insert the departmentId element in the target type using the popup menu.

MAPSimple4

And then we can draw a line between the source and target departmentId elements.

MAPSimple5

We now go back to the Replace action and change the Value expression to use our XQuery transformation file. The binding expression for the departmentId variable can easily be inserted using the builder dialog, just like we did when using the straight XPath expression.

DataFix2

That’s it, we are done with specifying the getDepartmentDetails branch.

Overview

We will leave it as an exercise for you to specify the request and response actions for the getDepartmentList branch. Note that you can ignore the expandDetails query parameter for now, we will discuss the implementation of this parameter in the next part of this article series. If you get stuck, you can take a look at the sample project, download links can be found at the bottom of this post.

Testing the REST Proxy Service

Service bus provides an easy way to test your services inside JDeveloper. You can right-mouse-click on each service and then choose Run.

run1

This will deploy the complete service bus application and will launch a tester window.

Run2

When we click the Execute button, the department list is shown:

Run3

Likewise, we can test the department details resource using departmentId 20:

Run4

Which results in the following response:

Run5

We can also test the REST resources outside JDeveloper. Postman is an excellent Google Chrome extension to test RESTful services. To use Postman, we need to know the full endpoint URL we should use. For this, we choose Edit on the proxy service popup menu, and then we click the Transport tab.

EndpointURI

We set the Endpoint URI field to /hr, which leads to a URL like this to test the department details resource:

http://host:port/hr/departments/20

This is how the result looks in Postman:

Postman

Conclusion

We have provided you with detailed step-by-step instructions on how to create two RESTFul resources that follow the design we have described in part 1 of this article series. If you are new to service bus you might be a bit overwhelmed by the number of configuration steps and dialog windows you have to go through. However, it is our experience (we started as newbies just like you not that long ago) that you very quickly get used to the sequence of steps you need to take. With the explanations, tips and recommendations in this blog post we hope to save you some valuable time and avoid some frustration which is common when learning a new product.

Learning service bus to create REST API’s is an investment that quickly pays back. Developing mobile applications, for example using Oracle’s Mobile Application Framework just becomes much easier and faster if you can use a mobile-optimized REST API, rather than complex back-end web services. And even more important, the performance of your mobile app is likely to be much better too.

In the next parts we will dive into more advanced concepts. We will implement the expandDetails query paramater, implement the other PUT and POST resources, discuss troubleshooting techniques, add error handling and will take a look at pipeline templates. Stay tuned!

Downloads:

 

Add Your Comment