X

Best Practices from Oracle Development's A‑Team

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

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.

Main Article

In part 1 we discussed the design of the REST API, in part 2 and part 3 we discussed the implementation of the RESTful services in service bus by transforming ADF BC SDO SOAP service methods. In this fourth part, we will take a look at techniques for logging, debugging, troubleshooting and exception handling.

Using Service Bus Logging

The easiest way to get more insight in what actually happens inside your pipelines is to add Log actions. You can simply drag and drop a Log action from the component palette and drop it anywhere you want. For example, if a call to a business service fails, you can add log statements to print out the request body before and after the transformation that takes place in the Replace action to inspect the payload.

LogActions

By default, the Severity of the log message is set to Debug. In order to see debug log messages, you need to change the OSB log level which is set to Warning by default.  You can do this using the Actions dropdown menu in the JDeveloper log window, and choosing the Configure Oracle Diagnostic Logging option. You can also use enterprise manager, by opening the service bus dropdown menu and choose Logs -> Log Configuration.

SBLogMenu

If you set the log level to Trace (FINE) or lower. your debug log messages will in appear in the JDeveloper window log level. However, with this log level you also get a lot of standard diagnostic OSB log messages in your log which makes it harder to find your own log messages. So, it is easiest to set the Log action Severity to Info, and the OSB log level to Notification (INFO). Note that if you change the log level, you do not need to restart Weblogic or redeploy your app, the changes are applied immediately.

LogLevelInfo

With info-level logging you have a clean log window that only contains your own log messages, and when you move your OSB application to production, you will not clutter the log files as long as the production log level is set to Warning or Error. Here is an example of the log window when we execute the /departments/{departmentId} resource (which maps to the getDepartmentDetails operation binding):

LogWindow

More information about service bus logging can be found here.

Running in Debug Mode

Another way to troubleshoot issues is to run your OSB application in debug mode. You can do this by choosing the Debug option from the proxy service popup menu:

RunDebugMode

You can set breakpoints on the actions in your pipeline diagram using the popup menu:

SetBreakpoint

When you then execute a resource, the debugger will stop at your breakpoint and you can use the "data" debug window to inspect the flow of data through your pipeline.

DebugData

You can expand the various XML elements to see the contents of the header and body of your request and your response. In the above screen shot we have expanded the body element which shows the same data as we logged in the previous section. Any custom variables that you use to store temporary data, like the expandDetails variable we introduced in part 3, are also visible. When the debugger hits a breakpoint you have the normal debugging options like Step Over to go to the next action in the pipeline, or Resume to go to the next breakpoint. In other words, running in debug mode allows you determine the execution path through your pipeline in addition to viewing the data like you can with log messages,

Handling Business Service Exceptions

Invoking business services might cause various (unexpected) exceptions. The business service call might fail because the server is down, or the call succeeds but leads to an error because some business rule is violated while performing some update action. Without error handling added to our service bus application, any exception will cause a response with HTTP code 500 Internal Server Error and a meaningless OSB error code and message.

It is a good practice to use appropriate HTTP error codes depending on the type of exception that occurs. When sending a JSON payload that contains invalid data, for example a non-existent manager ID, it is common practice to return HTTP error code 400 "Bad Request". When the business service does not respond at all, we should return HTTP error code 404 "Not Found". Using these error codes makes clear to the consumer whether he is dealing with an application error (400), or a server error (404).

To return appropriate HTTP error codes, we first need to define an XSD that contains the structure of the error message for each type of error.Here is the error.xsd that we will  use in our example:

<?xml version = '1.0' encoding = 'UTF-8'?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"             xmlns="http://oracle.com/mobile/errors"             targetNamespace="http://oracle.com/mobile/errors"             elementFormDefault="qualified"> <xsd:element name="ApplicationError">   <xsd:complexType>     <xsd:sequence>       <xsd:element name="code" type="xsd:string"/>       <xsd:element name="message" type="xsd:string"/>       <xsd:element name="severity" type="xsd:string"/>     </xsd:sequence>   </xsd:complexType> </xsd:element> <xsd:element name="ServerError">   <xsd:complexType>     <xsd:sequence>       <xsd:element name="message" type="xsd:string"/>     </xsd:sequence>   </xsd:complexType> </xsd:element> </xsd:schema>

With this XSD in place we can add two fault bindings to our REST operation bindings:

FaultBindings

Each fault binding needs to have its own unique XSD element type. If we would reuse the ApplicationError element type with the 404 fault binding, the 404 error code would never be returned. OSB determines which fault binding to use based on the element type used in the fault response returned by the pipeline.

To be able to return a different payload and associated HTTP error code in case of an exception, we need to add a so-called error handler to our route nodes. We right-mouse-click on the RouteNode of the createDepartment operation branch, and choose Add Error Handler. To figure out the kind of response we get when violating a business rule, we first drag and drop a Log action inside the error handler and set the expression to $body.

CDError1

We now execute the /departments POST resource with an invalid managerId in the payload:

InvalidMgrId

In the log window we can inspect the payload body returned in case of an ADF BC exception being thrown:

InvalidMgrError

The body contains a generic part with the <env:Fault> element, and inside the <detail> element we can find the ADF-specific error. We need an XQuery file to transform the ADF error message to the ApplicationError element. As source element type we choose the ServiceErrorMessage element:

ErrorInput

As target element we choose the ApplicationError element from the error.xsd and then we can drag the mapping lines as shown below

ErrorMapping

As we have seen in the JDeveloper log window, the message element contains both the error code and the error message. Since we have a separate element for the code, we want to strip the error code from the message. We can do this by using the XQuery String function substring-after: in the component palette, we change the value of the dropdown list to XQuery Functions, and expand the String Functions accordion.We drag and drop the substring-after function onto the message mapping line, inside the Mappings area in the middle. We click on the yellow function expression icon that appears and then we can complete the expression in the Expression - Properties window.

ErrorMapping2

We should replace the second argument of the function with ': 'because after these two characters the actual error message appears. Click on the XQuery Source tab to make sure that the expression has been saved correctly, sometimes the change you make in the properties window is not picked up. If this is the case, just re-enter the function argument in the source.

In the component palette there are many XQuery functions available. If you want to get more information on how to use them, you can use the xqueryfunctions.com website.

 To complete the XQuery transformation, we need to surround the ApplicationError with the standard SOAP fault element, If we forget this step, the payload will not be recognized as a valid fault payload and the fault bindings we defined for the REST operation will not be used. We cannot do this in a visual way, so we click on the XQuery Source tab and add the surrounding fault element as shown below:

ErrorMapping3

Note that the value of the <faultcode> element must be set to env:Server, otherwise it will not work. The value of the <faultstring> element doesn't matter. With the XQuery transformation in place we can add a Replace action inside the error handler which uses this transformation:

ErrorReplace1

The expression for the ServiceErrorMessage input variable is shown below. Note the double slashes which means it will search the whole tree inside the body element, not just the direct children.

ErrorReplace

The err namespace can be found in the JDeveloper log and should be set to http://xmlns.oracle.com/adf/svc/errors/.

The last step is to drag and drop a Reply action after the Replace action and set the option With Failure to inform the proxy service that a fault response is returned.

ReplyProps

That's it, if we now use Postman to create a new department with an invalid managerId we get a nice error response with HTTP code 400:

PostmanErr

To handle the situation where the ADF BC SOAP server is down, we need to return a response which contains the ServerError element so we can return the HTTP error code 404 together with a user-friendly error message. To distinguish between the 400 and 404 error response, we drag and drop an If-Then action inside the error handler, and enter the following expression in the Condition field:

$body//err:ServiceErrorMessage!=''

When this expression is true we are dealing with an ADF BC Exception so we should move the Replace action we already defined inside the If branch. In the Else branch we should return a generic error that the service is not available. Since there is nothing to transform, we can enter the required response payload directly in the expression field:

 <env:Fault xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">    <faultcode>env:Server</faultcode>    <faultstring>Generic Error</faultstring>    <detail>      <ns2:ServerError>        <ns2:message>The HR service is currently not available, please contact the helpdesk</ns2:message>      </ns2:ServerError>    </detail>  </env:Fault>

The complete error handler (with log actions removed) now looks like this:

ErrorHandlerComplete

When we bring the ADF BC Server down and use Postman again to submit a new department, we will get the 404 error code together with the generic error we just defined in the body replace expression:

PostmanServerError

To finish the exception handling, we need to add the same error handler to the updateDepartment operation. A quick way to do this is to right-mouse-click on the createDepartment error handler and choose Copy from the popup menu. Then right-mouse-click on the updateDepartment RouteNode and choose Paste from the popup menu. However, a better and more reusable way to do this is to create a pipeline template and define the error handler in the template. This prevents duplication of identical error handlers and it allows us to change the error handler over time in the template, with the changes being picked up automatically by all pipelines based on this template. We will look into pipeline templates in more depth later on in this article series.

Downloads:

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha