The IBM Global Solution Center team in Dallas developed a mobile application for retail banking. The app included many retail banking functions, such as branch and ATM location, contact, account balances and activities, funds transfer, and more. Designed for and deployed onto both iOS and Android smartphones, the app was built using IBM Worklight Studio with a hybrid approach. Dojo Mobile, a JavaScript framework for developing cross-platform mobile web applications, was used as the primary toolkit for implementing the mobile user interface, along with Worklight widgets.
The article describes key technical aspects in the design and development of the mobile application, including the design and implementation of the mobile user interface using Dojo Mobile, the Worklight project structure and environment optimization, the use of Dojo to reduce the effort of developing environment-specific artifacts, and the steps in developing a Worklight adapter — an essential component for integrating the application with enterprise systems. The integration with enterprise systems is illustrated in the included sample code using a REST service that simulates the account information in a banking system. The implementation of a security mechanism using Worklight authentication to protect resources (such as Worklight adapters) is also discussed. Sample code (developed and tested on Worklight Studio Developer Edition v5.0.0.0), as a subset of the overall mobile application, is included for illustration purposes.
Included here is information to help you get started:
- Implementing a mobile user interface using Dojo Mobile
- Worklight project structure using Dojo
- Developing Worklight adapters
- Implementing Worklight authentication
Implementing a mobile user interface using Dojo Mobile
Figure 1 shows the home screen of the banking app on an iPhone. From here, the user can access account information and initiate transactions, locate an ATM or branch, and initiate contact for help.
There are two common UI design approaches you might consider for enabling a user to navigate between functions:
- The tab bar approach uses a list of common functions, each shown as a button on the tab bar that is available on most screens. The user taps on a button to navigate to a specific function. This approach provides fast, one-click navigation to common functions. The drawback is the additional screen space used for the tab bar, reducing the screen space available for the functional area.
- The list-of-functions approach lists most (if not all) functions as buttons on the screen. The advantage here is that each function gets more screen space to work with. The main drawback is that additional clicks are required for navigating, even to commonly used functions.
Both approaches are used in the sample mobile application described here. The list-of-functions approach is shown in Figure 1, in which three main functions (My Accounts, Locations and Contact) are listed as buttons. When the user navigates to My Accounts after authentication, common functions such as Accounts, Cash and Transfer are listed as buttons on the bottom tab bar, shown in Figure 2. Additional functions are displayed by clicking the More tab.
Figure 1. Home screen
Figure 2. Accounts screen
Dojo Mobile is an HTML5 mobile JavaScript™ framework that enables rapid development of mobile web applications with a native look and feel on modern mobile devices, such as iPhone, iPod Touch, iPad, and on Android and RIM smartphones and tablets. Using the hybrid approach, a Dojo Mobile app can make use of native device APIs, such as GPS location or barcode scanning, and can easily be packaged as a native app using IBM Worklight Studio.
Figure 3 shows the Accounts screen implemented using Dojo Mobile widgets (all the widgets noted in this section are a part of the dojox.mobile package):
- The overall view (not shown) is a dojox.mobile.View, which contains a ScrollableView at the top and a TabBar at the bottom.
- The top ScrollableView shows a scrollable list of accounts, with a Heading at the top and an EdgeToEdgeList at the bottom.
- The Heading shows the title "Accounts" and contains a Back button and a ToolBarButton, "Logout."
- The EdgeToEdgeList contains a list of ListItem, each showing an account summary.
- The bottom TabBar contains a list of TabBarButton, each representing a common function such as Accounts, Cash, Transfer.
Figure 3. Account screen with Dojo Mobile widgets
Listing 1 shows the code used to implement these widgets on this screen.
Listing 1. Code for Accounts screen in Figure 3
<div data-dojo-type="dojox.mobile.View" id="LoggedInView" class="mobileClass"
style="height: 100%;">
<div id="AccountsView" data-dojo-type="dojox.mobile.ScrollableView"
data-dojo-props="selected:true" class="mobileClass">
<h2 data-dojo-type="dojox.mobile.Heading"
data-dojo-props="fixed:'top', back:'Back', moveTo:'IndexView', fixed:'top'"
style="font-size: 19px;" align="center">Accounts
<div id="logoutBtn" data-dojo-type="dojox.mobile.ToolBarButton"
data-dojo-props="label:'Logout', moveTo:'IndexView',
transition:'slide',transitionDir:'-1'"
style="width: 45px; float: right;" class="mblColorBlue"></div>
</h2>
<ul id="accountContainer" style="margin-top: 40px;"
data-dojo-type="dojox.mobile.EdgeToEdgeList" class="indexListStyle">
<!-- List of Accounts will be populated at runtime using the custom widget template
defined under folder 'custom/AccountWidget' -->
</ul>
</div>
<ul data-dojo-type="dojox.mobile.TabBar" data-dojo-props="fixed:'bottom'" style="height:
11%; overflow-x: hidden;">
<li data-dojo-type="dojox.mobile.TabBarButton" id="accountsTab" style="width: 20%"
data-dojo-props="icon1:'./images/tabs/icon_128x128_accounts.png',
icon2:'./images/tabs/icon_128x128_accounts_blue.png', selected:'true'">Accounts</li>
<li data-dojo-type="dojox.mobile.TabBarButton" style="width: 20%"
data-dojo-props="icon1:'./images/tabs/icon_128x128_cash_gradient.png',
icon2:'./images/tabs/icon_128x128_cash_blue.png'">Cash</li>
<li data-dojo-type="dojox.mobile.TabBarButton" style="width: 20%"
data-dojo-props="icon1:'./images/tabs/icon_128x128_transfer_solid.png',
icon2:'./images/tabs/icon_128x128_transfer_solid_blue.png'">Transfer</li>
<li data-dojo-type="dojox.mobile.TabBarButton" style="width: 20%"
data-dojo-props=" icon1:'./images/tabs/icon_128x128_billPay.png',
icon2:'./images/tabs/icon_128x128_billPay_blue.png'">Bill Pay</li>
<li data-dojo-type="dojox.mobile.TabBarButton" style="width: 20%"
data-dojo-props="icon1:'./images/tabs/icon_128x128_more.png',
icon2:'./images/tabs/icon_128x128_more_blue.png'">More</li>
</ul>
</div> |
Worklight project structure using Dojo
Worklight Studio provides an integrated development environment for cross-platform mobile applications. The Worklight project structure contains an apps folder that can contain one or more applications. An application can have multiple Worklight environments, depending on the platforms to be supported by the application. OpenFNApp in Figure 4 contains a common folder and both android and iphone environments for creating applications to be deployed to Android and iPhone devices. The dojo folder is automatically included when you create a Dojo-enabled application in Worklight.
Figure 4. Worklight project structure
The common folder in Figure 5 contains artifacts that will be shared by all environments. It contains a main HTML file (OpenFNApp.html) that is loaded on application startup, and other typical web resource folders such as js, css, and images.
Figure 5. Common folder structure
Environment-specific files should be included in the iphone or android folder, as appropriate. These folders have the same folder structure as the common folder. For example, if there are certain styles that need to be applied to a device-specific environment, then a CSS file can be created under the appropriate folder with the same name as the corresponding file in the common/css folder. When the native project is built, Worklight will append environment-specific css files to the common css files with the same names when generating the native code. The environment-specific styles will take precedence over the styles included in the common folder. Similarly, if the main HTML file is placed under the android or iphone folder, this HTML file will replace the main HTML file in the common folder and will be used by Worklight to build the native application. Environment-specific JavaScript files with the same names will be appended to each other. In the application described here, because of the cross-platform capability in Dojo, most of the code is in the common folder and shared in both iphone and android environments. Environment-specific code is added only when it is needed. For example, the OpenFN.css files in the android and iphone environments contain different height rules for AccountsView (Listing 1), whereas all the common application styles are defined in the common/css/OpenFN.css file.
Figure 6. Environment-specific file
A custom widget, AccountWidget, is used to display account information within the application. This widget is included in the common/custom folder (see Resources for details on implementing custom widgets). Listing 2 shows the JavaScript code to load the custom widget.
Listing 2. Code to load custom widget
<script>
var base = location.href.split("/");
base.pop();
base = base.join("/");
var dojoConfig = {
isDebug : false,
async : true,
parseOnLoad : false,
packages : [ {
name : "custom",
location : base + "/custom"
} ]
};
</script> |
For cross-platform application development, the dojox/mobile/deviceTheme module (Listing 3) automatically detects the device and includes the necessary stylesheets for rendering on that device. This facilitates a native look and feel of the application regardless of platform while utilizing the same code base.
Listing 3. Loading Dojo modules
<script type="text/javascript">
require(// module identifiers
["dojo/parser", "dojo/_base/connect", "dojox/mobile",
"dojox/mobile/deviceTheme", "dojox/mobile/compat",
"dojox/mobile/Button", "dojox/mobile/TabBar", "dojox/mobile/TabBarButton",
"dojox/mobile/ScrollableView"],
// Callback function invoked on dependencies evaluation results
function(parser, connect) {
dojo.ready(function() {
parser.parse();
dojo.style("content", "display", "");
connect.connect(dijit.byId("myAccountsListItem"), "onClick", null, getAccounts);
});
});
</script> |
Worklight adapters provide a single interface for a client application to communicate with backend systems. There are several types of adapters, including HTTP connections, SOAP services, SQL database calls, and more. These adapters can be secured and monitored.
An AccountsAdapter is used here to invoke a REST service that retrieves the user’s account information. The adapter consists of these files (Figure 7):
- A configuration XML file for specifying connectivity options and listing the procedures exposed to the client application or other adapters.
- A JavaScript file for implementing procedures declared in the configuration XML file.
- Optional XSL files for transforming the retrieved raw XML data.
Figure 7. Worklight adapter files
The configuration XML file for AccountsAdapter (Listing 4) specifies the connection details (such as connection policy that includes protocol, domain, and port) the adapter needs for the connection to retrieve the account information. The adapter resource is protected by using the Realm defined in the authenticationRealm attribute, which ensures the user is authenticated before the adapter procedure is invoked. (Authentication protection is set up in the next section.)
Listing 4. Configuration XML for AccountsAdapter
<xml version="1.0" encoding="UTF-8"?>
<wl:adapter name="AccountsAdapter" authenticationRealm="AccountsAuthRealm"
. . . . . . .
<displayName>AccountsAdapter</displayName>
<description>AccountsAdapter</description>
<connectivity>
<!--The configuration below assumes the REST service is deployed
on localhost with port 9080 -->
<connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
<protocol>http</protocol>
<domain>localhost</domain>
<port>9080</port>
</connectionPolicy>
<loadConstraints maxConcurrentConnectionsPerNode="2" />
</connectivity>
<procedure name="getAccounts" requiresAuthentication="true"/>
</wl:adapter> |
The adapter JavaScript file (Listing 5) contains the implementation code for the procedure defined in the configuration XML file. The procedure getAccounts invokes a REST service that retrieves account information for an authenticated user. There is no need for the client application to pass the user information to the adapter procedure since it can be obtained using WL.Server.getActiveUser() once the user is authenticated. The account information REST service is invoked using the WL.Server.invokeHttp call, which returns the data back to the client application in JSON format.
Listing 5. Adapter JavaScript code
function getAccounts() {
. . . .
var userObj = WL.Server.getActiveUser();
var serviceURL = "/OFNWebServices/rest/account/getAccounts";
. . . . .
return WL.Server.invokeHttp({
method : 'get',
returnedContentType : 'json',
path : serviceURL,
parameters : {
username : userObj.userId
}
});
} |
Implementing Worklight authentication
Entities like Worklight adapters can be protected by an authentication process that defines steps to be taken before an entity is accessed. AccountsAdapter is protected; the user needs to be authenticated before retrieving the account information using the back end REST service. Worklight authentication is used to implement this security measure:
- First, an authentication realm is defined in the authenticationConfig.xml file in the folder server/conf. The realm named AccountsAuthRealm (Listing 6) specifies the login-function and logout-function attributes. These attributes define the adapter methods to call when the login and logout functions are performed on the server end.
Listing 6. Authentication realm in authenticationConfig.xml<realm name="AccountsAuthRealm" loginModule="AccountsAuthModule"> <className>com.worklight.integration.auth.AdapterAuthenticator</className> <parameter name="login-function" value="AuthenticationAdapter.onAuthRequired" /> <parameter name="logout-function" value="AuthenticationAdapter.onLogout" /> </realm>
- A login module is then defined in the same authenticationConfig.xml file. In Listing 7, AccountsAuthModule uses com.worklight.core.auth.ext.NonValidatingLoginModule, indicating that the authentication will be performed by a developer-defined process within the adapter.
Listing 7. Login module in authenticationConfig.xml<loginModule name="AccountsAuthModule" canBeResourceLogin="true" isIdentityAssociationKey="false"> <className>com.worklight.core.auth.ext.NonValidatingLoginModule</className> </loginModule>
The login and logout functions are defined in the adapter named AuthenticationAdapter (Listing 8).
Listing 8. onAuthRequired and onLogout functions in AuthenticationAdapter-impl.jsfunction onAuthRequired(headers, errorMessage){ errorMessage = errorMessage ? errorMessage : null; return { authRequired: true, errorMessage: errorMessage }; } function onLogout(){ WL.Logger.debug("Logged out"); }
- In Listing 9, procedure submitAuthentication is defined in the AuthenticationAdapter.xml file. This procedure will be invoked from the client application for validating user credentials.
Listing 9. submitAuthentication in AuthenticationAdapter.xml<?xml version="1.0" encoding="UTF-8"?> <wl:adapter name="AuthenticationAdapter" . . . . <procedure name="submitAuthentication"/> </wl:adapter>
Procedure submitAuthentication (see Listing 10) takes in the username and password as parameters that can be validated against a user registry. If successful, the userIdentity object is created and placed in the pool of active users using the WL.Server.setActiveUser() method. A JSON object with parameter authRequired:false is also returned, indicating a successful authentication. If the user is not authenticated, a JSON object with parameter authRequired:true is returned indicating that authentication is still required.
Listing 10. submitAuthentication in AuthenticationAdapter-impl.jsfunction submitAuthentication(username, password) { if ((username=="wl" && password == "wl") || (username=="worklight" && password == "worklight") ){ WL.Logger.debug("Auth :: SUCCESS"); var userIdentity = { userId: username, displayName: username, attributes: {} }; WL.Server.setActiveUser("AccountsAuthRealm",userIdentity); return { authRequired: false }; }else{ WL.Logger.debug("Auth :: FAILURE"); return onAuthRequired(null, "Invalid login credentials"); } }
- Completing the authentication on the server side, AccountsAdapter is protected by specifying the realm AccountsAuthRealm for the authenticationRealm attribute in AccountsAdapter XML (Listing 11).
Listing 11. authenticationRealm in AccountsAdapter.xml<?xml version="1.0" encoding="UTF-8"?> <wl:adapter name="AccountsAdapter" authenticationRealm="AccountsAuthRealm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wl="http://www.worklight.com/integration" xmlns:http="http://www.worklight.com/integration/http"> . . . . <procedure name="getAccounts" requiresAuthentication="true"/> </wl:adapter>
Now, let’s look at authentication implemented on the client side.
- A JavaScript file auth.js is automatically generated to provide method skeletons to implement the Worklight authentication. The init function (Listing 12) is used for initialization. An event handler is defined for clicking the Sign On button (with ID submitAuthBtn). The event handling function invokes the authentication adapter procedure AuthenticationAdapter.submitAuthentication by passing the username and password from the input fields. Similarly, the event handler for clicking the Logout button (with ID logoutBtn) is also defined by invoking WL.Client.logout(‘AccountsAuthRealm’).
Listing 12. init function in js/auth.jsinit : function() { // Authenticator initialization code require([ "dojo", "dojo/_base/connect" ], function(dojo, connect) { connect.connect(dojo.byId("submitAuthBtn"),"onclick",null,function() { var username = document.getElementById("usernameInputField").value; var password = document.getElementById("passwordInputField").value; var invocationData = { adapter : "AuthenticationAdapter", procedure : "submitAuthentication", parameters : [ username,password ] }; WL.Client.invokeProcedure( invocationData, {onSuccess : signOnSuccess, onFailure : signOnFailure }); }); connect.connect(dojo.byId("logoutBtn"), "onclick", null, function() { WL.Client.logout('AccountsAuthRealm'); }); }); } - The isLoginFormResponse() function (Listing 13) is invoked once a response is received from the server side. The function returns a boolean indicating if the authentication is successful or not.
Listing 13. isLoginFormResponse function in js/auth.jsisLoginFormResponse : function(response) { if (!response || !response.responseJSON || response.responseText == null) { return false; } return response.responseJSON.authRequired; } - The onBeforeLogin() function (Listing 14) is used to clear input fields and prepare for the display of an authentication view. The authentication view can be a login page with inputs for username and password.
Listing 14. onBeforeLogin function in js/auth.jsonBeforeLogin : function(response, username, onSubmit, onCancel) { // clean up the input login fields document.getElementById("usernameInputField").value = ""; document.getElementById("passwordInputField").value = ""; document.getElementById("AuthInfo").innerHTML = response.responseJSON.errorMessage; } - Function onShowLogin() or onHideLogin() (Listing 15) displays or hides the login page. These are automatically invoked based on the boolean returned from isLoginFormResponse. If the authentication is successful, onHideLogin will be invoked. Otherwise, onShowLogin will be invoked. In this example, the onHideLogin function does not do anything. Instead, a callback function signOnSuccess() is specified for invoking the authentication adapter (Listing 12). Function signOnSuccess() calls another function getAccounts() to change the screen from the login view to the accounts view.
Listing 15. onShowLogin / onHideLogin function in auth.jsonShowLogin : function() { require([ "dijit" ], function(dijit) { var currentView = dijit.byId("LoginView").getShowingView(); var currentViewId = currentView.get("id"); if (currentViewId != "LoginView") { currentView.performTransition("LoginView", 1, "slide", null); } }); }, onHideLogin : function() { } . . . . function signOnSuccess(response) { getAccounts(); }
This article described experiences using Worklight and Dojo Mobile to develop an end-to-end mobile application. Technical topics, such as mobile user interface implementation using Dojo Mobile, Worklight adapter development, and Worklight authentication, were described.
We thank our colleagues Rod Fleming and Catherine McCauley for their support that led to the success of this project.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code sample | OFNProjectSampleCode.zip | 30 MB | HTTP |
Information about download methods
Learn
-
IBM
Worklight user documentation
-
IBM
Worklight features and benefits
-
Dojo documentation
-
Tutorial: Dojo custom widget
-
IBM developerWorks
Mobile zone
-
IBM developerWorks WebSphere
Get products and technologies
Discuss
Denzil Menezes has over 5 years of experience in J2EE systems development and integration. He is currently working on mobile application solutions at the IBM Global Solution Center, primarily using IBM Worklight and dojo mobile.
Karl Bishop is a Senior Software Engineer on the IBM Web Enablement and Support team. He works from the wilds of North Carolina. Karl works on various internal and external Web 2.0 and Mobile applications. He specializes in Dojo, Dojo Mobile, and Worklight based solutions.
Biao Hao is a Certified Senior IT Architect in the IBM Global Solution Center in Dallas. Currently he is leading the development of mobile banking solutions and demonstrations, using IBM Mobile Foundation technology. He has over 15 years of experiences with integration middleware and tools using WebSphere products and solution architecture and development for banking and insurance industry.




