Showing posts with label mobile. Show all posts
Showing posts with label mobile. Show all posts

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 native alerts and cofirmations of mobile using Cordova

The PhoneGap/Cordova framework allows you to display native dialog box of the device, which allows you to use say custom title and better control on number of buttons on the dialog box. In addition to that it also allows you to beep or vibrate the device(Both beep and vibrate feature dont seem to work in the Android emulator). I wanted to try these features so i built this sample application, which allows you to play with different notifications. This is how the native alert box looks like This is how the native confirmation box looks like I followed these steps to build Android PhoneGap application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i used the following index.html code
    
    <!DOCTYPE html>
    <html>
      <head>
        <title>Google Map Example</title>
      <meta name="viewport" content="width=device-width, 
      initialscale=1.0, user-scalable=no"></meta>
        <script type="text/javascript" charset="utf-8" 
    src="cordova-1.7.0.js"></script>
        <script type="text/javascript" charset="utf-8">
        function alertDismissed() {
            console.log("Inside alertDismissed");
        }
        function onConfirm(){
          console.log("Inside onConfirmation");
        }
        function showAlert() {
            navigator.notification.alert(
                'Sample native alert message',  
                alertDismissed,         
                'Sample native alert title',            
                'alert'                  
            );
        }
        function showConfirmation(){
          navigator.notification.confirm(
                  'Sample native confirmation',  
                  onConfirm,              
                  'Sample confirmation title',            
                  'Confirm'          
              );
        }
        function playBeep() {
            navigator.notification.beep(3);
        }
        function vibrate() {
            navigator.notification.vibrate(2000);
        }
        </script>
      </head>
      <body >
        <ul>
          <li><a href="#" onclick="alert('Browser alert'); 
    return false;">Show browser Alert</a></li>
          <li><a href="#" onclick="showConfirmation(); 
    return false;">Show Confirmation</a></li>
          <li><a href="#" onclick="showAlert(); 
    return false;">Show Alert</a></li>
          <li><a href="#" onclick="playBeep(); 
    return false;">Play Beep</a></li>
          <li><a href="#" onclick="vibrate(); 
    return false;">Vibrate</a></li>
        </ul>
         <p></p>
      </body>
    </html>
    
    The HTML of the page shows 5 different links each pointing to one JavaScript function. Each of the functions uses Phone Gap native feature to display native dialog boxes.

Google Map in PhoneGap/Cordova application

I wanted to figure out how to use Google Maps API in the Phone GAP application so i built this sample application that lets you enter address and it displays that address on the Map, you can download the sample application from here This is a screen shot of how my application looks like
This is how the index.html page for my application looks like

<!DOCTYPE html>
<html>
  <head>
    <title>Google Map Example</title>
 <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0; padding: 0 }
      #map_canvas { height: 100% }
    </style>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <script type="text/javascript" charset="utf-8" src="jquery.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=true"></script>
    <script type="text/javascript" charset="utf-8">
    function displayCurrentLocation(){
     console.log("Entering displayCurrentLocation()");
  try{
   var currentLocationLatAndLong = new google.maps.LatLng(37.422006,-122.084095);
   var mapOptions ={
    zoom:8,
    center:currentLocationLatAndLong,
    mapTypeId: google.maps.MapTypeId.ROADMAP
   };
   var mapDiv = document.getElementById("map");
   map = new google.maps.Map(mapDiv,mapOptions);
  }catch(e){
   console.log("Error occured in ConsultantLocator.displayMap() " + e);
  }
     console.log("Exiting displayCurrentLocation()");
    }
    function addMarker(latLng,title,contentString){
  console.log("Entering addMarker()");
  var markerOptions = new google.maps.Marker({
    map: map,
    position: latLng,
    title:title,
    clickable:true
   });
  var marker = new google.maps.Marker(markerOptions);
  var infoWindowOptions = {
   content: contentString,
   position: latLng
  };
  var infoWindow = new google.maps.InfoWindow(infoWindowOptions);
  google.maps.event.addListener(marker, "click", function(){
   infoWindow.open(map);
  });
  console.log("Exiting addMarker()");
 }
    function getLatLangFromAddress(address){
     console.log("Entering getLatLangFromAddress()");
     var geocoder = new google.maps.Geocoder();
     geocoder.geocode( { 'address': address}, function(results, status) {

      if (status == google.maps.GeocoderStatus.OK) {
    var returnedValue =results[0].geometry.location;
       console.log("Address found is " + returnedValue);
       addMarker(returnedValue);
      }else{
       alert("Geocode was not successful for the following reason: " + status);
      }
        });
     console.log("Exiting getLatLangFromAddress()");
    }
    function addMarkerForAddress(){

     console.log("Entering addMarkerForAddress()");
     var address = $("#address").val();
     console.log($("#address"));
     var latLangForLocation = getLatLangFromAddress(address);
     console.log("Value returned by getLatLangFromAddress " +latLangForLocation);
     addMarker(latLangForLocation,address,address);
     console.log("Exiting addMarkerForAddress()");
    }
 document.addEventListener("deviceready", displayCurrentLocation, false);
    </script>
  </head>
  <body >
   <p>
    <input type="text" name="address" id="address" />
    <input type="button" id="getLocation" onclick="addMarkerForAddress()" value="Get Location"/>
   </p>
    <div id="map" style="width:100%; height:100%"></div>
  </body>
</html>
This application loads the google map during startup. When user enters address and clicks on get location it takes the address and uses it to find the latitude and longitude for that address and then users addMarker() method to display maker for that location

Using JQuery Mobile in PhoneGap/Cordova application

JQuery Mobile is one of the most popular Mobile UI frameworks, i wanted to figure out how to use it in Cordova application so i built a sample application that uses JQuery Mobile and also the Cordova device API to display device related properties in JQuery Mobile UI. You can download the sample application from here. I followed these steps to build this application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. I did download the jquery.mobile-1.1.0.zip from JQuery Mobile Download page
  3. I did unzip the jquery.mobile-1.1.0.zip in c:\software folder
  4. Next i copied the css, docs and js folder from C:\software\jquery.mobile-1.1.0\demos into assets/www folder of my web application.
  5. If your just starting with JQuery mobile or playing around then you can even directly point to the CDN URL of JQuery Mobile without actually downloading it in your application, in that case you can skip step 2 and 3. You can simply add these lines in your HTML page
    
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    
  6. Next change your index.html page to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <!--  
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    -->
    <link rel="stylesheet"  href="css/themes/default/jquery.mobile-1.1.0.css" />
    <script src="js/jquery.js"></script>
    <script src="js/jquery.mobile-1.1.0.js"></script>
    <script type="text/javascript" charset="utf-8">
     document.addEventListener("deviceready", onDeviceReady, false);
     function onDeviceReady() {
      console.log("Entering index.html.onDeviceReady");
      //var element = document.getElementById('deviceProperties');
      var html = "";
      html = html + "<li>" + 'Device Name: ' + device.name + "</li>";
      html = html + "<li>" + 'Device Cordova: ' + device.cordova + "</li>";
      html = html + "<li>" + 'Device Platform: ' + device.platform + "</li>";
      html = html + "<li>" + 'Device UUID: ' + device.uuid + "</li>";
      console.log(html);
    
      $("#deviceProperties").html(html);
      $("#deviceProperties").listview('refresh');
      console.log("Exiting index.html.onDeviceReady");
     } 
    </script>
    </head>
    <body>
    
     <div data-role="page" id="page1">
      <div data-theme="a" data-role="header">
       <h3>Hello JQuery Mobile</h3>
      </div>
      <div data-role="content">
       Device Properties
       <ul data-role="listview" data-inset="true" 
        id="deviceProperties">
    
       </ul>
      </div>
      <div data-theme="a" data-role="footer">
       <h3>Copyright stuff</h3>
      </div>
     </div>
     
    </body>
    </html>
    
    My application has one JQuery Mobile page whose content is defined using div with id equal to page1. The div with data-role equal to content defines the content of the page. By default it only has empty ul element, i am using onDeviceReady() JavaScript function to read device property and add them to the list.
This is screen shot of application once its loaded

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.

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

Forcing jQuery Mobile to re-evaluate styles/theme on dynamically inserted content

Today i was trying to build a JQuery Mobile application that has multiple pages. When user clicks on the page switch button i take the markup on the new page and insert it in the old page. One problem that i noticed is when you insert HTML into a page and that HTML has jQuery UI widgets then those widgets do not get evaluated, instead jQuery Displays them without any styles. You can solve this problem by calling the $('changedDiv').trigger("create") method with value of the div equal to id where the new markup got inserted


<div id='changedDiv'>
//Insert jQuery widgets here
</div>

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.

Using JQuery Mobile in WorkLight application

In the Connecting to Database from WorkLight i blogged about how to build WorkLight application that talks to database, that application did not had good looking UI, so i made changes to use JQuery Mobile that makes building good looking UI much simpler. This is how my application looks like now
These are the steps that i followed
  1. Create a simple WorkLight application that invokes a SQL query you can use the steps mentioned in Connecting to Database from WorkLight
  2. First download the Worklight Starter Application for JQuery Mobile from Worklight Getting started page
  3. Expand the WorklightStarter_jQueryMobile.zip some where on your local disk
  4. Copy jquery.mobile*.js and jquery-*.js from the WorklightStarter_jQueryMobile.zip to the js folder of your application, Also copy jquery.mobile.*.css into css folder of your application
  5. Change the html page of your application to include the JQuery related css and js, this is how the html page looks for me
    
    <!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({})" 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>
    
    In the head section first you should include jquery related css and js and also call var jq = jQuery.noConflict(); so that the jQuery $ character does not conflict with $ used by the prototype javascript framework used by worklight. Next use the jquery mobile template in the body section that divided the page into head, content and footer section.
  6. Add following code to the javascript file for your application
    
    function wlCommonInit(){
      // Common initialization code goes here
    }
    
    
    function getContact(){
      console.log("Entering getContact()");
    
      var contactName = '%'+$('contactName').getValue()+'%';
    
      var invocationData = {
          adapter:"mySQLAdapter",
          procedure:"searchContactByFirstName",
          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.resultSet;
        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);
    }
    
    As you can can see i am using jq("#displayContact")> instead of regular $("#displayContact") this is the nonconflict version of JQuery that you must use in the WorkLight, but your still allowed to use the prototype based syntax for looking up objects as well like i did in $('contactName').getValue()

Debugging problems with worklight database access

In the Connecting to Database from WorkLight entry i developed a simple application to demonstrate how to connect to a database from Worklight, when i was developing that application i used these tools to debug. It seems that when i make a DB call using WL.Client.invokeProcedure() method it makes REST call and gets the response back in JSON format, i used the firebug tool to see what is being passed in and out. This is what data gets passed to the server
This is the response of the server that contains the response data in JSON format.The response in green displays what happens in case of success and the response with red boundary displays what happens in case of error
On the server side after deploying adapter i looked at the Worklight\server\log\server\server.log file to get detailed information about the errors and Worklight\server\log\server\error.log for short information about the errors

Connecting to Database from WorkLight

I wanted to figure out how to use SQL Adapter provided by WorkLight to create application that talks with database, with the difference that i want to access Apache Derby instead of MySQL that is used by WorkLight, so i built this simple application that takes contactId as input then use it to execute SELECT * from CONTACT where CONTACTID=contactId query and display the result.
I followed these steps to build my application
  1. First i did open the Worklight\server\conf\worklight.properties file in the text editor and i did add a section to define the JDBC connection parameters for connecting to Derby at the end of the file like this
    
    training-jndi-name=${custom-db.1.jndi-name}
    custom-db.1.relative-jndi-name=jdbc/worklight_training
    custom-db.1.driver=org.apache.derby.jdbc.ClientDriver
    custom-db.1.url=jdbc:derby://localhost:1527/C:/data/contact
    custom-db.1.username=dbadmin
    custom-db.1.password=dbadmin
    
  2. Then i did copy the derbyclient.jar which is JDBC driver for Apache Derby in the Worklight\server\lib folder, when i was copying the derbyclient.jar i noticed that, the same directory also has the mysql-connector-java-*.jar that worklight needs for its own database connectivity
  3. After that i had to restart the server for my changes to take effect
  4. Next i did create mySQLAdapter project by following the instructions on Creating SQL Adapters
  5. I changed the mySQLAdapter-impl.js like this
    
    var procedure1Statement = WL.Server.createSQLStatement("select * from CONTACT where CONTACTID = ?");
    function procedure1(param) {
      return WL.Server.invokeSQLStatement({
        preparedStatement : procedure1Statement,
        parameters : [param]
      });
    }
    
  6. Then i did use the following mySQLAdapter.xml
    
    <?xml version="1.0" encoding="UTF-8"?>
    <wl:adapter name="mySQLAdapter"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:wl="http://www.worklight.com/integration"
      xmlns:sql="http://www.worklight.com/integration/sql">
    
      <displayName>mySQLAdapter</displayName>
      <description>mySQLAdapter</description>
      <connectivity>
        <connectionPolicy xsi:type="sql:SQLConnectionPolicy">
          <!-- Replace 'data-source-jndi-name' with the jndi name as defined in the data source. -->
          <!-- Example using jndi name: java:/comp/env/jdbc/ProjectDS 
             or using a place holder: ${project.db.jndi-name}       -->
             
          <dataSourceJNDIName>${training-jndi-name}</dataSourceJNDIName>
        </connectionPolicy>
        <loadConstraints maxConcurrentConnectionsPerNode="5" />
      </connectivity>
    
        <!-- Replace this with appropriate procedures -->
        <procedure name="procedure1"/>
    </wl:adapter>
    
  7. Once the adapter was ready i did deploy it on the server making sure that the deployment was successful
  8. Then i did create HelloDatabase application in the same project that has the adapter and i changed the main html to look 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>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" />        
        </head>
    
        <body onload="WL.Client.init({})" id="content" style="display: none">
         <table>
          <tr>
           <td>Contact Id</td>
           <td><input type="text" name="contactId" id="contactId"/></td>
          </tr>
          <tr>
           <td><button onclick="getContact()" title="GetContact" 
        label="GetContact">GetContact</button></td>
          </tr>
         </table>
         <div id="displayContact">
         
         </div>
         
            <script src="js/HelloDatabase.js"></script>
            <script src="js/messages.js"></script>
            <script src="js/auth.js"></script>
        </body>
    </html>
    
  9. Last step was to change the HelloDatabase.js like this
    
    function wlCommonInit(){
      // Common initialization code goes here
    }
    
    function getContact(){
      
      
      var contactId = $("contactId").getValue();
      console.log("Contact id " + contactId);
      var invocationData = {
          adapter:"mySQLAdapter",
          procedure:"procedure1",
          parameters:[contactId]
      }
      var options ={
          onSuccess:loadContactSuccess,
          onFailure:loadContactFailure
      }
      
      WL.Client.invokeProcedure(invocationData, options);
    }
    
    function loadContactSuccess(result){
      console.log("Inside loadContactSuccess " + result);
      console.log(result.invocationResult.resultSet[0].FIRSTNAME)
      $("displayContact").innerHTML = result.invocationResult.resultSet[0].FIRSTNAME + " " 
      + result.invocationResult.resultSet[0].LASTNAME +" " 
      + result.invocationResult.resultSet[0].EMAIL;
    }
    
    function loadContactFailure(result){
      console.log("Inside loadContactError " + result);
    }
    

Enabling debug/log console in Worklight

Recently i started learning about the IBM WorkLight and i followed the instructions on the Your First application to build a simple HelloWorld type of application. As a developer one of the first thing that i like to figure out how the application is actually working so i wanted to figure out a way to debug/enable log for worklight, and i followed these steps
  1. WOrklight generates log information as well as it allows you to use the same logger to write your own log information, How the log information is displayed depends on the client. For example in case of desktop client you can ask Worklight to display the log in a popup window by calling WL.Client.init({showLogger:true}); JavaScript method during onload event
  2. If you want you can use the WorkLight logger to write application specific log messages in the same logger you can do that by calling WL.Logger.debug("Sample debug statement"); WL.Logger.error("Sample error statement");
With these changes, this is how the wlCommonInit() method of my application looks like

function wlCommonInit(){
 console.log("Entering wlCommonInit");
 WL.Client.init({showLogger:true});
 WL.Logger.debug("Sample debug statement");
 WL.Logger.error("Sample error statement");
 console.log("Exiting wlCommonInit");
}
Now when i access the application through browser a logger windows gets popped up and it has the log messages like these

Setting up Worklight development environment

I want to learn about Worklight which is mobile application development platform that you can use for developing HTML5, native and hybrid applications. So i decided to install it on my machine and i followed these steps
  1. First i did go to Worklight Evaluation Version Download Page and i did download all the software listed on the page
  2. I followed the same steps mentioned in the document to install it on my Windows XP machine, The first time i installed MySQL on my machine i did not follow the MySQL Configuration Guide for Windows and my Worklight server installation failed to start on the first page where it asks for the MySQL connection information. So i did have to reconfigure it to use the steps mentioned in the MySQL Configuration Guide for Windows guide. I think unless you disable Strict Mode the WorkLight server install does not work.
Besides that installing the WorkLight steps on the document works pretty well.