Showing posts with label worklight. Show all posts
Showing posts with label worklight. Show all posts

Use Worklight to encrypt the data stored in window.localStorage

In the Reading data stored in localStroage by Android Device or Google Chrome Browser entry i talked about how easy it is to read data stored by web application in the window.localStorage object. WOrklight provides alternative which is to use a Encrypted cache that still uses window.localStorage object to store the data but encrypts the actual data to make it harder for someone else to read that data, even if they get access to your mobile or desktop. I wanted to try this feature out so i built this simple application which lets me store and read data from encrypted cache
First i did build a simple HTML file like this

<!DOCTYPE html>    
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
    <title>HelloEncryptedCache</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/HelloEncryptedCache.css" />
  </head>
  <body onload="WL.Client.init({})" id="content" style="display: none">
    <h1>Encrypted Cache</h1>
    <table>
      <tr>
        <td>Encryption Key</td>
        <td><input type="text" name='encryptionKey' id="encryptionKey" /></td>
      </tr>
      <tr>
        <td><button id="openCache">Open Cache</button></td>
        <td><button id="closeCache">Close Cache</button></td>
      </tr>
      <tr>
        <td><button id="destroyCache">Destroy Cache</button></td>
      </tr>
      <tr>
        <td>Key</td>
        <td><input type="text" name='key' id="key" /></td>
      </tr>
      <tr>
        <td>value</td>
        <td><input type="text" name='value' id="value" /></td>
      </tr>
      <tr>
        <td><button id="encryptKey">Encrypt Key/Value</button></td>
        <td><button id="decryptKey">Decrypt Key</button></td>
      </tr>
      <tr>
        <td><button id="removeKey">Remove key</button></td>
      </tr>
    </table>
    <script src="js/HelloEncryptedCache.js"></script>
    <script src="js/messages.js"></script>
    <script src="js/auth.js"></script>
  </body>
</html>
This is how my JavaScript on the page looks like

window.$ = WLJQ;

function wlCommonInit(){
  $("#openCache").click(function(){
    console.log('The openCache button is clicked ' +$("#encryptionKey").val());
    WL.EncryptedCache.open($("#encryptionKey").val(), true, function(){
      console.log('The cache key opened successfully');
    },onOpenError);
  });
  $("#closeCache").click(function(){
    console.log('The closeCache button is clicked');
    WL.EncryptedCache.close(function(){
      console.log('The cache is closed successfully');
    });
  });
  $("#destroyCache").click(function(){
    console.log('The destroyCache button is clicked');
    WL.EncryptedCache.destroy(function(){
      console.log('Successfully destroyed the encrypted cache');
    });
  });
  $("#encryptKey").click(function(){
    console.log('The encryptKey button is clicked');
    WL.EncryptedCache.write($("#key").val(), $("#value").val(), function() {
      console.log('The entry written successfully');
    }, function(status){
      console.log('There was error in encryptingKey ' + status);
      switch(status){
      case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
        console.log('Error in key creation process');
        break;
      case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
        console.log('Local storage is not supported');
        break;
      case WL.EncryptedCache.ERROR_NO_EOC:
        console.log('No EOC');
        break;
      case WL.EncryptedCache.ERROR_COULD_NOT_GENERATE_KEY:
        console.log('Could not generate key');
        break;
      case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
        console.log('Credentials mismatch');
        break;
      }
    }); 
  });
  $("#decryptKey").click(function(){
    console.log('The decryptKey button is clicked');
    WL.EncryptedCache.read($('#key').val(), function(value) {
      console.log('Value from the encrypted cache is ' + value);
      alert('Encrypted value for the key -> ' + value);
    }, function(status){
      console.log('There was error in encryptingKey ' + status);
      switch(status){
      case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
        console.log('Error in key creation process');
        break;
      case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
        console.log('Local storage is not supported');
        break;
      case WL.EncryptedCache.ERROR_NO_EOC:
        console.log('No EOC');
        break;
      case WL.EncryptedCache.ERROR_COULD_NOT_GENERATE_KEY:
        console.log('Could not generate key');
        break;
      case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
        console.log('Credentials mismatch');
        break;
      }
    });
  });
  $("#removeKey").click(function(){
    console.log('The removeKey button is clicked');
    WL.EncryptedCache.remove($('#key').val(), function(){
      console.log('The encrypted key removed successfully ->' + $('#key').val() );
    })
  });
}
function onOpenError(status) {
  console.log("Inside onOpenError " + status);
  switch (status) {
  case WL.EncryptedCache.ERROR_KEY_CREATION_IN_PROGRESS:
    console.log("Error key creation in progress");
    break;
  case WL.EncryptedCache.ERROR_LOCAL_STORAGE_NOT_SUPPORTED:
    console.log("Error local storage not supported");
    break;
  case WL.EncryptedCache.ERROR_CREDENTIALS_MISMATCH:
    console.log("Error credentials mismatch");
    break;
  case WL.EncryptedCache.ERROR_SECURE_RANDOM_GENERATOR_UNAVAILABLE:
    console.log("Error secure random generator unavailable");
    break;
  case WL.EncryptedCache.ERROR_NO_EOC:
    console.log("Error no eoc");
    break;
  }
}
The JavaScript has one event handler for each of the button and when you click on the button it makes use of the WL.EncryptedCache API to read/write cache entries. While working with encrypted cache first you have to open the cache before you can write any entry and once your done writing cache entries you will have to close the cache. I noticed one strange thing is if i dont attach error handling functions then my code works in normal browser but it throws undefined error in ANdroid emulator. It seems that the Worklight API makes use of some native device functionality to get encrypted cache working. One thing i noticed is accessing encrypted cache (specially opening it is really slow, so you should use it only if you really need to encrypt the data) After storing data using the Encrypted Cache API i tried to access the file_0.localstorage file from the device but i could not download it due to some file access level restrictions that android is putting on it.
Also in the local browser i can not read the values stored in the encryptedCache

How to enable form based authentication in worklight application

  1. The first step is to change the application-descriptor.xml file like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Attribute "id" must be identical to application folder name -->
    <application id="HelloWorklightAuthentication" platformVersion="5.0"
      xmlns="http://www.worklight.com/application-descriptor" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    
      <displayName>HelloWorklightAuthentication</displayName>
      <description>HelloWorklightAuthentication</description>
      <author>
        <name>application's author</name>
        <email>application author's e-mail</email>
        <copyright>Copyright My Company</copyright>
        <homepage>http://mycompany.com</homepage>
      </author>
      <height>460</height>
      <width>320</width>
      <mainFile>HelloWorklightAuthentication.html</mainFile>
      <thumbnailImage>common/images/thumbnail.png</thumbnailImage> 
    
      <usage requireAuthentication="onStartup">
        <realm name="SampleAppRealm"/>
      </usage>
    
      <worklightServerRootURL>http://${local.IPAddress}:8080</worklightServerRootURL>
    </application>
    
    
    I made one change in the application-descriptor.xml which is to add usage element with value of requireAuthentication equal to onStartup which means user will have to authenticate at the start of the application. The second change is to add realm element with value equal to SampleAppRealm
  2. The SampleAppRealm is defined in the authenticationConfig.xml file like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <tns:loginConfiguration xmlns:tns="http://www.worklight.com/auth/config" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <realms>
      
        <realm name="SampleAppRealm" loginModule="StrongDummy">
          <className>com.worklight.core.auth.ext.FormBasedAuthenticator</className>
        </realm>
    
        <realm name="WorklightConsole" loginModule="requireLogin">
          <className>com.worklight.core.auth.ext.FormBasedAuthenticator</className>
          <onLoginUrl>/console</onLoginUrl>
        </realm>
    
      </realms>
      
      <loginModules>
        <loginModule name="StrongDummy" canBeResourceLogin="true" isIdentityAssociationKey="false">
          <className>com.worklight.core.auth.ext.NonValidatingLoginModule</className>
        </loginModule>
    
        <loginModule name="requireLogin" canBeResourceLogin="true" isIdentityAssociationKey="true">
          <className>com.worklight.core.auth.ext.SingleIdentityLoginModule</className>
        </loginModule>
      </loginModules>
    </tns:loginConfiguration>
    
    The SampleAppRealm is configured to use FormBasedAuthentication, which means we will have to submit a form to j_security_check URL and we will have to use j_username and j_password field name for user name and password on the form The SampleAppRealm uses StrongDummy as loginModule which uses com.worklight.core.auth.ext.NonValidatingLoginModule class for authenticating user name and password. the NonValidatingLoginModule class makes setup easy by not actually validating user name and password, which means no matter what user name and password you pass to it it will always say user logged in.
  3. Change the main .html file for the worklight application so that it looks like this, the basic idea is you should have one div for displaying login form and another for displaying regular application body. We will use JavaScript to check if user is already logged in, if no display login form to the user if no display normal application body.
    <!DOCTYPE html>    
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, 
     maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
        <title>HelloWorklightApp</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/HelloWorklightApp.css" />
      </head>
      <body onload="WL.Client.init({showLogger:true})" id="content" style="display: none">
    
        <div id="AppBody">
          <h1>Your logged in </h1>
          <input type="button" value="Logout" 
       onclick="WL.Client.logout('SampleAppRealm', {onSuccess:  WL.Client.reloadApp});" />
        </div>
    
     
        <div id="AuthBody">
          <div id="loginForm">
            Username:<br/>
            <input type="text" id="usernameInputField" 
      autocorrect="off" autocapitalize="off" /><br />
            Password:<br/>
            <input type="password" id="passwordInputField" autocorrect="off" 
      autocapitalize="off"/><br/>    
            <input type="button" id="loginButton" value="Login" />
          </div>
        </div>      
       
       <script src="js/HelloWorklightApp.js"></script>
        <script src="js/messages.js"></script>
        <script src="js/auth.js"></script>
      </body>
    </html>
    
    The AppBody div displays the normal application body and the AuthBody displays the login form
  4. 
    Last part is to change the auth.js file so that it looks like this
    var Authenticator = function () {
        var LOGIN_PAGE_SECURITY_INDICATOR = 'j_security_check';
        var USERNAME_INPUT_ID = '#usernameInputField';
        var PASSWORD_INPUT_ID = '#passwordInputField';
        var LOGIN_BUTTON_ID   = '#loginButton';  
        var onSubmitCallback  = null;
        function onFormSubmit() {
          console.log("Entering auth.js.onFormSubmit()");
            var reqURL = './' + LOGIN_PAGE_SECURITY_INDICATOR;
            var params = {
                j_username : $(USERNAME_INPUT_ID).val(),
                j_password : $(PASSWORD_INPUT_ID).val()
            };
            onSubmitCallback(reqURL, {parameters:params});
        }
        return {
            init : function () {
              console.log("Entering auth.js.init()");
                $(LOGIN_BUTTON_ID).bind('click', onFormSubmit);
            },
            isLoginFormResponse : function (response) {
              console.log("Entering auth.js.isLoginFormResponse () " + response);
                if (!response || response.responseText === null) {
                  console.log("Entering auth.js.isLoginFormResponse (), return false");
                    return false;
                }
                var indicatorIdx = response.responseText.search(LOGIN_PAGE_SECURITY_INDICATOR);
              console.log("Entering auth.js.isLoginFormResponse (), return " + (indicatorIdx >= 0));
                return (indicatorIdx >= 0);
            },
            onBeforeLogin : function (response, username, onSubmit, onCancel) {
              console.log("Entering auth.js.onBeforeLogin()");
                onSubmitCallback = onSubmit;
                onCancelCallback = onCancel;            
                if (typeof(username) != 'undefined' && username != null){
                    $(USERNAME_INPUT_ID).val(username);
                }
                else {
                    $(USERNAME_INPUT_ID).val('');
                }
                $(PASSWORD_INPUT_ID).val('');
            },
          onShowLogin: function() {
            console.log("Entering auth.js.onShowLogin()");
            $('#AppBody').hide();
            $('#AuthBody').show();
          },
          onHideLogin: function(){        
            console.log("Entering auth.js.onHideLogin()");
            $('#AppBody').show();
            $('#AuthBody').hide();
            }   
        }; 
    }();
    
    The isLoginFormResponse() is the main method for the authentication framework, it gets called on each response to check if the user is authenticated. Inside this method check the response text to figure out if the user is already authenticated or not. The onShowLogin() page method gets called if the user is not logged in, in that method hide the application body and hide the login form. The onHideLogin() method gets called if the user is already logged in in that case hide the login form and display the body. The onFormSubmit() gets called when user enter the user id and password and clicks submit, that method takes care of submitting the form using AJAX, worklight application is single page application so we cannot actually submit the form normally(I mean without ajax using browser's default form submit functionality)
Once the application is deployed when you access it for the first time you get login page like this
But once you enter user id and password and click submit you should get the application page like this

Using embedded worklight browser simulator

The Worklight 50 studio comes with a very nice mobile browser simulator, you can access it by going to http://localhost:8080/console and clicking on the device related link It opens up a nice mobile browser simulator like this, you can use it to simulate multiple devices, use different Cordova functions.

Worklight 50 release

IBM recently released release WorkLight 5, which is available for download from here. If you have IBM partner world account like me then you can download the Worklight server from partner world. Looks like IBM Worklight 5.0 has quite few new features In my case i already have a PhoneGap development environment setup, so all i had to do was add http://public.dhe.ibm.com/ibmdl/export/pub/software/mobile-solutions/wor as Available site in Eclipse and it took care of installing Worklight studio and it comes with embedded Worklight server in it, so no need to install either Worklight server or database server separately.

Chaning pages dynamically using JQuery Mobile - use different .htmls

In the Changing pages dynamically in JQuery Mobile Worklight application i blogged about how you can use the JQuery Mobile JavaScript framework to create multi-page web application. In that example i built a web application which has only single HTML file that contains HTML fragments for different pages and i used following JavaScript to change page dynamically

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}
One of the reader raised a very good question that it might not be good idea to have all the pages in same html and can we break the html's into different pages and answer is yes. I wanted to try that so first i did create a listcontact.html in the same directory as that of ManageContact.hml that has markup of only listcontact.html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, 
  maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<title>ManageContact</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/ManageContact.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="home">
    <div data-theme="a" data-role="header">
      <h3>Contact List</h3>
    </div>
    <div data-role="content" id="pagePort">
      <ul data-role="listview" data-inset="true" data-filter="true"
        id="displayContact">
      </ul>
       <a data-role="button"
            data-transition="fade" href="#home"
            id="searchContact"> Home </a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  <script src="js/ManageContact.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
The listcontact.html has the same structure as ManageContact.html (The worklight server does add more resources to the ManageContact.html as part of build process). Then i had to use following JavaScript to switch page

function switchToPage2(){
  jq.mobile.changePage ("listcontact.html");
}
and when i click on switchPage it does make forward control to listcontact.html, that works. But since we refreshed the whole page we loose the JavaScript context.

Calling a HTTP POST using Worklight REST adapter

I am trying to build Contact Management application using WorkLight that uses REST service as back end. You can take a look and download the REST service from Returning JSON response from JAXRS service This is how the insertContact() method of my JAXRS service looks like, the insertContact() method consumes MediaType.APPLICATION_FORM_URLENCODED

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public void insertContact(@FormParam("contactId") int contactId,
      @FormParam("firstName") String firstName,
      @FormParam("lastName") String lastName,
      @FormParam("email") String email) {
    Contact contact = new Contact();
    contact.setContactId(contactId);
    contact.setFirstName(firstName);
    contact.setLastName(lastName);
    contact.setEmail(email);
    ContactDAO contactDAO = new ContactDAOImpl();
    contactDAO.insertContact(contact);
}
This is how the insertContact() method in my REST adapter looks like. The method for this request is POST and the contentType is application/x-www-form-urlencoded i have to create the string representing the formBody and submit it in content.

function insertContact(firstName,lastName,email){
  WL.Logger.debug("Entering ContactRESTService1.insertContact()");
  var input = {
      method : 'post',
      returnedContentType : 'json',
      path : '/ManageContact/rest/contact',
      body:{
      contentType:'application/x-www-form-urlencoded',
      content:"firstName="+firstName+"&lastName="+lastName+"&email="+email
      } 
  };
  WL.Logger.debug("Exiting ContactRESTService1.insertContact()");
  return WL.Server.invokeHttp(input);
}

Changing pages dynamically in JQuery Mobile Worklight application

In the Creating multi page application with JQuery Mobile entry i talked about how to build a multi-page application with JQuery Mobile, in that i talked about how to switch page using static href's but most of time you would want to change page name dynamically after executing some JavaScript, so i changed my html like this

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</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/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage2()">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="javascript:switchToPage1()">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
   <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
In this the anchor for change page is changed to point to JavaScript function that looks like this, you can use the jq.mobile.changePage("<pageid>")

function switchToPage2(){
  jq.mobile.changePage ("#page2");
}

function switchToPage1(){
  jq.mobile.changePage ("#page1");
}

Worklight Phone Gap Android application

I just created my first Worklight Phone Gap application for Android. The process is pretty easy you just add support for Android environment and then WorkLight creates/generates Android project for you. Once your project is ready you just right click on it and say Run as Android application to get a view like this
I wanted to see what is going on so i looked at the generated project, but before that this is how my Worklight application is structured
When you add support for Android, WorkLight generates PhoneGap Android project that is similar to Getting Started with PhoneGap on Android project, the Generated Java class looks like this

package com.HelloWorkLightPhoneGap;

import android.os.Bundle;

import com.worklight.androidgap.WLDroidGap;

public class HelloWorkLightPhoneGap extends WLDroidGap {
 @Override
 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl(getWebMainFilePath());
 }  
}
So basically this class is forwarding control to the HTML page but the HTML file name is not hard coded here instead it comes from wlclient.properties file which looks like this

wlServerProtocol = http
wlServerHost = 192.168.94.131
wlServerPort = 8080
wlAppId = HelloWorkLightPhoneGap
wlAppVersion = 1.0
wlC2DMEmailSender = ${wlC2DMEmailSender}
wlMainFilePath = HelloWorkLightPhoneGap.html
enableSettings = true
So the HelloWorkLightGap.html file is the main file for my WorkLight application that becomes a main file here. Also if you take a closer look at the content of the project you will notice that the Android has assets/www/default folder which has all the files from your worklight application

Creating multi page application with JQuery Mobile

JQuery Mobile makes creating multi-page application very easy, I think its much better than using Worklight for creating multi-page application Take a look at this simple html page that i built which has 3 pages page1, page2 and page3, every page has link to the next page.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
  minimum-scale=1.0, user-scalable=0" />
<title>HelloWorkLightPhoneGap</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/HelloWorkLightPhoneGap.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>
<script src="js/jquery.mobile-1.0.min.js"></script>
<script type="text/javascript"
  src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body onload="WL.Client.init({})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page2">Go to second page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <div data-role="page" id="page2" >
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page3">Go to third page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  
  <div data-role="page" id="page3">
    <div data-theme="a" data-role="header">
      <h3>Third page</h3>
    </div>
    <div data-role="content" id="pagePort">
      <a href="#page1">Go to first page</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>

  <script src="js/HelloWorkLightPhoneGap.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>
When you want to move to the next page you use the link with value of href equal to the id of the next page

Setting up Android PhoneGap development environment

Before couple of days i was trying to setup Android Phone Gap environment on my machine, by following instructions at Getting started guide for Android environment on PhoneGap site, I was using PhoneGap 1.5 version and i had a problem of not being able to start the application. The solution was to use Android 2.2 SDK using the latest Android SDK some how does not work.

Invoking REST service that returns JSON from worklight

In the Invoking REST service from WorkLight i built a simple WorkLight application that makes REST call but the REST service in that case returns XML, To me if your building a REST service for access from Browser it would be much better idea to return JSON instead of XML that avoids one conversion. So with that goal in mind i wanted to change my application and i followed these steps for that
  • First thing that i had to do was change the REST service so that it returns JSON and also i made JSON as the default return type for service, you can get more information and download the sample application by using entry
  • Next i changed the ContactRESTService-impl.js, the worklight REST adapter that i am using for making the actual call,
    
    function getContactList() {
      var input = {
          method : 'get',
          returnedContentType : 'json',
          path : '/ManageContact/rest/contact'
      };
      return WL.Server.invokeHttp(input);
    }
    
    function searchContact(lastName){
      var input = {
            method : 'get',
            returnedContentType : 'json',
            path : '/ManageContact/rest/contact/search?lastName='+lastName
        };
        return WL.Server.invokeHttp(input);
    }
    
    Only change in this file is to let the WorkLight adapter know that i am expecting json as response type instead of xml
  • After that i had to change the way i am parsing the response and using it to display results to the user, the reason being now the structure of the response is different so the callback that reads the response and displays the result has to know about how to handle the changed structure.
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.contact; 
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
        displayError(e.toString());
      }
    }
    
    The highlighted code displays how i am getting the actual contact list from the response. This is how the response of the REST service looks like when i hit it directly
    This is how the response that WorkLight adapter is returning to the JavaScript client looks like

Creating multi page application in WorkLight

WorkLight allows you to create multi-page application, i wanted to try this feature, so i changed the Contact Search application that i created in Using BusyIndicator common control entry, so that, when executes a search for contact, it returns list of contact names, user can click on one of the contact name to go to the Contact Details page and that page has button that allows user to go back to Summary page. This is how my summary page looks like
When you click on the arrow button next to any user name it takes you to the Details page for that contact which looks like this
You can click on the Summary button on the Details page to go back to the Contact Summary page. I followed these steps to create this sample application
  1. First i changed the main or landing page of my app to mark the div where the new page should be inserted, i did that by setting value of id element to pagePort like this
    
    !DOCTYPE html>
    <html>
    <head>
    ....
    </head>
    <body onload="WL.Client.init({showLogger:true})" id="content"
      style="display: none">
    
      <div data-role="page" id="page1">
        <div data-theme="a" data-role="header">
          <h3>Contact DB App</h3>
        </div>
        <div data-role="content" id="pagePort" >
          <div data-role="fieldcontain">
            <fieldset data-role="controlgroup">
              <label for="textinput1"> First Name </label> <input
                id="contactName" placeholder="" value="" type="text" />
            </fieldset>
          </div>
          <a data-role="button" data-transition="fade"
            href="javascript:getContact()" id="searchContact"> Search
            Contact </a>
          <ul data-role="listview" data-inset="true" data-filter="true"
            id="displayContact">
          </ul>
        </div>
        <div data-theme="a" data-role="footer">
          <h3>Copyright stuff</h3>
        </div>
      </div>
      <script src="js/HelloDatabase.js"></script>
      <script src="js/messages.js"></script>
      <script src="js/auth.js"></script>
    </body>
    </html>
    
  2. Then i did create this new contactDetail.html page in the same directory as that of my first/landing page
    
    <div data-role="content" id="mainContent">
      <div data-role="fieldcontain">
        <fieldset data-role="controlgroup">
          <label for="firstName"> First Name </label> 
          <input id="firstName" placeholder="" value="" type="text" readonly="readonly" />
          <label for="lastName"> Last Name </label> 
          <input id="lastName" placeholder="" value="" type="text" readonly="readonly"/>
          <label for="email"> Email </label> 
          <input id="email" placeholder="" value="" type="text" readonly="readonly"/>
          
          <a data-role="button" data-transition="fade"
            href="javascript:showSummary()" id="searchContact"> Summary </a>
        </fieldset>
      </div>
    </div>
    
    The Details page shows the form with contact details and it has a Summary button that allows user to go back to the summary page, when you click on that button it will pass control to showSummary() method
  3. I had to make changes to my main JavaScript function to make it look like this
    
    var busyIndicator;
    function wlCommonInit() {
      busyIndicator = new WL.BusyIndicator('page1');
    }
    
    function getContact() {
      console.log("Entering getContact() REST service based version");
      var contactName = $('contactName').getValue();
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "searchContact",
        parameters : [ contactName ]
      }
      var options = {
        onSuccess : loadContactSuccess,
        onFailure : loadContactFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result) {
      console.log("Inside loadContactSuccess " + result);
      var html = '';
      try {
        if (result.status == 200) {
          var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
          var i = 0;
          for (i = 0; i < contactList.length; i++) {
            var currentContact = contactList[i];
            html = html + '<li><a href="javascript:showContactDetail('
                + currentContact.contactId + ')">'
                + currentContact.firstName + ' '
                + currentContact.lastName + '</a></li>';
          }
        }
        jq("#displayContact").html(html);
        jq("#displayContact").listview('refresh');
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function showContactDetail(contactId) {
      console.log("Show Contact Detail is clicked " + contactId);
      WL.Page.load("contactDetail.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          getContactDetails(contactId);
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      });
    }
    
    function showSummary() {
      WL.Page.load("contactSummary.html", {
        onComplete : function() {
          console.log("After fragment is loadded ");
          jq('#mainContent').trigger("create");
          
        },
        onUnload : function() {
          console.log("After fragment is unloadded ");
        }
      }); 
    }
    
    function getContactDetails(contactId) {
      console.log("Entering getContactDetails()");
      var invocationData = {
        adapter : "ContactWSService",
        procedure : "getContact",
        parameters : [ contactId ]
      }
      var options = {
        onSuccess : getContactDetailsSuccess,
        onFailure : getContactDetailsFailure
      }
      busyIndicator.show();
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function getContactDetailsSuccess(result) {
      console.log("Entering getContactDetailsSuccess");
      try {
        if (result.status == 200) {
          var displayContact = result.invocationResult.Envelope.Body.getContactResponse.contact;
          $('firstName').value=displayContact.firstName;
          $('lastName').value=displayContact.lastName;
          $('email').value=displayContact.email;
        }
        busyIndicator.hide();
      } catch (e) {
        busyIndicator.hide();
      }
    }
    function getContactDetailsFailure(result) {
      console.log("Entering getContactDetailsFailure");
    }
    
    I had to make quite a few changes in my JavaScript they are as follows
    • loadContactSuccess: The loadContactSuccess method gets called when you execute search and it gets back the result, this method generates one row each for the result. I changed this method so that when it was generating the list i did attach getContactDetails(contactId) method to each row
    • showContactDetail: The showContactDetail method will get called when user clicks on any of the user name, when that happens i am calling WL.Page.load("contactDetails.html" method which replaces the markup inside the page with contactDetail.html, this method also calls the getContactDetails which calls SOAP service with contactId to get details of the contact.
    • showSummary: The showSummary method will be called when user clicks on the Summary button on the details page, it again calls WL.Page.load("contactSummary.html" to replace the markup in the current page with the contact details

Using WL.SimpleDialog() to display error

I wanted to figure out how to use WL.SimpleDialog() so i used it to change the contact application that i developed in Using BusyIndicator common control so that if there is JavaScript Exception during searching of contact it displays that error in Dialog Box like this
This is how my JavaScript looks like after the changes

var busyIndicator;
function wlCommonInit(){
  busyIndicator = new WL.BusyIndicator('page1');
}
function getContact(){
  console.log("Entering getContact() REST service based version");
  var contactName = $('contactName').getValue();
  var invocationData = {
      adapter:"ContactWSService",
      procedure:"searchContact",
      parameters:[contactName]
  }
  var options ={
      onSuccess:loadContactSuccess,
      onFailure:loadContactFailure
  }
  busyIndicator.show();
  WL.Client.invokeProcedure(invocationData, options);
}

function loadContactSuccess(result){
  console.log("Inside loadContactSuccess " + result);
  var html = '';
  try{
  if(result.status == 200){
    var contactList = result.invocationResult.Envelope.Body.
 searchContactResponse.contactList;
    var i = 0;
    for(i =0 ; i < contactList.length ; i++){
      var currentContact = contactList[i];
      html =  html + '<li><a href="#">'+currentContact.firstName 
   +' ' +currentContact.lastName +'</a></li>';
    }   
  }
  jq("#displayContact").html(html);
  jq("#displayContact").listview('refresh');
  busyIndicator.hide();
  }catch(e){
    busyIndicator.hide();
    displayError(e.toString());
  }
}

function loadContactFailure(result){
  console.log("Inside loadContactError " + result);
  busyIndicator.hide();
  displayError(result);
}

function displayError(errorString) {
  var dialogTitle = "Error";
  WL.SimpleDialog.show(dialogTitle, errorString, [ {
    text : 'OK',
    handler : simpleDialogButton1Click
  }
  ]);
}
function simpleDialogButton1Click() {
}

First i did create a displayError() method that takes error message as input and displays it in Modal dialog that has only one button. The simpleDialogButton1Click() would get called when user clicks on OK in the Error dialog but it does not do anything. The displayError() method is getting called from the exception handler class as well as the loadContactFailure() class.

Using BusyIndicator common control

I wanted to try using the BusyIndicator so i decided to change the Contact Search application that i developed in Using JQuery Mobile in WorkLight application entry, so that as soon as user clicks on Search Contact it starts showing the busy indicator and that indicator stays still the results are updated. I made following changes in my JavaScript file.


var busyIndicator;
function wlCommonInit(){
  busyIndicator = new WL.BusyIndicator('page1');
}
function getContact(){
  var contactName = $('contactName').getValue();
  var invocationData = {
      adapter:"ContactWSService",
      procedure:"searchContact",
      parameters:[contactName]
  }
  var options ={
      onSuccess:loadContactSuccess,
      onFailure:loadContactFailure
  }
  busyIndicator.show();
  
  WL.Client.invokeProcedure(invocationData, options);
}

function loadContactSuccess(result){
  console.log("Inside loadContactSuccess " + result);
  var html = '';
  try{
  if(result.status == 200){
    var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
    var i = 0;
    for(i =0 ; i < contactList.length ; i++){
      var currentContact = contactList[i];
      html =  html + '<li><a href="#">'+currentContact.firstName +' ' 
   +currentContact.lastName +'</a></li>';
    }   
  }
  jq("#displayContact").html(html);
  jq("#displayContact").listview('refresh');
  
  busyIndicator.hide();
  
  }catch(e){
    busyIndicator.hide();
  }
}

function loadContactFailure(result){
  console.log("Inside loadContactError " + result);
  busyIndicator.hide();
}
First i had to create object of WL.BusyIndicator with value of page1 as input, page1 is the value of id attribute on enclosing div in my page. Without that i was getting JavaScript initialization error. Then the getContact() which is responsible for initiating the contact search call invokes the show() method to start the BusyIndicator. I am calling the hide() method of BusyIndicator in the loadContactSuccess() method which is a callback method that gets called once the results are ready and the UI is updated.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, 
  minimum-scale=1.0, user-scalable=0" />
<title>HelloDatabase</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/HelloDatabase.css" />
<link rel="stylesheet" href="css/jquery.mobile-1.0.min.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script>
  var jq = jQuery.noConflict();
</script>

<script src="js/jquery.mobile-1.0.min.js"></script>
</head>
<body onload="WL.Client.init({showLogger:true})" id="content" style="display: none">
  <div data-role="page" id="page1">
    <div data-theme="a" data-role="header">
      <h3>Contact DB App</h3>
    </div>
    <div data-role="content">
      <div data-role="fieldcontain">
                    <fieldset data-role="controlgroup">
                        <label for="textinput1">
                           First Name
                        </label>
                        <input id="contactName" placeholder="" value="" type="text" />
                    </fieldset>
                </div>
                <a data-role="button" data-transition="fade" 
    href="javascript:getContact()" id="searchContact">
                    Search Contact
                </a>
      <ul data-role="listview" data-inset="true" data-filter="true" id="displayContact">
      </ul>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  <script src="js/HelloDatabase.js"></script>
  <script src="js/messages.js"></script>
  <script src="js/auth.js"></script>
</body>
</html>

WorkLight SOAP Service debugging

I ran into few issues while working on Accessing SOAP service from the WorkLight app blog entry and these are the solutions to those problems First problem was i had to build the SOAP request in JavaScript manually for use in the HTTP Adapter class and since this code runs on server i was not able to debug it, and i was not sure what message is getting built. So i used the WL.Logger.debug("SOAP Request " + searchContactRequest) call in my Adapter code and then when i hit the adapter it did write this log statement in \Worklight\server\log\server\server.log directory like this 2012-03-29 15:40:30,469 DEBUG [developer] (pool-7-thread-2:0ad7210e-9ed8-4c58-9b38-853d1918131b) SOAP Request <soapenv:Envelope xmlns:q0="http://webspherenotes.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> </soapenv:Header> <soapenv:Body> <q0:searchContact> <arg0>at</arg0> </q0:searchContact> </soapenv:Body> </soapenv:Envelope> The worklight server does take care of writing the response of the SOAP request into the same log file, so having log enabled for com.srndpt.adapters helps Also note that the WorkLight server takes care of converting the SOAP XML response into JSON This is the response of my SOAP Service <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> - <S:Body> - <ns2:searchContactResponse xmlns:ns2="http://webspherenotes.com"> - <contactList> <contactId>1</contactId> <email>sdpatil@gmail.com</email> <firstName>Sunil</firstName> <lastName>Patil</lastName> </contactList> - <contactList> <contactId>2</contactId> <email>patil.jiyas@gmail.com</email> <firstName>Jiya</firstName> <lastName>Patil</lastName> </contactList> - <contactList> <contactId>3</contactId> <email>patil.navyas@gmail.com</email> <firstName>Navya</firstName> <lastName>Patil</lastName> </contactList> </ns2:searchContactResponse> </S:Body> </S:Envelope> The WorkLight server takes that response and converts it into JSON object and sticks it into Result element like this {"responseID":"10","statusCode":200,"errors":[],"isSuccessful":true,"statusReason":"OK","Envelope":{"Body":{"searchContactResponse":{"ns2":"http://webspherenotes.com","contactList":[{"lastName":"Patil","contactId":"1","email":"sdpatil@gmail.com","firstName":"Sunil"},{"lastName":"Patil","contactId":"2","email":"patil.jiyas@gmail.com","firstName":"Jiya"},{"lastName":"Patil","contactId":"3","email":"patil.navyas@gmail.com","firstName":"Navya"}]}},"S":"http://schemas.xmlsoap.org/soap/envelope/"},"warnings":[],"info":[]}

Accessing SOAP service from the WorkLight app

In the Using JQuery Mobile in WorkLight application entry i built a WorkLight application that takes contact last name as input and uses it to search for contact by using WorkLight SQL adapter, i wanted to check if i can achieve same functionality by using SOAP service so these are the steps that i used
  1. First i did build a simple JAXWS service that takes last name of the contact as input parameter and returns list of contacts with matching last name, you can download the service that i used from here
  2. Next i used the WorkLight studio to create a HTTP Adapter, i used ContactWSService as name for that adapter
  3. After creating the adapter i change the ContactWSService-impl.js that was generated to look like this
    
    function searchContact(lastName) {
     var searchContactRequest = '<soapenv:Envelope '+  
       'xmlns:q0="http://webspherenotes.com" '+  
       'xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" '+  
       'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '+  
       'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> '+  
       '<soapenv:Header> '+  
       '</soapenv:Header> '+  
      '<soapenv:Body> '+ 
      '<q0:searchContact> '+
       '<arg0>'+lastName+'</arg0> '+
       '</q0:searchContact> '+
       '</soapenv:Body> '+  
     '</soapenv:Envelope> ';
     WL.Logger.debug("SOAP Request " + searchContactRequest);
     var input = {
         method : 'post',
         returnedContentType : 'xml',
         path : '/ManageContactWS/contactws',
         body:{
          content: searchContactRequest.toString(),
          contentType: 'text/xml; charset=utf-8'
         }
     };
     return WL.Server.invokeHttp(input);
    }
    
    In order to make a SOAP request call you will have to create the SOAP message first, i used the Eclipse Web Services Explorer tool to first test my SOAP service and then copied the XML SOAP message that it used for request into my JavaScript file. Once i have the SOAP message in String format i used it to make HTTP POST call
  4. Next i had to change the ContactWSService.xml the deployment descriptor for my adapter to declare searchContact procedure, after changes the file looks like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <wl:adapter name="ContactWSService"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:wl="http://www.worklight.com/integration"
      xmlns:http="http://www.worklight.com/integration/http">
    
      <displayName>ContactWSService</displayName>
      <description>ContactWSService</description>
      <connectivity>
        <connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
          <protocol>http</protocol>
          <domain>localhost</domain>
          <port>9000</port>      
        </connectionPolicy>
        <loadConstraints maxConcurrentConnectionsPerNode="2" />
      </connectivity>
      <procedure name="searchContact"/>
    </wl:adapter>
    
  5. The last change was in the WorkLight application code where i make the adapter call and parse the returned results so that i can display them, This is how my .js file looks like
    
    function getContact(){
     console.log("Entering getContact() REST service based version");
     var contactName = $('contactName').getValue();
     var invocationData = {
       adapter:"ContactWSService",
       procedure:"searchContact",
       parameters:[contactName]
     }
     var options ={
       onSuccess:loadContactSuccess,
       onFailure:loadContactFailure
     }
     WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result){
     console.log("Inside loadContactSuccess " + result);
     var html = '';
     if(result.status == 200){
      var contactList = result.invocationResult.Envelope.Body.searchContactResponse.contactList;
      var i = 0;
      for(i =0 ; i < contactList.length ; i++){
       var currentContact = contactList[i];
       html =  html + '
  6. '+currentContact.firstName +' ' +currentContact.lastName +'
  7. '; } } jq("#displayContact").html(html); jq("#displayContact").listview('refresh'); } function loadContactFailure(result){ console.log("Inside loadContactError " + result); }
    Important Note: By default the JAXWS wraps the return value in XML element named return and the WorkLight server simply uses the same name while converting the XML result into JSON object, but that makes accessing the return element difficult in the client javascript because return is the JavaScript keyword and you can not use it in your code. So to get around this problem i had to change my sample web service and use JAXWS annotation to customize name of the return element to contactList

WorkLight HTTP Adapter returning XML

In the Invoking REST service from WorkLight entry i blogged about how to make a REST call that returns XML from WorkLight application using HTTP adapter. One thing that i noticed during that is even though my REST service returns XML WorkLight converts it into JSON for me and the generated JSON follows the same structure as that of the XML. This is how my XML response of the REST service looks like
The WorkLight server converts it into JSON and return's JSON that looks like this

Invoking REST service from WorkLight

In the Using JQuery Mobile in WorkLight application entry i blogged about how to create a simple Contact Search application that takes user's last name as parameter and searches all the contacts with that name displays to the user. In that example i used the WorkLight SQL adapter for making query, but i already have my own REST service that can do same thing and i wanted to use it.
This service takes part of last name as query parameter and returns all the contacts that match the name in XML format. I wanted to use this service in my WorkLight application, so that i could use the Http Adapter so i followed these steps
  1. First i did create a ContactRESTService adapter
  2. Then i changed the ContactRESTService-impl.js like this
    
    
    function getContactList() {
      var input = {
          method : 'get',
          returnedContentType : 'xml',
          path : '/ManageContact/rest/contact'
      };
      return WL.Server.invokeHttp(input);
    }
    
    function searchContact(lastName){
      var input = {
            method : 'get',
            returnedContentType : 'xml',
            path : '/ManageContact/rest/contact/search?lastName='+lastName
        };
        return WL.Server.invokeHttp(input);
    }
    
    
    function getStoriesFiltered(){
    }
    
    My adapter has 2 methods first is getContactList() that returns all the contacts in the database by calling /ManageContact/rest/contact URL and it does not take any parameter. The searchContact() takes lastName as parameter and makes GET call to '/ManageContact/rest/contact/search?lastName='+lastName URL.
  3. I also had to change the ContactRESTService.xml so that my adapter descriptor looks like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <wl:adapter name="ContactRESTService"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:wl="http://www.worklight.com/integration"
      xmlns:http="http://www.worklight.com/integration/http">
    
      <displayName>ContactRESTService</displayName>
      <description>ContactRESTService</description>
      <connectivity>
        <connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
          <protocol>http</protocol>
          <domain>localhost</domain>
          <port>9000</port>      
        </connectionPolicy>
        <loadConstraints maxConcurrentConnectionsPerNode="2" />
      </connectivity>
      <procedure name="getContactList"/>
      <procedure name="searchContact"/>
      <procedure name="getStoriesFiltered"/>
    </wl:adapter>
    
  4. After that i had to make couple of my minor change in my WorkLight application so that it would use the HTTP adapter instead of the SQL adapter, This is how my JavaScript that makes call to the adapter looks like
    
    function getContact(){
     console.log("Entering getContact() REST service based version");
     var contactName = $('contactName').getValue();
     var invocationData = {
       adapter:"ContactRESTService",
       procedure:"searchContact", 
       parameters:[contactName]
     }
     var options ={
       onSuccess:loadContactSuccess,
       onFailure:loadContactFailure
     }
     WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result){
     console.log("Inside loadContactSuccess " + result);
     var html = '';
     
     if(result.invocationResult.isSuccessful){
      var contactList = result.invocationResult.contacts.contact;
      var i = 0;
      for(i =0 ; i < contactList.length ; i++){
       var currentContact = contactList[i];
       html =  html + '<li><a href="#">'+currentContact.firstName +' ' 
    +currentContact.lastName +'</a></li>';
      }   
     }
     
     jq("#displayContact").html(html);
     jq("#displayContact").listview('refresh');
    }
    
    function loadContactFailure(result){
     console.log("Inside loadContactError " + result);
    }
    
    First the getContact() which makes call to the HttpAdapter had to changed to use the name of the ContactRESTService as adapter and searchContact as procedure name. Then i had to change the loadContactSuccess() method the part which reads the search results. Worklight makes sure that i get results in JSON format with little bit different structure.

WorkLight resources directory

You might have noticed that when you make changes in any HTML or JavaScript file in the WorkLight Studio those changes do not get reflected right away instead you will have to Right Click on the Project and say Run -< Build All and Deploy. It seems that when you click on Build and Deploy the Studio generates the files required for WorkLight and for every Environment in the Worklight\server\widget-resources directory like this
My HelloWorkLight project supports ipad and iphone environment so every time i click on the Build All and Deploy, WorkLight studio creates relevant directories under Worklight\server\widget-resources directory one is HelloWorlight-common-*, HelloWorkLight-ipad-*,HelloWorkLight-iphone-* the * represents a integer that worklight keeps incrementing. WorkLight studio copies all the application related files in these folders, if you make a change directly in of these files those changes get reflected right away but then you will not have those changes in your project and you those changes wont get picked after next deployment. If you open the files in these directories you will notice that WorkLight adds quite a few elements to it. For example this is my html file

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, 
  maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
        <title>HelloWorklight</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/HelloWorklight.css" />        
    </head>
    <body onload="WL.Client.init({})" id="content" style='display: none'>
    <div id="AppBody">
      <div id="header">
        <div id="ReloadButton" onclick="reload();"></div>
        <h1>Basic Development</h1>
      </div>
      <div id="wrapper">
        <label for="actions">Display: </label>
        <select id="actions" onchange="displayInfo();">
          <option value="appEnvironment" selected="selected">Application Environment</option>
          <option value="language">Language</option>
        </select>
        <div id="info"></div>
      </div>
      <div id="worklight" onclick="loadWebPage();"></div>
    </div>

        <script src="js/HelloWorklight.js"></script>
        <script src="js/messages.js"></script>
        <script src="js/auth.js"></script>
    </body>
</html>
After deploy the same file gets changed to

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, 
  maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
        <title>HelloWorklight</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/HelloWorklight.css" />        
    <link rel="stylesheet" href="wlclient/css/wlclient.css" />
    <link rel="stylesheet" href="xilinus/css/default.css" />
    <link rel="stylesheet" href="xilinus/css/alphacube.css" />
    <link rel="stylesheet" href="xilinus/css/debug.css" />
    <script type="text/javascript">
        // Define WL namespace. 
        var WL = WL ? WL : {};
        
        /** 
         * WLClient configuration variables.
         * Values are injected by the deployer that packs the gadget.
         */
        WL.StaticAppProps = {
          "APP_DISPLAY_NAME": "HelloWorklight",
          "APP_LOGIN_TYPE": "never",
          "APP_SERVICES_URL": "/apps/services/",
          "APP_VERSION": "1.0",
          "ENVIRONMENT": "preview",
          "HEIGHT": 460,
          "IID": 0,
          "LOGIN_DISPLAY_TYPE": "popup",
          "LOGIN_POPUP_HEIGHT": 610,
          "LOGIN_POPUP_WIDTH": 920,
          "LOGIN_REALM": null,
          "PREVIEW_ENVIRONMENT": "common",
          "TIMESTAMP": "192.168.94.131 on 2012-03-28 at 17:00:00",
          "WIDTH": 320,
          "WORKLIGHT_ROOT_URL": "/apps/services/api/HelloWorklight/common/0/"
    };
    </script>
    <script src="common/js/prototype.js"></script>
    <script src="common/js/containerCommunicationAPI.js"></script>
    <script src="common/js/base.js"></script>
    <script src="wlclient/js/messages.js"></script>
    <script src="common/js/wlcommon.js"></script>
    <script src="common/js/busy.js"></script>
    <script src="xilinus/js/window.js"></script>
    <script src="xilinus/js/debug.js"></script>
    <script src="wlclient/js/worklight.js"></script>
    <script src="wlclient/js/gadgetCommunicationAPI.js"></script>
    <script src="wlclient/js/wlclient.js"></script>
    <script src="wlclient/js/wlfragments.js"></script>
    <script src="wlclient/js/encryptedcache.js"></script>
    <script src="wlclient/js/blockTEA.js"></script>

  </head>
    <body onload="WL.Client.init({})" id="content" style='display: none'>
    <div id="AppBody">
      <div id="header">
        <div id="ReloadButton" onclick="reload();"></div>
        <h1>Basic Development</h1>
      </div>
      <div id="wrapper">
        <label for="actions">Display: </label>
        <select id="actions" onchange="displayInfo();">
          <option value="appEnvironment" selected="selected">Application Environment</option>
          <option value="language">Language</option>
        </select>
        <div id="info"></div>
      </div>
      <div id="worklight" onclick="loadWebPage();"></div>
    </div>

        <script src="js/HelloWorklight.js"></script>
        <script src="js/messages.js"></script>
        <script src="js/auth.js"></script>
    </body>
</html>

Enable tracing for WorkLight

When i was developing application accessing database from Worklight i wanted to debug to figure out what Query the SQL Adapter is making also i wanted to figure out how to enable the logging tracing for WorkLight product itself. There is a log4j.xml file in the Worklight\server\lib folder that you can change and restart the WorkLight server and then you can check the Worklight\server\log\server\server.log file for actual log statements For Example i did change the log level for com.srndpt.adapters package to DEBUG and then when i tried executing the code that uses adapter to make SQL query can i could see following log statements 2012-03-28 15:50:27,326 DEBUG [SQLQuery] (pool-1-thread-2:682f4681-e757-4a24-b20a-34f655f48c68) Prepare statement: select * FROM CONTACT WHERE FIRSTNAME LIKE ? 2012-03-28 15:50:27,326 DEBUG [SQLQuery] (pool-1-thread-2:682f4681-e757-4a24-b20a-34f655f48c68) Execute the query 2012-03-28 15:50:27,326 DEBUG [SQLQuery] (pool-1-thread-2:682f4681-e757-4a24-b20a-34f655f48c68) payload received.