Friday, December 30, 2011

Application Craft Introductory Tutorial - Set GMap Location



Overview
Application Craft is a Mobile, Tablet and Desktop development environment. See the side bar for other articles I've written about AC.  To work along with this tutorial you should sign up for an account here: http://www.applicationcraft.com/get-started

To see a finished version of the tutorial, visit: http://acft.ws/bnqs.  What this tutorial does is simply position the Google Map V2 to your current location.  Please use Chrome for this tutorial as we will also be utilizing  the Ripple Emulator and it only works in Chrome.  

Along the way we'll learn the following:

  • Place components on a page
  • Set the properties of a component
  • Center text on a heading
  • Repeat components across pages
  • Center a footer button
  • Adaptive layout - responsive design
  • Use the ACE Javascript editor
  • Add Javascript functions 
  • Change the Google Maps center position
  • Use the debugger
  • Use the Ripple Editor

Starting a project
Go to the URL that was provided during  the signup process and login.  You'll see the following screen.  Use the New button and create a App called GMapLocation.  For the Theme, select Mobile.


In the next screen, there are three items I'd like to call out to your attention.  

  1. On the left are three folders: Private, Shared and Public.  Notice that GMapLocation is in the Private folder.  Only you will be able to edit it.
  2. In the middle there is a Short URL.  This is useful for sending the app's link to your friends.
  3. On the bottom is the Edit button which we will not click.

AC IDE
In the next screen you see the AC IDE.  At the top there are 4 tabs: App, Edit, Layout and About.  You'll be working in the App tab most of the time and that's the current tab now.  In the middle section of the screen there are three vertical panels: on the left are currently the widgets that can be placed on a page.  In the center is the Page.  On the right are the Properties of the current selected component.  

Along the bottom of the screen there are a group of tabs on the left: Explorer, Code, Style and Toolbox.  The Toolbox tab is currently selected.  We'll be working in the Explorer and Code later.  In the center bottom position are controls for the Pages.  Since this is a single page app we won't be using that.  On the right bottom area is the Events and Properties tab.  The Events tab will change the display to show the available events for the currently selected component.  Currently the Properties tab is selected.

Now click the Page which is in the middle panel. You'll notice that the Page has a dotted line around it signalling the selected component.  On the right side set the name of the page to "home" and press the Save.  One real nice feature of AC is each Save has a unique version and you are able to revert back if necessary.  Just go back to the main dashboard and select the version you want from the Versions tab and click Revert.

Adding Toolbar Components

This next step is to add  all the components to the page.  We want to have the app work responsively in a web browser running on the desktop or mobile device and we want our native app to look well.

Also I want to show how to setup the toolbar component so that if there were multiple pages each page would have the toolbar consistently across the pages.

So first, within the left panel Toolbox, select the accordion called Mobile.  Select the Toolbar and drag it to the Page which is the white component in the center of the page. Drop the component near the top.


In the Properties editor, select the Layout and change it to Horizontal.  This will control how the components which will be added to this Toolbar will be laid out.  Set the "Align in Container" to center so that components will be centered.  Scroll down in the properties and change the Mobile Theme to Blue.  


Now select the Common accordion in the Toolbox and select the Label component.  Drag and drop it onto the Toolbar we just added.  While the text is still selected, or if necessary click on the text, you'll notice a little triangle at the bottom right hand corner.  Select that and drag it to the right to the end of the Toolbar.


Now in the Properties, set the Name to label. Set the Layout "Align in Container" to Center.  Select the Font and change the size to 32 in the popup dialog that appears.   Change the Widget Style to Title.


In the screen below, on the left is the Explorer view of the GMapLocation app.  The home page now has two components - the Mobile Toolbar and  it contains the label.  When the Sizes Property is edited, the Sizes dialog is displayed as shown below.  When you set the Percent Width to 100 it will resize to fill that available space.  We'll use this idea to have the app dynamically fit to the display.


To enable the Toolbar to be on every page, select the Repeat property and set the values as:

  • Select Repeat State: Repeat
  • Relative Position: Top
  • Select Repeat Use: All  
This means that this Toolbar will be repeated on all pages at the top.  If you want to include or exclude pages, change the Select Repeat Use option and include/exclude the desired pages.





Preview and Live Mode and Debug


Now Save your app and click the Preview.  There's not much going on right now.  Click the X to close it.  


In order to work with the debugger we have to a some Javascript command that informs the debugger we want to debug.  To enter Javascript code, press the Code tab at the bottom center.  It will open an editor.  Add the following two lines to the editor:



debugger;
console.log("GMap Location");



Line 1 instructs the debugger to stop here.  The line 2 writes out to the debugger console widow.  



So let's bring up the Chrome Debugger.  From the top right corner of the Chrome browser, select the Wrench icon, select Tools and then Developer Tools.  This will bring up the debugger.  Now click the Preview button again.


You should see the following screen and you should be stopped in the debugger on the "debugger" statement.  On the right press the continue key to do the log message.  From within the debugger, select the Console tab and see the message "GMap Location".






Personally I don't use the Preview mode much as I found it doesn't do exactly like the Live mode does as far as Layout is concerned.  So if I am interested in how the app looks and behaves, I use Live mode and debug from there.  I'll show you how to do that now.


First, let's turn of the Chrome debugger by clicking the X on the right hand side of the debugger itself, it's near the top of the debugger - see above picture.


Now do the Live Mode.  Your app will come up in a separate window.  Use your mouse and click the right mouse button and from the context menu select Inspect Element.  You'll have the debugger available now in a different window.


Adding the GMap Component


Now select the tab Design at the bottom of the IDE.  Select the Toolbox tab at the bottom left of the IDE.  Select the accordion More from the Toolbox.  Find the GMap component and drag it to the page.  Size it by dragging the little triangle at the bottom right corner of the component.  Leave some room at the bottom for the footer component.  


Be aware that you should utilize your own Google Api Keys for this component.  While in testing mode you can use the default ones provided.  But follow the process of getting your own keys by clicking the Google Api Key Property and then clicking the "Sign Up to get Google Api Key".


Adding the Footer Component and Button


We will now add a Container component called a Panel.  The Panel will contain 2 other panels.  The reason for this is we want to place a Button in the footer so we can get our Location.  We will want this button to be centered.  In order to do this we will add two components into our panel.  One will be a "spacer" and the other will contain our Button.  The spacer Panel will be configured to occupy the first 33% of the width.  That other container which contains our Button will then fill the next 33%.  Hopefully it will make more sense in a minute or two.


So now, from the Toolbox Containers, select Panel and drag it to the bottom of the Page.  Change the Layout to Horizontal. Set the Size to Percent Width 100, Width 332, Height 53.  


Select a Panel from the Toolbox Container again and drop it *inside* the Panel we just placed.  Set the Size to Percent Width 100, Width 174 and height 31.  Add another panel just like this one so that you have two panels contained by the first panel.  For both of these last two panels, set the Border to None.  On the last panel, set the Layout to Vertical.


We're now ready to add the Location button.  From the Toolbox Mobile select Action Button and drag and drop it on the last panel that was just added.  Change the Label to "Location" and change the Mobile Theme to Blue.


If you have difficulty selecting the correct components, switch to the Explorer view of the app.  This will now appear as below.




Javascript & JSLint


If you try the app in Preview and Live you'll notice the GMap doesn't resize properly.  The header and footer components do though.  We'll address that now by learning about Events and how to add Javascript.


First, be sure the Explore is displayed by clicking the Explorer tab at the bottom.  Click the home page. This will set the home page as the active component. 


We'll add an Event by clicking first the Events tab at the bottom right.  Then select the "On Resize" and from the drop down select Javascript.  Then do the same for "On Page Show".  At this time you should have two functions as shown below (be sure to remove the debugger and console.log from earlier).



function handler_home_onResize(ev, width, height){

}
function handler_home_onPageShow(){

} 

If you have an error with the Javascript syntax, you'll see a little red bar at the bottom of the page with a number on it indicating the number of errors encountered by JSLint. JSLint parses the Javascript code and determines if it is syntactically correct.  If not, you have an error displayed.


When you see a red bar indicating an error has been detected, click on the red bar and you'll see the details of what is causing the error.  Click on the red bar again to hide the details.  This is one of the many features that are part of Application Craft that I really appreciate.


When the page is first displayed, the event "onPageShow" will call our function handler_home_onPageShow().  When the page is resized, the other function will be called.


What we want to do is adjust the size of the components each time either of these events occur.  Fill in the code for the two functions as shown below:


function handler_home_onResize(ev, width, height){
   heightBar = app.getProperty("MobileToolbar","height");
   heightBottom = app.getProperty("PanelContainer", "height");

   app.setProperty("googleMaps", "height", height - heightBar - heightBottom - 30);
   app.setProperty("googleMaps", "width", width -5); 
}
function handler_home_onPageShow(){
   handler_home_onResize(null, window.innerWidth, window.innerHeight);
} 


Save your app and try the Live mode and verify that the Google Map component is now also full size.


Setting Google Map's Location


We want to now change the location of the Google Map display.  We will do that by adding a javascript handler for when the Location button is clicked.   We'll get the coordinates from the browser and set the center of the map with these values.


First, click the Design tab at the bottom.  Then click the Location button.  Then select the Events tab at the bottom.  For the event "On Click" select Javascript.


Fill out the functions as provided below:
function geoLocation_onSuccess(data) {
   map = app.w('googleMaps').googleMap();
   map.setCenter(new GLatLng(data.coords.latitude,data.coords.longitude), 13);
}
function geoLocation_onError(data) {
   debugger; 
}
function handler_actionBtn_onClick(mouseev){
   if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(geoLocation_onSuccess, geoLocation_onError);
   } else {
     return alert('Sorry, geolocation is not supported by your browser.');
   }
}



This code may need some explaining if you're new to Javascript.  When the Location button is clicked, the function handler_actionBtn_onClick will be called passing in the mouseev.  This value is the mouse event that cause the action.  If you want to see the values, add the "debugger;" statement and run it with the debugger active and see for yourself.

If the browser supports navigator.getLocation it will call getCurrentPosition and pass the values of two functions, one to be called when there is a success and the other if there is any kind of failure.  In our case, we have the function geoLocation_onSuccess for the successful call, and geoLocation_onError for unsuccessful.

The next bit of interesting code is what is happening in the geoLocation_onSuccess function.  Again, you can use the debugger to see the data values.  The first line uses an AC idiom of getting a component from the app.  But the component called "googleMaps" in this case is the AC Google Map Widget which contains the actual google map.  Sounds confusing?  Just realize that AC built a widget to contain the Google Map and provide Events and Properties for us to control the apperance at design time.  But now we want the actual Google map control.  So that's what is happening on that first line.

On the next line, we use the data coordinates passed in and create a GLatLng object initializing it with our values and then setting the center.  The 13 is the value for what zoom level the map should in.

That's all for the code and design.  We're ready to test it in the Ripple Emulator and generate our native apps.

Ripple Emulator

To use the Ripple Emulator you first have to be running in Chrome.  Then go to this page http://ripple.tinyhippos.com/download and follow the instructions.  Once the emulator is installed you want to Enable it.  In the top right corner of the Chrome Browser will be a icon like this: 



Select it and then from the popup dialog, click Enable.  The dialog stays and waits for you to click somewhere else.  So click the Live button back on the AC IDE.  


You can change the phone to your preference, change the orientation and enter the coordinates of your choice as shown below:




Summary


The next step in this maybe to generate the native apps by using the PhoneGap:Build functionality but that is available in the Pro pricing model - see http://www.applicationcraft.com/pricing-main and not the free.


Hope you enjoyed the ride!

Node.js & Oracle db - Not so bad union




Overview

When I was playing around with Node.js at work in scripting data files I thought about how nice it would be to directly use Oracle rather then run my queries in TOAD and export the data to a file for processing.   I can't say this idea was mine but I do think it's easier to get your hands around then this article (but then I'm slow) Combining java and Node.js through redis pub/sub and a json remote interface
Basic Idea


This proof of concept project is to provide access to a Oracle database from a Node.js client.


A java app is run from the command line and subscribes to 2 channels 
(query and storedProc) with redis using java lettuce client. It waits for messages. 


When it receives a message, the message contains the query or storedProc information and arguments.  The oracle database is accessed and the data is published as JSONARRAY of JSONObjects.  It continues to wait for more messages.


The redis node.js client (oraclePubSub.js) has 2 subscribers and 2 publishers.  The publishers send query or storedProc requests.  The subscribers listen for the messages containing the results.  The test client (testPubSub.js) creates two requests and processes the responses.

To run


 -  Open new terminal window
  - Open new terminal window
  • Install redis - this is the node.js redis client use https://github.com/mranney/node_redis 
  • do "npm install redis"
  • git clone git://github.com/bartonhammond/OraclePubSub.git
  • cd OraclePubSub\java\src\main\java
  • edit jdbc.properites - this assumes the sample HR schema is available
  • cd OraclePubSub\sql 
  • define hr.get_emp_rs.sql in hr.schema.  Otherwise test will fail
  • cd OraclePubSub\java
  • mvn package
  • cd target
  • enter "java -jar oraclepubsub-0.0.1-jar-with-dependencies.jar"  - This will hang waiting for messages.
   - Open different terminal window
  • cd OraclePubSub
  • node testPubSub.js


Code review

Java
The Java part of this is rather straight forward.  I won't go into the Oracle JDBC stored procedure or query except to say it's a toy program and I only want to prove to myself it could be done.   The interesting part here is in two methods:

public void processMessages() throws InterruptedException, Exception {
    Jdbcquery jdbcQuery = new JdbcQuery();
    while (true) {
        ChannelMessage cm = channelMessages.take();
        if (cm.channel.equals("query")) {
            JSONArray json = jdbcQuery.performQuery(cm.message);
            redis.publish("query-output", json.toString());
            
        } else if (cm.channel.equals("storedProc")) {
            JSONArray json = jdbcQuery.performRefCursor(cm.message);
            redis.publish("storedProc-output", json.toString());
        }
    } 
}

The ChannelMessage class has only 2 attributes - the channel listening to and the message.  The message contains the query or stored procedure. Depending on the channel, in line 6 or 10 a JSONArray is returned with the data ready to be published to the queue that the Node.js client is subscribed to.

Node.js

var redis = require("redis");

//Pair for storedProc
var storedProcSubscribe = redis.createClient();
var storedProcPublish = redis.createClient();

//Pair for query
var querySubscribe = redis.createClient();
var queryPublish = redis.createClient();

exports.setup = function () {
   storedProcSubscribe.on("ready", function () {
       storedProcSubscribe.subscribe("storedProc-output");
    });
   querySubscribe.on("ready", function () {
       querySubscribe.subscribe("query-output");
    });

}

exports.performStoredProc = function(proc, cb) {
    storedProcSubscribe.on("message", function (channel, message) {
        cb(message);
    });
    storedProcPublish.publish("storedProc",proc);
};

exports.performQuery = function(query, cb) {
    querySubscribe.on("message", function (channel, message) {
        cb(message);
    });
    queryPublish.publish("query",query);
};

//Client call to end the listening
exports.end = function end() {
    querySubscribe.end();
    queryPublish.end();
    storedProcSubscribe.end();
    storedProcPublish.end();

}
This code is really just 2 pairs of subscribers and listeners.  One listener and subscriber each for the query and storedProc.  Lines 4 - 9 are creating the redis clients.  

The setup function on line 11 subscribes for the output from Java.   For the storedProc, when  the message comes in it will be on line 22.  For the query, on line 29.  When the client Node call performStoredProc or performQuery,  they will publish their sql in the channels "storedProc" and "query" respectively.  

Finally, the end function on line 36 ends all the pub sub.

Let's look at the Node client.

//Setup the pub & sub
oraclePubSub.setup();

//Use async so that both can run in parallel
//and at conclusion the end can be invoked
//so script ends gracefully
async.parallel({

    //This query is same as stored proc so that testing was easier
    query: function(callback) {
        oraclePubSub.performQuery("select first_name, last_name, email,employee_id from HR.EMPLOYEES where department_id = 60 order by last_name, first_name asc",function(data){
            callback(null,data);
        });
    },
    storedProc: function(callback) {
        var foo = {};
        foo.sp = "BEGIN hr.get_emp_rs(?, ?); END;";
        //Do arg_types/args in parallel
        foo.arg_types = [oraclePubSub.sqlType.INTEGER];
        foo.args = [60];
        //What column contains the ResultSet
        foo.rs = 2;
        var fooStr = JSON.stringify(foo);
        oraclePubSub.performStoredProc(fooStr,function(data){
            callback(null,data);
        });
    },
},
               /**
                  * Results contains array 
                  */
               function(err,results) {
                   processQueryResults(results.query);
                   processStoredProc(results.storedProc);
                   oraclePubSub.end();
               });


function processQueryResults(queryResults){
    var qrArray = eval(queryResults);
    qrArray.forEach(function(employee) {
        console.log(employee);
    });
    
}
function processStoredProc(storedProcResults) {
    var sprArr = eval(storedProcResults);
    sprArr.forEach(function(employee) {
        console.log(employee);
    });
}

On line 2, I'm calling the setup we discussed earlier.  Notice on line 7 I'm using async.parrallel https://github.com/caolan/async- this allows my two queries to run in any order and I don't call my "end" function until both queries are done.  But I do want to call my end function when I am done.  That way I can merge the results if I choose first and then cleanly end the session.

On line 10, I'm performing a simple query against the demo database provided with Oracle.   Following the pattern of async parallel processing the results will be provided in line 32, the anonymous function.  Notice that the results object has two attributes - query and storedProc.  After processing these results, I can successfully end the session.


There are other async opportunities in this example but it's working well enough that I was able to improve on it and use it in my daily work.  Imagine that!