Developing a client and server mashup mobile application with IBM Worklight

This article illustrates the development process of a hybrid mobile application that combines data, presentation, and functionality from several sources on both the client and server side. The main characteristics of such a mashup application are the combination, visualization, and aggregation of different sources. A variety of published web sources are utilized, as well as data from a relational database that could be hosted by an enterprise. You will see that IBM® Worklight V5 is an ideal platform for building such mashup-based hybrid applications that can be targeted for a broad variety of mobile devices. This content is part of the IBM WebSphere Developer Technical Journal.

Walter M. Jenny (wjenny@at.ibm.com), Executive Architect, IBM

Author photo of Walter JennyWalter Jenny is an Executive Architect in IBM's Software Group. He has over 20 years experience in application development and business process management. Walter holds a Master's degree in Computer Science, a Master’s in Business Administration, and a Doctorate in Business Administration.



12 September 2012

Also available in Chinese Vietnamese Portuguese

Introduction

Get Worklight now

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

IBM Worklight V5 provides an open, comprehensive, and advanced mobile application platform that can help you efficiently develop, run, and manage HTML5, hybrid, and native applications.

Using standards-based technologies and tools, mobile-optimized middleware, a variety of security mechanisms, and integrated management and analytics capabilities, Worklight simplifies the application development lifecycle across multiple mobile platforms, including iOS, Android, BlackBerry, and Windows® Phone. As an Eclipse-based visual development environment, Worklight helps you accelerate development, testing, and delivery of your mobile applications using open technologies such as HTML5, Apache Cordova, JavaScript™, and popular JavaScript frameworks such as Dojo, jQuery, and Sencha Touch.

To determine which technologies to use when developing a mobile application, you need to understand the nature of the different development approaches for mobile web, hybrid, and native applications:

  • Mobile web applications can run on nearly any device, as they do not hook into any native feature of the device (like local file store, push notifications, and so on). These applications are typically based on HTML5 in conjunction with CSS and JavaScript. Using these established technologies can help reduce your development cost because you can re-use available skills and do not require native platform developers. A single deployment, maintenance, and testing lifecycle is also less complex. In addition, you don’t have to worry about launching the application on proprietary app stores, which could reduce your potential revenue and make application maintenance more cumbersome.
  • Hybrid applications are comprised of HTML5, CSS, and JavaScript with access to native features of the mobile device. They offer all the advantages of mobile web applications, including the ability of the application to execute on many platforms, but can also leverage many native device features. This approach typically leverages a framework that “virtualizes” the native device capabilities. Such an abstraction is provided via a JavaScript bridge, like Apache Cordova (the former PhoneGap framework). This combination maximizes the user experience and provides the highest amount of flexibility and scalability to develop applications across a wide spectrum of devices. Different form factors and device capabilities can be addressed via skins.
  • Native applications are useful when you want to leverage all of a device’s native features, along with native look and feel. This approach is typically recommended when memory-intensive, complex device functions are needed. However, this approach is typically more expensive and less scalable because native code for different devices must be developed, maintained, and tested.

The hybrid application approach embraces Worklight’s strength combining the best of both worlds, supporting applications primarily written in HTML5, CSS, and JavaScript, but also enabling full access to device capabilities via Cordova as part of its mobile client runtime. Cordova is a mobile framework that enables you to natively target all smartphones with a single code base to an embedded WebView or Webkit on the device.

In this article, you will explore a number of Worklight features to create a hybrid application that blends data, presentation, and functionality from several sources on the client side, as well as on the server side. Sample code is included so you can test the sample in your environment.


Worklight architecture

The scenario described in this article uses several Worklight components (Figure 1):

  • IBM Worklight Studio is an Eclipse-based IDE that enables you to perform all the coding and integration tasks required to develop a fully operational application.
  • IBM Worklight Server is a Java-based gateway between applications, external services, and the enterprise back end infrastructure. Worklight Server contains security features to enable connectivity, multi-source data extraction and manipulation, authentication, direct update of web and hybrid apps and analytics, and operational management functions. Worklight Server can run as a dedicated application on a Java EE application server, such as WebSphere Application Server. For the purpose of this article, we will be using the lightweight Jetty HTTP server that comes with Worklight Studio.
  • IBM Worklight Device Runtime Components provide the SDK and client-side mobile application runtime for the generic and the various target environments. It also embeds the client part of the server integration functionality.
  • IBM Worklight Console is a web-based UI dedicated for the ongoing monitoring and administration of Worklight Server and its deployed apps, adapters, and push notifications.
Figure 1. Main Worklight components
Figure 1. Main Worklight components

For this scenario, imagine you must present information from several sources, all of which are related to a specific location. This could be sales-related information coming from field offices or geographical territories, all provided in a variety of formats. For the sake of simplicity here, we’ll only leverage well known public Internet sources.

All the code (HTML, CSS and JavaScript) will be developed in Worklight Studio and then deployed it to the Worklight server. Worklight provides an integration framework of adapters that connect to back end systems, deliver data to and from mobile applications, and perform any necessary server-side logic on this data. The Worklight Console will be helpful for controlling the application and also as a launching point.

The basic control flow of the sample application is shown in Figure 2.

Figure 2. Control flow
Figure 2. Control flow
  1. The mobile web application is loaded from Worklight Server.
  2. As part of the initial HTML file, a custom widget is created for calling the Google Maps API to render a map, initially with the user’s current location via the HTML5 geolocation feature.
  3. The application calls the server side adapter to provide a list of locations.
  4. The server side LocationAdapter retrieves an array of locations via JDBC from a DB2® database and sends it back to the mobile client.
  5. Whenever the user selects a new location, the position on the map is updated and related information is retrieved via the NewsAdapter.
  6. The NewsAdapter first collects data from the Google News Feed.
  7. It then uses chaining to call StockAdapter to retrieve stock figures from Yahoo! Finance and augments these two pieces of information.

Let’s look at the two major pieces you need to build this application – the client and the server parts – and then run them on a mobile simulator.


The client part

Let’s start with the client part.

  1. In Worklight Studio, create a new Worklight project by selecting File > New > Worklight Project or by clicking on Create Worklight Artifacts (Figure 3).
    Figure 3. Create Worklight Project
    Figure 3. Create Worklight Project
  2. Name the Project Mashup, select a Hybrid Application template, then click Next (Figure 4).
    Figure 4. Select a Hybrid Application
    Figure 4. Select a Hybrid Application
  3. Name your application Mashup. Select Add Dojo Toolkit, which enables your application to utilize the Dojo Javascript library capabilities, then click Finish (Figure 5).
    Figure 5. Create Worklight Application
    Figure 4. Select a Hybrid Application

This creates your basic Worklight project structure (Figure 6). (See IBM Worklight user documentation and Working with Worklight, Part 1: Getting started with your first Worklight application for details on these main elements.)

Figure 6. Basic Worklight project structure
Figure 6. Basic Worklight project structure

The next set of steps involves creating logic to render a map enriched with geolocation information. You could achieve this by writing the required JavaScript code in Mashup.js directly using the Dojo toolkit shipping with the Dijit framework, a rather powerful and comprehensive set of widgets. However, if you want to build a customized widget that can be reusable by others on your development team, a custom widget is a good approach.

Create a Dijit custom widget

For this scenario, you need a custom widget that shows data in a map related to a specific location. The first part of the development involves showing the current location and rendering it in a standard Google map. A custom widget will then be developed to encapsulate these reusable UI elements, along with a specific piece of additional functionality. The steps to create the custom Dojo widget include:

  1. Create the file structure for your custom widget

    It's generally considered a good practice to have a proper file structure for your custom Dijit work, and Worklight Studio supports that approach. You are going to create a folder named custom, which will represent your namespace. (You can use whatever name you like, but something meaningful is best, like the name of your application or organization.)

    Worklight Studio makes this step fairly easy:

    1. Right-click on the /Mashup/apps/Mashup/common folder in the Explorer view and select New > Dojo Widget. The New Dojo Widget wizard appears. For Module Name, enter custom; for Widget Name, enter LocationViewer. The HTML template and style sheet for the widget paths are populated automatically (Figure 7).
      Figure 7. New Dojo Widget wizard
      Figure 7. New Dojo Widget wizard
    2. Click Finish. Three files are created under the common folder (Figure 8).
      Figure 8. New Dojo Widget Wizard
      Figure 8. New Dojo Widget Wizard
  2. Create your widget class using declare

    Worklight Studio automatically opens the LocationViewer JavaScript source file in the editor, which contains the generated Dojo statement (Listing 1, comments removed).

    Listing 1. Create the custom widget JavaScript code
    define("custom/LocationViewer", [ "dojo", "dijit", "dijit/_Widget",
    		"dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
    		"dojo/text!custom/templates/LocationViewer.html" ], function(dojo,
    		dijit, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin) {
    	return dojo.declare("custom.LocationViewer", [ dijit._Widget,
    			dijit._TemplatedMixin, dijit._WidgetsInTemplateMixin ],
    {
    templateString : dojo.cache("custom",	"templates/LocationViewer.html"),
    constructor : function() {
    		},
    	postMixInProperties : function() {
    		},
    	postCreate : function() {
    		}
    	});
    });

    The AMD module format replaces dojo.provide and dojo.require with define when creating custom modules. These define calls are identical to require calls, except that the callback returns a value that is saved and used as the resolved value of the module. Worklight Studio declared your custom LocationViewer widget, using dijit/_WidgetBase and dijit/_TemplatedMixin as a base.

    Hint: if you want to get the same template created without using the wizard (for example, when working on an existing JS file) you can also use the built-in code generation engine by typing decl and then Ctrl+Space (Figure 9).

    Figure 9. Declaring a Dijit widget without the wizard
    Figure 9. Declaring a Dijit widget without the wizard

    Next, you need to define some properties, so that the widget is aware of what types of properties it will receive (Listing 2).

    Listing 2. Setting the properties
    zoom			: 10,
    address		: "",
    // Define reasonable defaults for mobile devices
    containerWidth	: "320px",
    containerHeight	: "460px",

    The postMixInProperties function is called when all variables inherited from super classes are "mixed in." Common operations for postMixInProperties are to modify or assign values for widget property variables defined in the template HTML file. All you do here is call the super class method (Listing 3).

    Listing 3. Setting the ‘mixed in’ properties
    postMixInProperties : function() {
    	this.inherited('postMixInProperties', arguments);
    },

    The postCreate method is called once your widget's DOM structure is ready, but before it is inserted into the page. This is typically the best place to put any sort of initialization code. For the time being, you will call the real work horse, the function refreshMap, which creates the Google map, with an empty address and no information. To keep the constructor chain, call the super class method (Listing 4).

    Listing 4. Initialize the custom widget
    postCreate : function()
    {
    this.refreshMap(this.address, "");
    	this.inherited('postCreate', arguments);
    },

    The real functionality is implemented in the refreshMap function. When called with an empty address and no information, you use the HTML5 geo-location feature to get the current position. Otherwise, you attempt to find the given address on the map using Google’s Geocoder. The content of the InfoWindow is passed as an argument (Listing 5).

    Listing 5. Drawing the map
    refreshMap : function(address, information)
    {
      var localZoom = this.zoom;
      var mapOptions = {
                zoom : localZoom,
                mapTypeId : google.maps.MapTypeId.ROADMAP
      };
      var map = new google.maps.Map(this.mapNode, mapOptions);
      if (address == null || address == "")
      {
        if(navigator.geolocation) // Try HTML5 geolocation
        {
          navigator.geolocation.getCurrentPosition(function(position)
          {
            var pos = new google.maps.LatLng(position.coords.latitude,
                                             position.coords.longitude);
            var infoWindow = new google.maps.InfoWindow({map : map,
                                             position :        pos,
                                             content : 'This your current location<br>' +
    		Found using <a href="http://dev.w3.org/geo/api/spec-source.html">HTML5 
    		Geolocation</a>'
          });
          map.setCenter(pos);
          infoWindow.open(map);
        }, function() {
          handleNoGeolocation(true);
          });
        }
        else // Browser doesn't support Geolocation
        {
          handleNoGeolocation(false);
        }
        return;
      }
      var geocoder = new google.maps.Geocoder();
      geocoder.geocode({'address' : address
      }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK)
        {
          map.setCenter(results[0].geometry.location);
          var infoWindow = new google.maps.InfoWindow({ content  : information,
    maxWidth : 230
          });
          var marker = new google.maps.Marker({ map      : map,
                                                position : results[0].geometry.location
          });
          infoWindow.open(map, marker);
          }
          else
          {
            alert("Error: " + status);
          }
        });
      },
      handleNoGeolocation : function (errorFlag)
      {
        …
      }

What is AMD?

The Asynchronous Module Definition (AMD) format is the new module format for Dojo 1.7 and beyond, replacing dojo.provide, dojo.require, dojo.requireIf, dojo.requireAfterIf, dojo.platformRequire, and dojo.requireLocalization. It provides many enhancements over the legacy Dojo module style, including fully asynchronous operation, true package portability, better dependency management, and improved debugging support. AMD is also a community-driven standard, which means that modules written to the AMD specification can be used with any other AMD-compliant loader or library. See Defining Modules for more information.

  1. Create the custom widget HTML code

    Templates should always have one parent wrapping element that contains all of the other elements. This can be whatever element you want, but it's important that it have just one root element. For your basic widget here, just use div as your wrapping element.

    In the Explorer view, double-click templates/LocationViewer.html to open the HTML template for the widget and complete your code as shown in Listing 6.

    Listing 6. Custom widget HTML code
    <div>
    	<div dojoAttachPoint="mapNode"
                 style="width:${containerWidth}; height:${containerHeight}">
    	</div>
    </div>

    Using dijit/_TemplatedMixin, you can use ${attribute} syntax to directly insert some values, like containerWidth. Nodes should be given an attach point, meaning that in your widget code, you can use that name to reference that node directly. This is akin to calling getElementById and you set up references in advance.

  2. Style as appropriate

    In case you need special CSS instructions, you can customize themes/LocationViewer.css.

  3. Use the custom widget in your application

    The custom widget is now available in the Other Dojo Widgets section of Worklight Studio palette (Figure 10). To use this widget in your project, Worklight Studio’s Rich Page Editor provides a great deal of support.

    Figure 10. Custom widget in the Worklight Studio template
    Figure 10. Custom widget in the Worklight Studio template

    In the Explorer view, double-click common/Mashup.html to open the HTML template. Remove the text “Mashup.” Drag a dojox.mobile.View from the “Dojo Mobile Widgets” palette to Mashup.html and drop it near the comment <!-- application UI goes here -->. Drag the locationViewer icon to Mashup.html and drop it within the dojox.mobile.View div. Add the reference to the Google map API (bold line in listing 7), and add an id for the LocationViewer either in the code or via the Properties tab.

    Depending on your personal preference, you can either write this code manually or take advantage of the Design Mode in Rich Page Editor to guide you in the placement of code when you drop a widget onto a container widget. Visual cues highlight possible drop locations and pop-up cues indicate editing functions that are available for the selected widget. For example, when you drag your LocationViewer from the Palette onto the View, you get a visual cue for placement. The Rich Page Editor uses embedded browsers to produce a visual representation of a web page in the Design view. (See Module 03.5 - Rich Page Editor for more details.)

    Figure 11. Design mode of the Rich Page Editor
    Figure 11. Design mode of the Rich Page Editor

    Worklight Studio will insert all the other required code (Listing 7).

    Listing 7. Custom widget in the main HTML file
    <!DOCTYPE html>		
    <html>
    	<head>
    		<meta charset="utf-8" />
    <meta name="viewport"
    	content="width=device-width, initial-scale=1, maximum-scale=1, 
    	user-scalable=no" />
    <title>Mashup</title>
    		<link rel="shortcut icon" href="images/favicon.png" />
    		<link rel="apple-touch-icon" href="images/apple-touch-icon.png" />
    		<link rel="stylesheet" href="css/reset.css" />
    		<link rel="stylesheet" href="css/Mashup.css" />
    <script type="text/javascript"
    	data-dojo-config="isDebug: false, async: true, parseOnLoad: true"
    	src="dojo/dojo.js"></script>
    <script type="text/javascript" src="dojo/core-web-layer.js"></script>
    <script type="text/javascript" src="dojo/mobile-ui-layer.js"></script>
    <script type="text/javascript" src="dojo/mobile-compat-layer.js"></script>
    <!--  add this line -->
    <script
    src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
    require(
    // Set of module identifiers
    [ "dojo", "dojox/mobile/parser", "dojox/mobile/View", "dojox/mobile", 
    	"dojox/mobile/compat", "dojox/mobile/deviceTheme", "custom/LocationViewer" ],
    // Callback function, invoked on dependencies evaluation results
    function(dojo) {
    	dojo.ready(function() {
    
    	});
    });
    </script>
    <meta name="apple-mobile-web-app-capable" content="yes">
    </head>
    	<body onload="WL.Client.init({})" id="content" style="display: none">
    	<div data-dojo-type="dojox.mobile.View" id="view0"
    		data-dojo-props="selected:true">
    		<div data-dojo-type="custom.LocationViewer" id="locationViewer"></div>
    	</div>
    
    	<script src="js/Mashup.js"></script>
    		<script src="js/messages.js"></script>
    		<script src="js/auth.js"></script>
    	</body>
    </html>

Running the application

To run and test this first version of the application, you need to publish it to a local Worklight server. Worklight Studio V5 contains an embedded local server you can use to test the code you created above. Right-click on apps/Mashup and select Run as > Build All and Deploy. This will start the local server on port 8080, activate the project, and then deploy the app for you to test in a browser.

Figure 12. Deployed application in the Worklight Console
Figure 12. Deployed application in the Worklight Console

You can open the application in the browser by clicking on either the icon or the Preview as Common Resources link. As expected, the current location is shown (Figure 13).

Figure 13. Preview the application
Figure 13. Preview the application

The server part

The next step is adding access to the back end resources. The tools of choice are Worklight Adapters, which connect to back end systems, deliver data to and from mobile applications, and perform any necessary server-side logic on this data. The adapter framework provides:

  • Flexible and powerful server-side JavaScript that produces succinct and readable code for integrating with back end applications and processing it. You can also use XSL to transform hierarchical back end data to JSON.
  • Support for read-only and transactional access modes to back end systems.
  • Flexible authentication facilities to create connections with back end systems. Adapters offer control over the identity of the user with whom the connection is made.
  • Data retrieved from back end applications is exposed in a consistent manner so that you can access data uniformly, regardless of its source, format, or protocol.

Create the LocationAdapter

Worklight provides these adapters:

  • SQL adapter
  • HTTP adapter (supports both REST and SOAP)
  • Cast Iron adapter

You will utilize the SQL adapter to access information contained within a relational database.

Adapter procedures are declared using XML and usually are implemented in JavaScript, but because an adapter is a server side entity you can use Java snippets in the adapter code. A procedure can process the data before or after calling the service.

The LocationAdapter is a very basic adapter that queries a DB2 database with the table shown in Listing 8.

Listing 8. SQL code to create DB2 database
CREATE SCHEMA TRAVEL;
SET SCHEMA = TRAVEL;
	
DROP TABLE travel.locations;
CREATE TABLE travel.locations
	(
	city varchar(10),
	country varchar(20),
	stock varchar(10)
	);

DELETE FROM travel.locations;
INSERT INTO travel.locations values ( 'Paris', 'France', 'FCHI');
INSERT INTO travel.locations values ( 'London', 'UK', 'FTSE');
INSERT INTO travel.locations values ( 'New York', 'US', 'DJI');
INSERT INTO travel.locations values ( 'Frankfurt', 'Germany', 'GDAXI');
INSERT INTO travel.locations values ( 'Tokyo', 'Japan', 'N225');
INSERT INTO travel.locations values ( 'Madrid', 'Spain', 'IBEX');

You can run this SQL code in the Database perspective of Worklight Studio, or in any similar tool. To create the LocationAdapter, right-click the apps/Mashup folder in the Explorer view and select New > Worklight Adapter. The New Worklight Adapter wizard appears (Figure 14).

Figure 14. New Worklight Adapter wizard
Figure 14. New Worklight Adapter wizard

For Adapter type, select SQL Adapter, and for Adapter name, enter LocationAdapter. This creates the project file structure shown in Figure 15.

Figure 15. Adapter file structure
Figure 15. Adapter file structure

You need to update LocationAdapter.xml to use the correct database connectivity. In this case, configure the data source for a DB2 database, and rename the standard procedure to getLocations (Listing 9).

Listing 9. LocationAdapter Connectivity
<displayName>LocationAdapter</displayName>
<description>LocationAdapter</description>
<connectivity>
	<connectionPolicy xsi:type="sql:SQLConnectionPolicy">
		<dataSourceDefinition>
			<driverClass>com.ibm.db2.jcc.DB2Driver</driverClass>
			<url>jdbc:db2://localhost:50001/TRAVELDB</url>
		<user>user</user>
		<password>password</password> 
		</dataSourceDefinition>
</connectionPolicy>
<loadConstraints maxConcurrentConnectionsPerNode="5" />
</connectivity>
<procedure name="getLocations"/>

Alternatively, you can right click on LocationAdapter.xml and select Open With > Adapter Editor to enter the configuration there (Figure 16).

Figure 16. Adapter Editor
Figure 16. Adapter Editor

The adapter logic is implemented in adapters/LocationAdapter/LocationAdapter-impl.js. A procedure declared in the adapter’s XML file must be implemented in the corresponding JavaScript file. For this basic SQL statement, there are two methods provided by the Worklight adapter framework that you can apply:

  • WL.Server.createSQLStatement() creates a prepared SQL statement, to be later invoked with WL.Server.invokeSQLStatement. The method can only be used inside a procedure declared within an SQL adapter. It must be used outside of the scope of any JavaScript function. The method accepts any valid SQL statement that include question marks (“?”) as parameter placeholders and returns an object representing the prepared statement.
  • WL.Server.invokeSQLStatement() takes these parameters:
    • the preparedStatement, returned by the method above
    • an optional array of parameters to the prepared statement
    • transformation, an optional XSL transformation for the response

    The method returns the result set of the prepared statement, after the optional processing. (See the IBM Worklight V5.0 Developer Reference Guide for details.)

Use these two methods in the implementation shown in Listing 10.

Listing 10. LocationAdapter implementation
var procedure1Statement = WL.Server.createSQLStatement("select * from travel.locations 
	order by city");
function getLocations() {
	return WL.Server.invokeSQLStatement({
		preparedStatement : procedure1Statement,
		parameters : []
	});
}

Notice the simplicity and elegance of the code. An adapter like this can be invoked within any Worklight application, either on the client side or the server side. It allows the caller to communicate with any data source without being subjected to same-origin constraints.

You also need a JDBC driver. If you have deployed the Worklight console on a preconfigured WebSphere Application Server, it might already have the DB2 JDBC driver in his classpath. For this example, you will use the built-in Jetty HTTP engine, for which you need to supply the JDBC driver (db2jcc4.jar if you use the type 4 driver) from your DB2 installation and copy it to the server/lib folder, or add its location to the Jetty classpath.

To run a quick test of your adapter configuration and code, right-click on adapters/LocationAdapter and select Run As > Invoke Worklight Procedure.

Figure 17. Invoking the LocationAdapter
Figure 17. Invoking the LocationAdapter

In the Edit Configuration wizard, select getLocations as the Procedure name and click Run. This will invoke the target adapter from within the browser (Figure 18).

Figure 18. Invocation result
Figure 18. Invocation result

You’re all set now to deploy the adapter. To do that, right-click on the Adapter and selecting Run As > Deploy Worklight Adapter (Figure 19).

Figure 19. Deploying the LocationAdapter
Figure 19. Deploying the LocationAdapter

Worklight Studio archives the adapter code and deploys it onto the Worklight Server. You can see the deployed adapter in the Worklight Console, along with the initial Mashup application (Figure 20).

Figure 20. Deloyed LocationAdapter
Figure 20. Deloyed LocationAdapter

Adapter chaining

In a similar fashion, you create the other two adapters as instances of an HTTPAdapter. Remember from Figure 2 that the client part calls the NewsAdapter whenever it needs related information for a specific location. You pass this location information to the server side and aggregate the related data from two sources – Google News Feed and Yahoo! Finance – effectively a server side mashup from different sources. Such an aggregation is also called adapter chaining (Figure 21).

Figure 21. Chain of adapters
Figure 21. Chain of adapters

Adapter chaining enables all server side processing to be executed within a single client invocation. Such “coarse grained” interactions are considered good practice, and the way you use such a pattern depend on your concrete requirements; for example, how much data has to be retrieved, how expensive is the server side processing logic, can you cache on the client side and work with stale data, and so on. Of course, such architectural considerations are not new or limited to the mobile space, but are important decisions to be made in any multi-tier architecture.

Create the remaining adapters

You first want to implement the last link in the chain, the StockAdaptor. It is a simple call to Yahoo! Finance service. The connectivity is configured as shown in Listing 11.

Listing 11. StockAdapter connectivity
<displayName>StockAdapter</displayName>
<description>StockAdapter</description>
<connectivity>
	<connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
		<protocol>http</protocol>
		<domain>finance.yahoo.com</domain>
		<port>80</port>			
	</connectionPolicy>
	<loadConstraints maxConcurrentConnectionsPerNode="2" />
</connectivity>
<procedure name="getStock"/>

The call to the Yahoo! Finance service needs a specific stock symbol (the country stock index, for example DJI or FTSE, is used here as an example) along with details of what exact figures you want. the service returns a comma separated values (CSV) stream of data. For this reason, set returnedContentType to csv (Listing 12). This is another nice feature provided by the Adapter framework.

Listing 12. StockAdapter Implementation
function getStock(stock) {
	var input = {
	    method : "get",
	    returnedContentType : "csv",
	    path : "d/quotes.csv",
		parameters : {
			"s" : "^" + stock,
			"f" : "sl1c1c"
		}
	};
	return WL.Server.invokeHttp(input);
}

To call the Google News service, you need to setup the connectivity shown in Listing 13.

Listing 13. NewsAdapter connectivity
<displayName>NewsAdapter</displayName>
<description>NewsAdapter</description>
<connectivity>
	<connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
		<protocol>http</protocol>
		<domain>news.google.com</domain>
		<port>80</port>			
	</connectionPolicy>
	<loadConstraints maxConcurrentConnectionsPerNode="2" />
</connectivity>
<procedure name="getNews"/>

You pass the location information and get the News RSS channel for this specific interest. The RSS channel contains an array of items, and to keep the data short and crisp you just pick the first item in the array. We could also use the XSL to filter out the content you really need.

The next link in the adapter chain, StockAdapter, is called as part of the post-processing. The way the invocation is carried out is discussed next. Both pieces of data are aggregated into the newsData object (Listing 14).

Listing 14. NewsAdapter implementation
function getNews(interest, stock)
{
	var input =
	{
		method : "get",
		returnedContentType : "html",
		path : "news",
		parameters :
		{
			"q" : interest,
			"output" : "rss"
		}
	};
	var newsData = WL.Server.invokeHttp(input).rss.channel.item[0];
	var stockData = WL.Server.invokeProcedure(
	{
		adapter : 'StockAdapter',
		procedure : 'getStock',
		parameters : [ stock ]
	});
	newsData["stock"] = stockData.text;
	return newsData;
}

In a nutshell, the NewsAdapter returns a mashup from the two resources that you will process and display in the client.

Invoking the adapters

You are done with the server-side implementation of the adapters. Now, you want to invoke them from the client. First, you need a UI element to store the retrieved location data. Insert a basic ComboBox in the Mashup.html file (Listing 15).

Listing 15. Add a ComboBox for the locations
<body onload="WL.Client.init({})" id="content" style="display: none">
<div data-dojo-type="dojox.mobile.View" id="view0"
	data-dojo-props="selected:true">
	Available Locations <select id="availableLocations"
		onchange="javascript:changeLocation();">
		<option value="" disabled="disabled">Select a location</option>
	</select>
	<div data-dojo-type="custom.LocationViewer" id="locationViewer"></div>
</div>

You put the actual implementation, which invokes the adapters, into js/Mashup.js. This file is automatically generated and is the application’s main JavaScript file. It contains the wlCommonInit() function that will be invoked during application startup once the Worklight framework initialization completes. It is the best place to add your application’s initialization code. This function is also used in environment-specific JavaScript files to have a common initialization starting point.

Listing 16. Initialization code
var busyIndicator = null;
var adapterData = new Array();

function wlCommonInit()
{
	busyIndicator = new WL.BusyIndicator("view0");
	getLocations();
}

Create a busyIndicator that provides the user with feedback indicating that a potentially lengthy operation is in progress. The getLocations() method is the first adapter call to fill the ComboBox with available locations (Listing 17).

Listing 17. Invoking the LocationAdapter
function getLocations()
{
  busyIndicator.show();
  var invocationData =
  {
    adapter    : "LocationAdapter",
    procedure  : "getLocations",
    parameters : []
  };
  WL.Client.invokeProcedure(invocationData,
  {
    onSuccess : function(response)
    {
      busyIndicator.hide();
      if (response.invocationResult.resultSet.length != 0)
      {
        var locationList = response.invocationResult.resultSet;
        var availableLocations = document.getElementById("availableLocations");
        availableLocations.length[1] = null;
        for ( var i = 0; i < locationList.length; i++)
        {
          newLocation = new Option(locationList[i].CITY, locationList[i].CITY + ", "
 + locationList[i].COUNTRY + ":" +
                                  locationList[i].STOCK, false, true);
          availableLocations[i + 1] = newLocation;
        }
        availableLocations[0].selected = "selected";
      }
      else
      {
        WL.SimpleDialog.show("Mashup", "Adapter can't load locations. Check your 
database connection", [
        {
          text : "Reload app (via WL.Client.reloadApp)",
          handler : WL.Client.reloadApp
        } ]);
      }
    },
    onFailure : function()
    {
      busyIndicator.hide();
      WL.SimpleDialog.show("Mashup", "Adapter can't load locations. Check your 
database connection", [
      {
        text    : "Reload app (via WL.Client.reloadApp)",
        handler : WL.Client.reloadApp
      } ]);
    }
  });
}

To invoke an adapter procedure, you need an invocationData object to which you pass the name of the adapter and procedure to call, as well as an array of optional parameters. Upon successful completion of the asynchronous call, the onSuccess function is invoked as a callback with a response object. The onFailure function is called if the adapter invocation failed. If everything is set up correctly, this initialization code fills up your ComboBox (Figure 22).

Figure 22. Available locations retrieved via adapter
Figure 22. Available locations retrieved via adapter

When you select a location, the changeLocation() function is invoked. Here, you maintain a local cache of the adapter data. If you have not yet fetched the location-related information, you call getNews() to retrieve it. Otherwise, you use the cached data and show it on appropriate map location (Listing 18).

Listing 18. Changing a location
function changeLocation()
{
	var value = document.getElementById("availableLocations").value;
	var newLocation = value.split(":")[0];
	var newStock = value.split(":")[1];
	// Lazily load the Stock and News information
	if (adapterData[newLocation] == undefined)
	{
		busyIndicator.show();
		WL.Logger.debug("Load adapterData for " + newLocation);
		getNews(newLocation, newStock);
	}
	else
	{
		dijit.byId("locationViewer").refreshMap(newLocation,
				adapterData[newLocation]);
		busyIndicator.hide();
	}
}

This function invokes the getNews procedure from the NewsAdapter, if necessary, and shows its return value on the map, using the same invocation pattern discussed above (Listing 19).

Listing 19. Invoking the NewsAdapter
function getNews(location, stock)
{
  var invocationData =
  {
    adapter    : 'NewsAdapter',
    procedure  : 'getNews',
    parameters : [ location, stock ]
  };
  WL.Client.invokeProcedure(invocationData,
  {
    onSuccess : function(response)
    {
      var stockData = response.invocationResult.stock.split(',');
      var news = ">b>" + location + ":>/b>>br>" +
          stockData[0].substring(2, stockData[0].length - 1) + ": " +
         stockData[1] + ">/b>" + ">font color=" +
         (stockData[2] > 0 ? "green>" : "red>" ) + " (Change: " +
         stockData[3].substring(2, stockData[3].length - 1) + ")>br>>a href=" +
         response.invocationResult.link + ">" +
         response.invocationResult.title + ">/a>";
      adapterData[location] = news;
      dijit.byId("locationViewer").refreshMap(location, news);
      busyIndicator.hide();
    },
    onFailure : function()
    {
      busyIndicator.hide();
      WL.SimpleDialog.show("Mashup",
                           "NewsAdapter can't load content for location " + location,
                 [{
                     text : "OK"
                 }]);
    }
  });
}

Figure 23 shows what happens when you try a few locations.

Figure 23. Available locations retrieved via Adapter
Figure 23. Available locations retrieved via Adapter

Adding a Worklight environment

As discussed in the Introduction, Worklight provides both a development and a run time environment to support the construction of applications for specific targets, using a concept called environments. Similar to a mobile web app, Worklight Studio creates target-specific artifacts when you add one of the supported environments.

For example, to add an iPhone environment, right-click on apps/Mashup and select New > Worklight Environment. In the New Worklight Environment wizard, select iPhone (Figure 24).

Figure 24. Adding a Worklight Environment
Figure 24. Adding a Worklight Environment

Worklight Studio now creates all the required artifacts to run the application you just created on an iPhone device. A new folder called iphone is added automatically to your project, and will appear only after you build and deploy your application on the Worklight Server.

Build and deploy the application and refresh the Worklight Console to see a new icon to launch the application in the mobile browser simulator, where you select Apple’s iPhone4 and Samsung’s Galaxy Ace (Figure 25):

Figure 25. Launching the application in the mobile browser Simulator
Figure 25. Launching the application in the mobile browser Simulator

Conclusion

This article described how you can develop a hybrid mobile application that uses mashup technologies on the client as well as on the server side. You created a custom Dijit widget and used it as an integral part of the application, and also developed and chained several adapters retrieving back end data from a variety of sources.


Download

DescriptionNameSize
Code sampleMashup-160812.zip6.8 MB

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
ArticleID=834511
ArticleTitle=Developing a client and server mashup mobile application with IBM Worklight
publish-date=09122012