Showing posts with label worklight5. Show all posts
Showing posts with label worklight5. Show all posts

Using AngularJs in Worklight/PhoneGap application

Angular.js is a JavaScript MVC framework, that makes development of HTML applications easy. I wanted to figure out how to use it for developing Worklight application so i followed these steps to build simple Hello AngularJs application
  • First create a WorkLight application using WorkLight wizard, make sure that it works
  • Next make changes in the index.html or entry page of your application to include angular.js from Google CDN and also add <p>Hello {{'World'.length}}</p>
  • to test if AngularJs template is working
    
    <!DOCTYPE HTML>
    <html>
    <head>
     <meta charset="UTF-8">
     <title>HelloWorld</title>
     <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0, 
    minimum-scale=1.0, user-scalable=0">
     <link rel="shortcut icon" href="images/favicon.png">
     <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
     <link rel="stylesheet" href="css/HelloWorld.css">
     <script>window.$ = window.jQuery = WLJQ;</script>
    </head>
    <body id="content" style="display: none;" ng-app>
     <h1>Hello Angularjs</h1> 
     <p>Hello {{'World'.length}}</p>
     <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js" />
     <script src="js/initOptions.js"></script>
     <script src="js/HelloWorld.js"></script>
     <script src="js/messages.js"></script>
    </body>
    </html
    
After deployment you will notice that it prints Hello + length of 'world' which is 5 characters like this

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

Using jQuery/jQuery mobile in Worklight 5

In the Using JQuery Mobile in WorkLight application entry i talked about how to use JQuery Mobile in the Worklight application, that was using Worklight 4.2.1 In the Worklight 5.0 version one big change is that the JQuery Mobile framework comes out of the box with Worklight, and unlike before we dont have to manually include the jQuery framework on the page and we wont have to use the following code

<script>
  var jq = jQuery.noConflict();
</script>
Instead if you take a closer look at the JavaScript file generated by the Worklight studio you will notice that it binds jQuery global variable at window.$ and also assigns it to WLJQ global variable like this

window.$ = WLJQ;
function wlCommonInit(){
 // Common initialization code goes here
}
But problem is that it does not assign the global jQuery variable to window.jQuery variable, so when you want to use jQUery mobile you have two options either you can include your own version of jQuery in addition to the one that comes OTB or you can modify the code like this

window.$ = WLJQ;
window.jQuery = WLJQ;
function wlCommonInit(){
 // Common initialization code goes here
}

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

PhoneGap/Cordova network connectivity information

The PhoneGap/Cordova framework allows us to get information about the device network status, i wanted to try this feature out so i used the following code in index.html

<!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>Hello Cordova</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 >
  <h1>Hello Cordova</h1>
  <button id="networkStatus" >Get network statusind</button>
  <script src="js/cordova.js"></script>
  </body>
</html>
This html displays only one Get network status button and when you click on it the following JavaScript function gets executed which displays the current network status as alert.

$("#networkStatus").click(function(){
   var networkState = navigator.network.connection.type;
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.NONE]     = 'No network connection';
    alert('Connection type: ' + states[networkState]);
});
First i used the Worklight Mobile browser simulator to test it and this is the screen shot. I tried to test the network status related API in the android simulator but i could only test connected and disconnected status. To get the disconnected status i had to go to Setting and switch to airplane mode without wi-fi

PhoneGap/Cordova batter status

The PhoneGap/Cordova framework has 3 events that your application can listen to get battery related information those events are batterystatus, batterylow, batterycritical This is sample code that i have to listen to battery related events

document.addEventListener("deviceready", function(){
  window.addEventListener("batterystatus", function(info){
    console.log("Inside document.addeventlistener -batterystatus ");
    console.log("Battery level " + info.level);
    console.log("isPlugged " + info.isPlugged);
  }, false);
  window.addEventListener("batterylow", function(info){
    console.log("Inside document.addeventlistener -batterylow " );
    console.log("Battery level " + info.level);
    console.log("isPlugged " + info.isPlugged);
  }, false);
  window.addEventListener("batterycritical", function(info){
    console.log("Inside document.addeventlistener -batterycritical " );
    console.log("Battery level " + info.level);
    console.log("isPlugged " + info.isPlugged);
  }, false);
 }, false);
I used the Worklight Mobile browser simulator to test this code, inside my event handler i am reading battery level and if battery is plugged in or not.

Events in PhoneGap/Cordova

The PhoneGap/Cordova framework allows us to listen to quite few different events, you can attach your event handler to event by using code like this

document.addEventListener("deviceready", function(){
 console.log("Inside document.addeventlistener -deviceready");
}, false);
document.addEventListener("pause", function(){
 console.log("Inside document.addeventlistener - pause");
}, false);
document.addEventListener("resume", function(){
 console.log("Inside document.addeventlistener - resume");
}, false);
document.addEventListener("online", function(){
 console.log("Inside document.addeventlistener - online");
}, false);
document.addEventListener("offline", function(){
 console.log("Inside document.addeventlistener - offline");
}, false);
document.addEventListener("backbutton", function(){
 console.log("Inside document.addeventlistener - backbutton");
}, false);
document.addEventListener("menubutton", function(){
 console.log("Inside document.addeventlistener - menubutton");
}, false);
document.addEventListener("searchbutton", function(){
 console.log("Inside document.addeventlistener - searchbutton");
}, false);
document.addEventListener("startcallbutton", function(){
 console.log("Inside document.addeventlistener - startcallbutton");
}, false);
document.addEventListener("endcallbutton", function(){
 console.log("Inside document.addeventlistener - endcallbutton");
}, false);
document.addEventListener("volumeupbutton", function(){
 console.log("Inside document.addeventlistener - volumeupbutton");
}, false);
document.addEventListener("volumedownbutton", function(){
 console.log("Inside document.addeventlistener - volumedownbutton");
}, false);
The Worklight mobile browser simulator makes testing event quite easy, one issue is that the deviceready event does not get called in the mobile browser simulator and it also does not let us invoke that event manually. But the wlCommonInit() can play the same role and it works quite ok in worklight

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.

Notes about Embedded Jetty server inside Worklight Studio

The Worklight 50 simplifies development by embedding the Worklight server as plugin. It uses embedded Jetty server for running the worklight application. This makes starting, stopping server and deploying application to server much faster compared to before. I noticed one problem with the Worklight embedded server which is that as soon as i start my IDE it starts embedded jetty on port 8080, which conflicts with others server application and so far i have not found a way to change this port number. Also when you try to stop the server from within worklight studio it does not actually stop the server. When you right click on your application and say "Build All and Deploy" it actually just copies the application in WorklightServerHome\<appname> directory. In that directory you can see few Jetty server related directories, there is also a log directory which has Jetty server related logs You can take a look at <workspace>\WorklightServerHome\<appname>\log\server.log for more details on the problems on server.

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.