Error handling in IBM Worklight adapters

As companies extend access to their enterprise services to mobile devices, concerns about data security, scalability, and data transparency must be addressed. The IBM® Worklight® mobile application framework addresses these concerns through its adapter mechanism. Worklight adapters are components that are deployed to the server on the Worklight mobile application platform to access enterprise services. They serve as a mediator or gateway between mobile applications and enterprise systems, receiving requests from mobile applications and returning to them the data fetched from enterprise systems. When designing adapters, it’s crucial that error handling be thought through carefully with the goal of delivering error information to mobile applications in a clear and consistent manner so as to reduce mobile application complexity. This article provides best practice recommendations for adapter error handling, derived from real-world experiences developing Worklight-based applications and adapters. This content is part of the IBM WebSphere Developer Technical Journal.

Bill Paris, Software Developer, IBM

Bill Paris is a software developer consulting for the IBM Software Services for WebSphere group. He specializes in the development of mobile applications and server middleware.



05 December 2012

Also available in Chinese Vietnamese

Introduction

Get Worklight now

Download IBM Worklight Developer Edition 5.0 now at no cost, and with no expiration date!

As the mediator between mobile applications and enterprise systems, IBM Worklight adapters provide secure access to enterprise systems and enhance data transparency by presenting enterprise data to mobile devices in a uniform data format.

Worklight provides three adapter types:

  • HTTP adapters provide access to HTTP-based enterprise services, including RESTful and SOAP-based services.
  • SQL adapters provide access to enterprise databases.
  • Cast Iron® adapters initiate orchestrations in IBM WebSphere Cast Iron.

Coded in JavaScript™, adapters run server-side on the IBM Worklight mobile application platform, which uses the Rhino JavaScript engine for executing the JavaScript source code. Figure 1 depicts a simplified view of the adapter framework within the larger Worklight platform.

Figure 1. The adapter framework
Figure 1. The adapter framework

Conceptually, an adapter is a set of JavaScript functions that can be remotely invoked by an application. From a development standpoint, an adapter consists of:

  • An XML file for configuring connectivity from the adapter to the enterprise system and to declare the adapter procedures available for invocation by the mobile application.
  • A JavaScript file containing the implementation of the JavaScript adapter procedures (functions).

The two files are bundled into a .adapter archive file that is then deployed to the IBM Worklight Server. Once deployed, the adapter procedures are ready to be invoked by Worklight applications running on mobile devices and in browsers.

See Resources for information that provides a deeper understanding of adapters.

Adapter error processing

Adapter procedures are JavaScript functions that can take any number of parameter values and return a JavaScript object to the calling client application. The example described here for the purpose of this article is a mobile banking application and focuses on an example adapter procedure named transfer, which invokes a RESTful enterprise service to transfer money between bank accounts.

As shown in Listing 1, the transfer procedure accepts three input parameter values, uses the Worklight API function WL.Server.invokeHttp to invoke the transfer service on the enterprise system, then returns the response information sent by the enterprise system back to the caller.

The WL.Server.invokeHttp function is part of the Worklight server-side API set and is used to invoke a RESTful (HTTP) service. For database operations, SQL adapters can make use of the WL.Server.invokeSQLStatement and WL.Server.invokeSQLStoredProcedure API functions.

Listing 1. The transfer adapter procedure
1	// Adapter procedure to invoke the bank’s transfer service 
2	// via HTTP	   
3	function transfer (fromAccount, toAccount, amount) {	   
4		   
5	   // Build the WL.Server.invokeHttp input object to invoke the
6	   // transfer service	   
7	   var service = "transferservice?fromAccount=" +fromAccount+
8	      "&toAccount=" +toAccount+ "&amount=" +amount;	   
9	   var input = {	   
10	      method              : 'get',	   
11	      returnedContentType : 'json',	   
12	      path                : service	   
13	   };	   
14		   
15	   // Invoke the enterprise service to the transfer funds 	   
16	   var response = WL.Server.invokeHttp(input);	   
17		   
18	   // Return the response received from the transfer service to 
19	   // the caller	   
20	   return response;		   
21	}

Adapter call flow

To form the basis of this error handling discussion, let’s step through a typical application-to-adapter execution flow:

  1. The flow starts with a Worklight-based application invoking an adapter procedure by using the Worklight client-side API function WL.Client.invokeProcedure. The caller passes in adapter information – adapter name, procedure and parameters – and options, such as success and failure response handler functions, as shown in Listing 2.
    Listing 2. Client call to the adapter transfer procedure
    1	WL.Client.invokeProcedure(	
    2	   // Invocation data
    3	   {
    4	      adapter    : "BankServiceAdapter",
    5	      procedure  : "transfer",
    6	      parameters : [fromAccount, toAccount, amount]
    7	   },
    8	   // Options object
    9	   {
    10	      // Success handler function
    11	      onSuccess : function(resp) {
    12	      var confirmationCode = 
    13	         resp.invocationResult.transferResult.confirmationCode;
    14	         WL.Logger.debug("Transfer succeeded, conf code = "
    15	            + confirmationCode);	
    16	      },
    17	      // Failure handler function
    18	      onFailure : function(resp) {
    19	         WL.Logger.debug("Transfer failed, errors = " 
    20	            + resp.invocationResult.errors.join(",") );
    21	      }
    22	   }
    23	);
  2. The transfer adapter procedure is then invoked on the Worklight Server and the procedure carries out an enterprise operation, such as invoking a RESTful transfer enterprise service (shown in Listing 1).
  3. The procedure completes and returns a response object.
  4. Depending on the success of the procedure invocation, the caller’s success handler or failure handler function is called and is passed a response object.

Now, let’s take a closer look at the response object and at the conditions that determine whether the success or failure handler gets called.

Success handler response object

The success handler response object can contain the properties shown in Table 1 (and possibly additional properties).

Table 1. Handler response object
Property Description
invocationContextThe invocationContext object if passed from mobile client in the WL.Client.invokeProcedure options object.
statusThe HTTP response status (for HTTP adapters only).
invocationResultAn object containing the data returned by the invoked adapter procedure, and additional invocation status. Its format is:

invocationResult = { isSuccessful: Boolean, errors : ["error message", “error message” …], warnings : ["warning message", “warning message” …], info : ["info message", “info message” …], // Procedure results go here }

Where:
  • isSuccessful – Is set true if a connection was established with the back-end, false otherwise
  • errors – An array of strings containing error messages
  • warnings – An array of strings containing warning messages
  • info – An array of strings containing informational messages

For example, the success handler for the transfer adapter procedure call might receive a response object with this invocationResult property (Listing 3).

Listing 3. Example invocationResult delivered to success handler
invocationResult : {
     isSuccessful: true,
     errors : [],
     warnings : [],
     info : [],
     statusCode: 200,
     statusReason: "OK",
     transferResult : {
          confirmationCode : "446183348",
          amount : "$2000.00"
     }
}

In this example, the first six properties are automatically supplied by the Worklight platform and the transferResult property is part of the procedure results that were included in the response object by the adapter procedure.

Failure handler response object

The failure handler is called for both server-side errors and client-side errors that occur during the call to the adapter procedure. These errors can be distilled down to two cases: a technical failure, such as server connection failure or a timed out call that resulted in the procedure not being called, and an application level failure in which the procedure was called but failed.

When a technical failure occurs, the response object passed to the failure handler contains the properties shown in Table 2.

Table 2. Failure handler response object properties
Property Description
invocationContextThe invocationContext object if passed from mobile client in the WL.Client.invokeProcedure options object.
statusThe HTTP response status (for HTTP adapters only).
errorCodeAn error code string
errorMsgAn error message provided by the IBM Worklight Server.

For example, when the connection to the Worklight Server was terminated just before an SQL adapter procedure was invoked, the response object received by the failure handler contained these three properties:

invocationContext : ""
errorCode : "UNRESPONSIVE_HOST"
errorMsg : "The service is currently not available. "

For the second condition that leads to the failure handler being called — the procedure was called but failed — the failure handler is passed the same response object shown in Table 1, but the isSuccessful property is set to false. For example, when the HTTP adapter procedure in Listing 1 was invoked while the enterprise transfer server was offline, the response object received by the failure handler contained the properties in Listing 4.

Listing 4. Example invocationResult delivered to failure handler
invocationResult {
     errors: [
          "Runtime: Http request failed: org.apache.http.conn.HttpHostConnectException: 
          Connection to http://221.16.94.228:9080 refused"
     ],
     warnings: [],
     info: [],
     isSuccessful: false
}

Another situation that leads to the failure handler being invoked is when the response data returned by the enterprise service cannot be transformed as directed by the returnedContentType input parameter passed to WL.Server.invokeHttp.

The case for expanding error handling in adapter procedures

The failure handling discussion in the previous section was focused on situations in which the Worklight platform detects adapter errors and consequently invokes the calling client’s failure handler. By default, Worklight adapters are indifferent to (or unknowing of) application level failures. For example, if an HTTP adapter procedure invokes an enterprise service that subsequently responds with an HTTP 500 Internal Server Error, the Worklight platform will invoke the calling client’s success handler — not the failure handler — and pass it a response object containing the properties in Listing 5.

Listing 5. InvocationResult passed to success handler with HTTP 500 Internal Server Error
invocationResult {
     isSuccessful : true,
     errors : [],
     warnings : [],
     info : []
     statusCode : 500,
     statusReason : "Internal Server Error"
}

This situation puts a burden on client application developers to have a deep understanding of the adapter’s interaction with the enterprise system so as to detect the range of errors that can occur. While every developer enjoys coding the happy path – that mythical scenario in which processing always succeeds – in reality, writing error handling code is a necessity, even though it often doubles or triples the coding effort. Therefore, any error handling technique that simplifies the client application is likely to result in cleaner code and a more robust application.

Based on early experiences in developing Worklight client applications and adapters, a colleague and I sought to improve our adapter error handling practices in subsequent Worklight projects. After some consideration, two guidelines were established:

  1. Adapter procedures should use a consistent mechanism for reporting errors to calling clients so that clients don’t need to have knowledge of adapter-to-enterprise service specifics.
  2. This mechanism should fit within the existing Worklight response handling pattern.

These guidelines were easy to meet thanks to the flexibility of Worklight adapter response handling. Specifically, by taking advantage of the existing errors property present in every adapter reponse to provide application level error information, the client application needs only to look in one place for error information and is freed from having to understand adapter and enterprise service specifics.

As an example, Listing 6 updates the transfer adapter procedure shown earlier with additional code to test for application level errors (lines 15-33) and then to provide error information to the client application in the response’s errors property.

Listing 6. Transfer adapter procedure updated to handle application level errors
 1	function transfer (fromAccount, toAccount, amount) {
2
3	   // Build the WL.Server.invokeHttp input object
4	   var service = "transferservice?fromAccount=" +fromAccount+
5	      "&toAccount=" + toAccount+ "&amount=" +amount;
6	   var input = {
7	      method              : 'get',
8	      returnedContentType : 'json',
9	      path                : service
10	   };
11
12	   // Invoke the enterprise service to the transfer funds 
13	   var response = WL.Server.invokeHttp(input);
14
15	   // Check for application level errors
16	   if (response.statusCode == 200) {
17	      // Did we receive a transfer confirmation code?
18	      if (response.transferResult.confirmationCode != "") {
19	         WL.Logger.debug("Successful transfer, conf code = " 
20	            + response.transferResult.confirmationCode);
21	      } else {	
22	         // Tell client that transfer failed
23	         response.errors.push("Transfer failed, errNo = " 
24	            + response.transferResult.errNo);
25	         WL.Logger.debug(response.errors);
26	      }
27	   } else {	
28	      // Tell client that transfer failed with non-success 
29	      // HTTP status
30	      response.errors.push("Transfer failed, HTTP status ="
31	          + response.statusCode);
32	      WL.Logger.debug(response.errors);
33	   }
34
35	   // Return response received from transfer service to caller
36	   return response;
37	}

Giving the adapter the ability to handle application level errors and communicate error information back to the client application enabled us to easily meet our guidelines, and forms the basis of the error handling best practice recommendations in the next section.


Adapter error handling recommendations

1. Shield client applications from adapter-to-enterprise system specifics

Don’t mess with the internals

While explicitly setting the isSuccessful property in an adapter procedure is tempting — its value controls whether the calling application’s success or failure handler gets invoked — the resulting behavior is undocumented and could change in future releases. Therefore, the isSuccessful value set by Worklight API calls is best left untouched.

As discussed earlier, the Worklight platform does not treat application level errors as failures. For example, when an adapter invokes an HTTP service request, a Connection Refused response is considered a failure and is directed to the failure handler, but an HTTP response status 500 Internal Server Error from the enterprise service is not seen as an error and is directed to the success handler. Enterprise service failure situations should be handled by the adapter in a way so as to free client applications from the need to know how the adapter and enterprise service interact.

This approach is shown in Listing 7, in which the adapter procedure invokes a service that returns a set of transfer-specific response codes on failure. Catching these types of failures in the adapter procedure instead of the client application shields the application from enterprise service error specifics.

Listing 7. Failure handling when the enterprise service indicates an error
 1	// Issue the Transfer Funds HTTP request
2	var response = WL.Server.invokeHttp(input);
3	
4	// Any problems?
5	if (response.transferResult.errNo != 0) {
6	   if (response.transferResult.errNo == 1) {
7	      response.transferResult.errors.push(
8	         "Transfer service is down for maintenance");
9	   } else if (response.transferResult.errNo == 2) {
10	      response.errors.push(
11	         "Transfer not permitted between these accounts");
12	   } else {	
13	      response.errors.push(
14	         "Transfer service failed with unknown errNo: "  
15	            + response.transferResult.errNo);
16	   }
17	   WL.Logger.debug(response.errors);
18	}

2. Report error information to the calling client in a consistent manner

It is a necessity to return a failure indication to the caller. In addition, you should always provide this indication in a consistent location, such as in a specific response property. One logical property for this error information is the errors array that the Worklight platform already provides in the response object. This practice is employed in the adapter code in Listing 7 (lines 7, 10 and 13).

Additional error-related properties could be added to the response if desired, such as an error code property.

3. Code adapter procedures defensively

It is good programming practice to catch errors made by the calling client when invoking the adapter procedure, as shown in Listing 8 (lines 4 – 17).

Listing 8. Catching invalid input parameters
 1	var errors = [];
2	
3	// Any missing params?
4	if (typeof fromAccount == 'undefined') {
5	   errors.push("Missing fromAccount parameter. ");
6	}
7	if (typeof toAccount == 'undefined') {
8	   errors.push("Missing toAccount parameter. ");
9	}
10	if (typeof amount == 'undefined') {
11	   errors.push("Missing amount parameter. ");
12	}
13	
14	// Transfer more than $10000 no permitted	
15	if (amount > 10000) {	
16	   errors.push("Transfer amount must be $10000 or less. ");
17	}
18	
19	// Any errors?	
20	if (errors.length > 0) {
21	   // Don’t proceed, tell calling client what they did wrong
22	   return resp = {
23	      "errors": errors
24	};

For additional robustness, it would also be wise to handle run time errors using try/catch/finally in adapter procedures, where appropriate.

4. Test, test, test

Adapter procedure error handling is generally easy to test given the adapter-related testing mechanisms provided by the Eclipse-based IBM Worklight Studio. Studio options provide for deploying an adapter to Worklight Studio’s built-in Worklight Server, invoking an adapter procedure, and directly invoking a back-end enterprise service.


Conclusion

In summary, our adapter error handling best practices include:

  • Code the adapter to handle enterprise service failures in such a way as to shield the calling client application from having to know about service details. This practice leads to simpler client application logic.
  • Normalize error information returned to the application client such as by providing error information in the response object’s errors array, so that the application needs only to check one place to determine success or failure.
  • Programming 101: Take time to add robust and defensive error handling code in adapter procedures and test the error handling thoroughly to save debugging time in QA and embarrassment in production.

Acknowledgements

The author thanks Karl Bishop for contributing his technical expertise in the development of these best practices, and his colleagues Tom Thacher, Raanan Avidor, and Gang Chen for their technical guidance.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Mobile development
ArticleID=848564
ArticleTitle=Error handling in IBM Worklight adapters
publish-date=12052012