- First i did follow the steps described in the How to enable form based authentication in worklight application entry to enable the Form based authentication for the user.
- Then i did change my html page to look like this, I am using CDN version of the jQuery Mobile so i dont have to manually add the necessary css and JavaScript in my project
The .html page has 4 div's representing 4 jQuery Mobile pages they are<!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>JQMLogin</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="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> </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>Autenticated Page - Page1</h3> </div> <div data-role="content"> <h3>You are logged in -Page1</h3> <a href="#page2">Go to page2</a> </div> <div data-theme="a" data-role="footer"> <input type="button" value="Logout" onclick="WL.Client.logout('SampleAppRealm', {onSuccess: WL.Client.reloadApp});" /> </div> </div> <div data-role="page" id="page2"> <div data-theme="a" data-role="header"> <h3>Autenticated Page - Page2</h3> </div> <div data-role="content"> <h3>You are logged in -Page2</h3> <a href="#page3">Go to page3</a> </div> <div data-theme="a" data-role="footer"> <input type="button" value="Logout" onclick="WL.Client.logout('SampleAppRealm', {onSuccess: WL.Client.reloadApp});" /> </div> </div> <div data-role="page" id="page3"> <div data-theme="a" data-role="header"> <h3>Autenticated Page - Page3</h3> </div> <div data-role="content"> <h3>You are logged in -Page3</h3> <a href="#page1">Go to page1</a> </div> <div data-theme="a" data-role="footer"> <input type="button" value="Logout" onclick="WL.Client.logout('SampleAppRealm', {onSuccess: WL.Client.reloadApp});" /> </div> </div> <div data-role="page" id="loginPage"> <div data-theme="a" data-role="header"> <h3>Hello JQuery Mobile</h3> </div> <div data-role="content"> <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> <div data-theme="a" data-role="footer"> <h3>Copyright stuff</h3> </div> </div> <script src="js/JQMLogin.js"></script> <script src="js/messages.js"></script> <script src="js/auth.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> </body> </html>
page1, page2, page3 and loginPage
, the first 3 pages are pretty simple all they do is display link to other page and there is a logout button in the footer. TheloginPage
is used to display login page to the user, it is set as 4th page so that it does not get displayed to the user by default, instead we use the logic in auth.js to manually display and hide the login page. -
This is how the auth.js file for my application looks like
Most of the code in auth.js is same as that of How to enable form based authentication in worklight application, but changes are in two functionsvar 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'; 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("Inside auth.js.init"); $(LOGIN_BUTTON_ID).bind('click', onFormSubmit); }, isLoginFormResponse : function(response) { console.log("Inside auth.js.isLoginFormResponse " + response.responseText); 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("Inside 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("Inside auth.js.onShowLogin"); $.mobile.changePage("#loginPage"); }, onHideLogin : function() { console.log("Inside auth.js.onHideLogin"); $.mobile.changePage("#page1"); } }; }();
onShowLogin
andonHideLogin()
In theonShowLogin()
function i am using jQuery Mobile javaScript code to change the page tologinPage
and in theonHideLogin()
method i am resetting the page back to page1, but once the user is logged in you can move between page1, page2 and page3 and it works.
Using custom login page with Worklight and JQuery Mobile
In the How to enable form based authentication in worklight application entry i talked about the steps that you must follow to enable the Form based authentication in the Worklight application, so that as soon as user logs in he gets prompted for the user name and password and once he logs in then he sees the application page.
I wanted to try this with jQuery Mobile, basic idea is that i will have multi page application with jQuery Mobile and then it will also have a login page, so when user visits the application for the first time he see's the login page and after login user can go to the Home page and traverse across different pages.
This is how my login page looks like
Once user is logged in he will see the application page like this, there is logout button in the footer that let's user go back to login page.
I followed these steps to build my application
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
- The first step is to change the application-descriptor.xml file like this
I made one change in the application-descriptor.xml which is to add usage element with value of<?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>
requireAuthentication
equal toonStartup
which means user will have to authenticate at the start of the application. The second change is to addrealm
element with value equal toSampleAppRealm
- The
SampleAppRealm
is defined in the authenticationConfig.xml file like this
The<?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>
SampleAppRealm
is configured to use FormBasedAuthentication, which means we will have to submit a form toj_security_check
URL and we will have to usej_username
andj_password
field name for user name and password on the form TheSampleAppRealm
usesStrongDummy
asloginModule
which usescom.worklight.core.auth.ext.NonValidatingLoginModule
class for authenticating user name and password. theNonValidatingLoginModule
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. -
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.
The<!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>
AppBody
div displays the normal application body and theAuthBody
displays the login form -
TheLast 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(); } }; }();
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. TheonShowLogin()
page method gets called if the user is not logged in, in that method hide the application body and hide the login form. TheonHideLogin()
method gets called if the user is already logged in in that case hide the login form and display the body. TheonFormSubmit()
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)
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
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
<!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.

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
You can take a look at

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

<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.
Using jQUery .load(), ajax wrapper method
In the Posting data to REST service in PhoneGap/Cordova i talked about how to use jQuery to make a HTTP POST call to a REST service. In that case i used
$.post()
method, But jQuery also provides one wrapper method load()
that makes making AJAX call even simpler. I wanted to try that so i changed the sample to use the load()
.
The load()
method works on jQuery selector, so it first makes AJAX request and then takes the response of AJAX method and inserts it in all the elements returns by the selector. In my case first i did create a HTTP form and when i click on it, i want to collect all the values entered by the user on the form and use them to make HTTP POST call and once the result it returned i want to insert it into html div with id equal to result. This single line of code does everything
$('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact',
$("#insertContact :input"));
First i am selecting a div with id equal to result by using $('div#result')
selector, the http://192.168.1.101:9000/ManageContact/rest/contact
parameter is the URL to which the XHR request should be made. Second paramater $("#insertContact :input")
says that select the form insertContact
and return all the inputs on it in array format and submit them to the URL.
<!DOCTYPE html>
<html>
<head>
<title>Manage Contact</title>
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
$("#submit").click(insertContact);
function insertContact(){
console.log("Entering insertContact()");
$('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact',
$("#insertContact :input"));
return false;
}
});
</script>
</head>
<body>
<form id="insertContact">
<table>
<tr>
<td>Contact Id</td>
<td><input type="text" name="contactId" /></td>
</tr>
<tr>
<td>First Name</td>
<td><input type="text" name="firstName" /></td>
</tr>
<tr>
<td>Last Name</td>
<td><input type="text" name="lastName" /></td>
</tr>
<tr>
<td>Email</td>
<td><input type="text" name="email" /></td>
</tr>
<tr>
<td><input type="submit" id="submit" name="submit"
value="Submit" /></td>
</tr>
</table>
</form>
<div id='result'></div>
</body>
</html>
The load()
method looks at the second parameter to figure out if the request should be made using HTTP GET or POST. If i wanted to submit the form using HTTP GET i should have used
$('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact',
$("#insertContact").serialize());
Subscribe to:
Posts (Atom)