Wednesday, December 12, 2012

ApplicationCraft - Easy Notifications with Xtify & Pusher






Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

This blog is about learning how to notify connected or unconnected native mobile app users and also desktop browser web apps.  ApplicationCraft makes it very easy to build both web apps and mobile native apps.  So we'll be looking out how all this notification can be done with a single code base and deployed as a web app on the mobile and desktop and as a native mobile app.

I'll be using Xtify to do notifications to a native app.  The notifications will be sent to the mobile device and made available through the devices notification manager if the app is not running.  If the app is running, the app receives the notification.

I'll be using Pusher to do real time notifications to connected browsers running either on the desktop or on the mobile.

Video

The following video gives a brief overview of how AC utilizes Xtify and Pusher to notify connected and unconnected users.



Live App

Because this app uses my personal keys from Xtify, the Xtify portion won't work as I've changed the values. I've also changed the Pusher keys/secret values but if you have your own values, you can enter them in the UI and validate the Pusher portion works.

The app is here: http://acft.ws/nnqe

Source Code

All the source code for the ApplicationCraft app and the Pusher javascript class is located here: https://github.com/bartonhammond/TaskList

Xtify

Note: I'm only showing how to set up the Android.

Xtify is provided by ApplicationCraft as a plugin for the AC Mobile Build.  You can read about setting Xtify up by starting here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/

You first start here: http://developer.android.com/guide/google/gcm/gs.html and then take the link with "Google APIs Console Page".  On the following picture you'll create a API Project - be sure to name the Project ID with some meaning text.

On your API Access tab you will want to copy the API Key:


Now over to Xtify,  once you've registered you'll go to console and then App Manager -> App Key Manager which is at https://console.xtify.com/manage-app-keys.   Here you'll add a new Application and provide the Google API Key from the previous image above.


Then you have to go to the App Manager -> API Keys Manager and "Add New API Key" and be sure to add the Advanced Key Type:

Let's look at the code required for this

Now we're almost ready to send a message but first we need to set up a few things.  The app needs some configuration which is documented here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/configuring-your-app/

Once you have the configuration in place and the code added to the "handler_application_onAppStarted()" function, we need to invoke the SSJS (Server Side JavaScript) to do the Push Notification.  Now typically the SSJS would be written to respond to a DB commit and notify everyone of the change.  Or in my case the SSJS function will be monitoring for Date/Time reminders by checking the database.  When a reminder is due, a notification will be sent out.

On the client side we have a simple function:
/**
 * Test my device using XID
 */
function handler_sendMessageBtn_onClick(mouseev){
    var xid = app.getData("xidTxt");
    debugger;
 app.callSSJ('pushMessage',function(foo) {
       console.log(foo);
 }, [xid]);
}
We're call the SSJS function "pushMessage" and provide the XID from the textField from line 5.

On the SSJS function we have:
function pushMessage(xid) {
    var url = "http://api.xtify.com/2.0/push";
    var apiKey = "fced37b9-0a50-40ba-a0d9-6fd4dc018dcd"; //https://console.xtify.com/api-keys-manager
    var appKey = "ac7d8afd-50d7-4af5-a312-ae3c883adfb2"; //https://console.xtify.com/manage-app-keys 
    var headers = {"Content-Type": "application/json"};
    
    
    var request = buildPushRequest(apiKey, appKey, xid);
    
    var response = ssj.httpRequest(url, "POST", ssj.OBJ2JSON(request),"string", headers);

    return response;
}

function buildPushRequest(apiKey, appKey, xid) {

    var request = {
        "apiKey": apiKey,
        "appKey": appKey,
        "xids": [ xid ],
        "content": {
            "subject": "Test Subject Barton",
            "message": "Test message Barton"
        }
    }
    
    return request;

}

The function "pushMessages" has the apiKey and appKey defined.  The apiKey is from //https://console.xtify.com/api-keys-manager while the appKey is from //https://console.xtify.com/manage-app-keys

The function "buildPushRequest" just builds an object and substitues the apiKey, appKey and the one XID that we're targeting.

On line 10 the SSJS httpRequest method is used to send the Push Notification.  Note that the request object on line 10 has to be converted into JSON with the "ssj.OBJ2JSON" function.

You're now ready to build the native Android app.

AC Mobile Build

After you configure your app, then you can proceed to the AC Mobile Build.  The build is very straight forward - just close your app and from the console select Mobile Build -> AC Mobile Build.  For Android, I used the default certificate.

Install the generated app on to your Android device.  You don't have to run it but if your do, go ahead and close it so this next part works right.

Sending A Push Notification with Xtify

You're now ready to send a Notification to your Android.  You'll have to run the native app once on the Android and click the button "Display Xid on Mobile" and copy that.  You'll want to close the Android app and bring up your desktop browser and past the XID into the second section (see the video above).

Click the "Send Mobile Notification" and watch your Andriod get notified.  You can then click on the notification and it will start up your native app.

Pusher

Let's look at the SSJS Pusher code

Now on to using Pusher to send real time notifications to connected browsers.  First let's look at the code that was used to make all this possible.

I wrote an Pusher javascript class that requires a class to do Crypto from Google call crypto-js.  Within AC on the server side, I created a new page and copied / pasted the code from Google.

You'll want to do the same thing with my Pusher class - just copy/paste into a Server page. Below is the implementation of the ApplicationCraft version of SSJS Pusher:
//This is based of the work of
//https://github.com/fabrik42/pusher
//
//
//For details see: http://pusher.com/docs/rest_api

/**
 * Constructor expects options as:
 * 
 * {
        appId:  YOUR_app_id,
        appKey: YOUR_auth_key,
        secret: YOUR_auth_secret
    }
    */
function Pusher(options) {
    this.options = options;
    this.url = 'http://api.pusherapp.com';
    this.headers = {"Content-Type": "application/json"};
}
/**
 * Return a channel
 */
Pusher.prototype.channel = function(chan){
    this.options.channel = chan; 
    return this;
};
/**
 * Trigger the event for this channel
 */
Pusher.prototype.trigger = function(event, data) {
    this.event = event;
    this.data = data;
    
    this.jsonData = ssj.OBJ2JSON({"name": event,"channels":[this.options.channel],
        "data":JSON.stringify(data)});
    
    this.body_md5 = ssj.md5(this.jsonData);
        
    var response = ssj.httpRequest(this.path(),
        "POST", 
        this.jsonData,
        "string", this.headers);
};
/**
 * Return the url path to POST to
 */
Pusher.prototype.path = function() {
    return this.url 
        + this.uri() + '?' 
        + this.queryString() 
        + '&auth_signature=' + this.signature();
};
/**
 * Return the uri
 */
Pusher.prototype.uri = function() {
    return '/apps/' + this.options.appId + '/events';
};
/**
 * Return the queryString with the body_md5 
 */
Pusher.prototype.queryString = function() {
    var timestamp = parseInt(new Date().getTime() / 1000);
    return [
      'auth_key=',        this.options.appKey,
      '&auth_timestamp=', timestamp,
      '&auth_version=',   '1.0',
      '&body_md5=',       this.body_md5,
    ].join('');
}
/**
 * Return the Google HmacSHA256 for the querystring
 */ 
Pusher.prototype.signature = function() {
    var signData = ['POST', this.uri(), this.queryString()].join('\n');
    var crypto = CryptoJS.HmacSHA256(signData, this.options.secret);
    return crypto.toString();
};

The class is documented and I won't go into a lot of detail here.  I based this class off a node.js version I found here: https://github.com/fabrik42/pusher

The class Pusher is defined on lines 16-20.  It assumes that the options object will be passed in which has your app_id, auth_key and secret.  These are the values that you entered via the UI.

Pusher.com documents how the Restful API for the server is done.  You can read about it here: http://pusher.com/docs/rest_api.  If you follow through the Pusher class along with the reference, you should be able to follow along.  There's some tricky javascript stuff but not too bad.

Note on line 77 the use of the Google CrytoJS.HmacSHA256 function.

Let's see how easy it is to use this Pusher class.
/**
 * Pusher notification message to channel/event
 */
function pushNotification(app_id, auth_key, auth_secret, channel, event, message) {
    //var app_id = '33490';
    //var auth_key = '282e38c72e6f4fd8ff8b';
    //var auth_secret = '3c78d3ce5911feead471';
    
    debugger;
    var pusher = new Pusher({
        appId:  app_id,
        appKey: auth_key,
        secret: auth_secret
    });
    
    var channel = pusher.channel(channel);


    channel.trigger(event, message);

}
This SSJS function has all the required fields passed to it from the client side code.  On line 10, a new instance of Pusher is created and a object with the appId, appKey, and secret is passed as the options.

On line 16, the channel is defined and a instance is returned.

On line 19, the event for this channel is triggered - it's fired or sent in other words.  It fires with the message.

The SSJS Pusher class makes all this very easy.

Now let's look at the Pusher Client

Before you use Pusher on the client you have to include the file "http://js.pusher.com/1.12/pusher.min.js".  You do this in the Application Properties -> Preload Files.

The following code demonstrates how to subscribe the client to the channel and event:
/**
 * Subscribe to Pusher event
 */
function handler_subscribeBtn_onClick(mouseev){
    //key is from http://app.pusherapp.com/apps/xxxx/api_access
    var pusher = new Pusher(app.getData('appKeyTxt'));
    var channel = pusher.subscribe(app.getData('channelTxt'));
    channel.bind(app.getData('eventTxt'), function(data) {
       alert('just got a mesesage: ' + JSON.stringify(data));
    }); 
}

On line 6 a new Pusher is constructed with the appKey value from the textbox.

On line 7, the pusher object subscribes to the channel which is provided by the textbox.

On lines 8-10, the channel is bound to the event and the callback function is used everytime a message is received.  In this case, on line 9, an alert displays the stringified data.

Summary

Using both Xtify and Pusher with ApplicationCraft is rather easy.  Admittedly it was somewhat of a challenge to figure it out and write the Pusher object only because there wasn't a server side library available for AC.  But for those that are wanting to use Pusher, it should be rather straight forward now.  I think the Xtify is more difficult to setup but if you follow the docs it will make sense.  Hopefully this blog helps with some of that setup.

I hope you enjoyed this blog.






Friday, December 7, 2012

Easy Testing with Application Craft and QUnit

&

Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

I am in the process of writing a TaskList using Application Craft.  This blog is about learning how to do unit and intergration test with ApplicationCraft.  Unfortunatly ApplicationCraft doesn't provide a testing framework.  So I did some searching around for a javascript testing framework and found QUnit.  This testing framework was originally written by John Resig of JQuery frame.  I figured since AC is written using JQuery Mobile that this would be a reasonable testing framework for my purposes.

Video

The following video gives a brief overview of how AC was utilized to drive the tests and how to incorporate QUnit.  

Live app

The live app can be run from here

Source code

As usual, you can download all the code as a ApplicationCraft project from my github account for TaskList.

Tutorial

So to start off we will look at the Step 3 - Unit Test project and how it is configured.  If you watch the video it will help alot.

Setting up the Step 3-  Unit Test App

  • On the testing app page, set the property "Preload Files" to use http://code.jquery.com/qunit/qunit-git.js and http://code.jquery.com/qunit/qunit-git.css
  • On the testing app page drop a HTML component and add the following which is used by QUnit to display the test results.  
    <h1 id="qunit-header">QUnit Test Suite</h1>
    <h2 id="qunit-banner"></h2>
    <div id="qunit-testrunner-toolbar"></div>
    <h2 id="qunit-userAgent"></h2>
    <div id="qunit-testresult"></div>
    <ol id="qunit-tests"></ol>
    <div id="qunit-fixture">
          <div id="container"></div>
    </div>
    
    
    

  • Be sure to enable the HTML component for Resizing.
  • Add an Embed App widget (See Toolbox -> Advanced).  Once you drop that widget set the property Embed App to the "Step 2 - Persistence" app.
  • Select the AppEmbed component and in the Events tab, select the On Started so that a function is created.

Our first test

A little prep work in the SUT (System Under Test - in this case Step 2 - Persistence app).


This first test will demonstrate how to access a client side function in the embedded app.  In the Step 2 - Persistence app (the app being tested) there is a silly function I've added call "sayHi".  Now just how does the parent app (Step 3 - Unit Testing) get access to that embedded app's function?  Well, the trick is to set a global object with that function as a member.  The following code demonstrates how an Embedded App function is made available for testing.
//Silly function for demonstration purpose
function sayHi(foo) {
    return "Hi " + foo;
}

//Set global object
var gObj = {};

//Set the functions in the global object
gObj.sayHi = sayHi;

gObj.fillZingerChart = fillZingerChart;

//Make the object available 
app.setGlobalData('gObj',gObj);

The function "sayHi" is defined on lines 2-4.  It merely returns the parameter prepended with "Hi ".

On line 7, there is a object defined.  On lines 10 & 12, that object is updated to have two functions: 1) sayHi and 2) fillZingerChart.  These are the two client side functions that I want to test so I define them to this object.

On line 15 that object gObj is set into the global data with the key 'gObj'.  We'll see later how the unit testing code can now access that gObj and invoke these client side functions.

The actual client test code

The following code is entered into the embedded on start function as shown below

function handler_appEmbed_onStarted(){
    /**
     * Simple client side test
     */
    test('call client side function',1, function() {
        var gObj = app.childApp('appEmbed').getGlobalData('gObj');
        var rtn = gObj.sayHi('Barton');
        equal(rtn,'Hi Barton','sayHi should say Hi Barton');    
    });
So line 5 starts the test case.  The first parameter to "test" is a description of what the test will do.  It will show up later in the test results that QUnit provides.  The second parameter, 1 in this case, says how many assertions will be done.  Then a callback function is provided that will be executed when the test is run.

On line 6, we reference the embedded app and get the Global Data value for the key 'gObj' which we set earlier in the client/embedded app.

On line 7 we invoke the "sayHi" function and pass a parameter and save the return value.

On line 8 we assert that the return value is correct.  The first parm is the actual value, the second is the expected and the third value is a description of what the test is asserting.

There you have it - our first unit test run from within Application Craft to test Application Craft.  The code is external to the code we're testing and all the features of being a project within Application Craft are ours for free: source control, debugging, the IDE, the editor, etc.

Let's test the SSJS code

In this next bit of testing we are going to invoke some SSJS code.  Now this gets a little more complex because you have to let the test run asyncronously.  So I want to call three methods: 1) clear the log 2) write to the log 3) return the logs.  I want to confirm that if I send only one log, that I get only one log.  

Note that this test also is running in the embedded on Started method so that all the "plumbing" is in place.
/**
     * Simple server side only test 
     */
    test('test log get cleared and only 1 log is added',3,function() {
        stop();
        //Clear the log 
        app.childApp('appEmbed').callSSJ('clearLog', function(error,data){
            //Set one log object
            app.childApp('appEmbed').callSSJ('setLog', function(error,data) {
                //Get all the logs
                app.childApp('appEmbed').callSSJ('getLog',function(error,data) {
                    start();
                    equal(data.length, 1, 'only one log should be returned');
                    equal(data[0].id, 1, 'id should be 1');
                    ok(data[0].time > 1, 'time should have a value');
                },[true]);
            },[{id:1},true]);   
        },[]);
    });

The test is defined starting on line 4 and it expects to have 3 assertions which are on lines 13-15. In the callback of the test, on line 5, the tests are stopped because we're doing async calls.  The first call to the server is on line 7 which clears the log.  In the callback of that function, on line 9 a call is made to "setLog".  On the callback of that function call on line 11, a call is made to get the log.  Within the callback function we start the QUnit testing again with a "start" on line 12.  Lines 13-15 assert that the logs returned are of length 1 and the data contains the "id" and "time" attributes.

Combining SSJS and Client side testing

This next bit of code calls the SSJS function to "startTesting" which returns the logs from the test run.

    /**
     * Combination SSJS & Client side test
     */
    test('fill Zinger chart data',7, function() {
        stop();
        //Run 1 series of CRUD tests
        app.childApp('appEmbed').callSSJ('startTesting', function(error,data) {
            start();
            equal(data.length,10,'there should be 10 logs');
            
            //Start client side test
            var gObj = app.childApp('appEmbed').getGlobalData('gObj');
            var jsonData = gObj.fillZingerChart('test',data);
            
            //Validate the title
            var title = jsonData.graphset[0].title.text;
            var tokens = title.split(' ');
            equal(tokens[0],'test','title should be test');
            
            //Validate there are only 4 series (CRUD) and one value per series
            equal(jsonData.graphset[0].series.length, 4, 'there should be 4 series');
            for (var row = 0; row < 4; row++) {
                equal(jsonData.graphset[0].series[row].values.length, 1, 'there should only be one series in '
                + jsonData.graphset[0].series[row].text);
            }
        },[1,app.childApp('appEmbed').getValue('databaseRadio').value,false,false]);

          
    });
 
On line 4, we provide a description for the test and state that there will be 7 assertions.  In the callback we immediately stop all testing on line 5.  On line 7 we call the SSJS function "startTesting" and provide the required parameters on line 26.  Note on line 26 how the embedded apps component is referenced to obtain the DB connection string.

In the callback of the "startTesting" SSJS call, on line 8 we start our testing again and validate that there should be 10 log statements.

On line 12 we call get the global object again so that we can reference the client side function "fillZingerChart" which is called on line 13. Lines 16 - 26 then just validate that the structure built by "fillZingerChart" is basically correct.

Interacting with the UI components

This last test demonstrates how to interact with the visual components of the embedded app.


/**
     * Interact with the UI and run 20 tests confirming under 2 seconds 
     */
    test("run 20 crud tests under 2 seconds", 3, function () {
        stop();
        
        //Navigate to the 'crud' page
        app.childApp('appEmbed').w('actionBtn').base().trigger('vclick');
   
        setTimeout(function() {
            //Confirm crud page
            equal(app.childApp('appEmbed').currentPage().name(),'crud');
            
            //Set UI component to 20 CRUD tests
            app.childApp('appEmbed').setValue('numberOfTestsSlider',20);
            
            setTimeout(function() {  
                //Confirm UI set correctly
                equal(app.childApp('appEmbed').getValue('numberOfTestsSlider'),20, 'slider was set'); 
                
                //Start the test
                app.childApp('appEmbed').w('startTestBtn').base().trigger('vclick'); 
                
                setTimeout(function() {
                    //Get the data from the Zingchart
                    var json = zingchart.exec(app.childApp('appEmbed').w('ZingchartAC').getZingChartId(), "getdata");
                    var title = json.graphset[0].title.text;
                    var tokens = title.split(' ');
                    //Confirm the time is less then 2 seconds
                    ok(tokens[2] < 2000, 'the query is less then 2 seconds');
                    start();
                },4000);
                
            }, 2000);
            
        }, 2000); 
    });
Now the problem we're faced with again is the asyncronous of the calls.  Everytime we interact with a visual component we have to give up control and let the embedded app respond to the interaction.

So in the code above, on line 4 we setup the test and state that there will be 3 assertions.  In the callback we immediately stop the tests so that we can interact with the visual UI components.

On line 8 we trigger a "click" event on the embedded apps actionbtn.

On line 10, we set a standard javascript timeout function for 2000 milliseconds.  When that period of time expires, the function is invoked on line 12.  Here we assert that our new current page in the embedded app is  named 'crud'.

Now on line 15 we set the Slider component to have the value of 20 representing the number of tests to run.

Again we set a timeout to allow the component to respond.  The timeout is also 2 seconds.  In the callback on line 19 we validate that the slider component was indeed set to 20.

Now on line 22 we trigger the "Start Test" button and immediately on line 24 do another timeout.  At the completion of these 4 seconds (line 32),  we can reference the ZingChart that was created.  The ZingChart provides us the JSON data that is then validated on line 30.

Now this is an integration test - I'm using a real database and actually performing the 20 tests.

Summary

As you can see doing unit and integration tests with Application Craft is not so bad.  Other then the asynchronous call the effort is fairly straight forward.

What I really like about this approach is that I continue to do all my work using Application Craft itself.  I didn't have to start writing code using a 3rd party product and then have to worry about where to store that code in GitHub or whatever.  I didn't have to learn yet another tool to do debugging and editing.  With this approach we get to leverage all the power of Application Craft!

Please feel free to leave comments.

Friday, November 30, 2012

Application Craft - Easy CRUD

Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

This blog will address setting up a database with one table representing the users, performing CRUD operations, run performance tests between the AC database vs Xeround.  I want to see if there's any advantage of one cloud database provider vs the other.

Video / Caution with that SessionObject

Please review the video as it shows the results of this analysis.  It appears that either using AC Database or Xeround is sufficient.  The question may be more to space available as Xerounds free account limits you to 10Mb.  

I bumped into something that was rather interesting.  When running the tests it appeared that the longer I ran the worse the performance became - quite large in fact.  

It took me some time to finally figure out what was causing it - the ssj.SessionObject.  You can watch the video and see all the test results are look at the pictures following for comparison when SessionObject is not used.


The following two pictures demonstrate the difference between running the same number of tests (20 CRUDs) with and with out using the SessionObject:

Note in the above charts that the number next to AC Database and Xeround is the time the entire test took in milliseconds.

Now the results for the same number of tests with the "useSession" checked:


Notice how both graphs increase duration of test run over time.  And look at the total for entire run!  It went from less then 2 seconds to around 15 seconds!  Oh my.  I entered a defect in the AC portal Issue # 6807

Source code

You can clone my Git repository or just use the Zip option.  Once you have the source code you will find the  "app_Step+2+-+Persistence.acft" which can be imported to AC by going to "Apps" -> Import.  My github account is here: https://github.com/bartonhammond/TaskList

Database

I'm creating a a database table with the fields I've captured from the Social Network Login.  See this blog Easy oAuth for more info.  I determined that the three Social Networks that AC supports (Facebook, Twitter and Dropbox), have two fields that I want to retain, namely the id and name.  The id is unique to the social network but together with the social network, a unique key can be created.  Since I don't have any intention of contacting the users directly with email, I don't need it.  And besides, Twitter doesn't provide it.

The table I've created is called AppUser.  The schema looks like this:

-- Create Table: AppUser
CREATE TABLE AppUser
(
`userId` INT NOT NULL AUTO_INCREMENT
,PRIMARY KEY (userId)
,`name` VARCHAR(250) NOT NULL
,`source` VARCHAR(10) NOT NULL
,`id` INT NOT NULL
,`created` TIMESTAMP NOT NULL
        , index sourceid (id, source)
)
ENGINE=INNODB
The  userId is auto increment - each time an insert is created, a incremented int is assigned.  The name is the field that the social network provides.  The source is going to be either "facebook", "twitter" or "dropbox.  The id is the unique id the social network provided.  The created timestamp lets me keep track of modifications.  Note there is a index call sourceid which is made up of two columns, id and source.

Creating and establishing the database

AC currently will support setting up a database for you by asking their support@applicationcraft.com.  That's what I did and Ian Jobling set it up and got back to me the next day with account information.  What you get is a userid/password and a url to a PhPMyAdmin account.  You can easily create the table using the script I provided above. 

Setting up Xeround was just as simple, in fact they use the same PhpMyAdmin tool.

So once you setup the database and have the table defined you need to set that up in AC as a Connection.  Here's a screen shot of what that looks like:

Note that the Server value is *not* http://.  I put that in first and it caused some problems.  The other issue I had was getting the Database field to respond correctly.  That field is actually a drop down list box that is populated from the connection information provided.  I finally got it to work correctly by first Save'ing it and then try selecting the Database option.

Let's look at the CRUD UI

So my objective with the following UI was simply to kick off some tests from the client and compare the response time between AC Database and Xeround.   The test I am considering is simply a CRUD on the AppUser table.  I only need to capture the time for each start/end of the separate CRUD operations and then display them within a chart.


Now there are a couple of check boxes that need explanation.  When I first started with this I wanted to see how much time it took to get a connection, start a transaction, execute the sql, and commit the transaction.  As you'll see in the code later, I put some controls to limit how much data was being generated.  The checkbox "Detail" controls that behavior.  

The next checkbox, "useSession" is rather interesting.  I wasn't sure how to capture the logging information I was going to generate.  At first I was going to drive the test from the client in that each request would be initiated from the client and the SSJS would only perform the requested operation (Create, Read, Update, Delete).  But I decided that I wanted the client to be a little more dumbed down.  That meant I need to save the logs on the server.  My first thought was just to use the SessionObject so that's what I did.  This checkbox controls the use of that SessionObject and when unchecked, just stores the logs in a global array.

The radio button group allows me to easily toggle between the two database connections.  

Let's look at some code

On Start Test clicked

The client side code is pretty straight forward.  When the Start Test button is clicked we do the following:
function handler_startTestBtn_onClick(mouseev){
    var numberOfTests = app.getValue("numberOfTestsSlider");
    var connection = app.getValue("databaseRadio").value;
    var detail = app.getValue("detailChkbox");
    var useSession = app.getValue("useSessionChkBox");
    
    app.callSSJ('startTesting', function(error,data){
          console.log(data);
          fillZingerChart(data);
        }, [numberOfTests,connection,detail, useSession]); 
}
lines 2-5 are simply pulling out the test run configuration options.

lines 7-10 - Here we call the SSJS function "startTesting" and pass in the parameters numberOfTests, connection, detail, and useSession.  When the SSJS function completes it returns to the anonymous function and provides the results in the parameter "data".  After logging the data, the client side function "fillZingerChart" is passed the data.

SSJS logging

There are three simple functions that do the logging.  They are shown below:
logData = [];
/**
 * clear the session log
 */
function clearLog() {
    ssj.setSessionObject('logData',[]);
}
/**
 * get the sessionObject and if the value is null,
 * set the sessionObject
 */
function setLog(obj, useSession) {  
    if (useSession) {
        logData = ssj.getSessionObject('logData');
        if (logData === null) {
            logData = [];
        }
    }
    obj.time = (new Date()).getTime();
    var newObject = JSON.parse(JSON.stringify(obj));
    logData.push(newObject);
    
    if (useSession) {
        ssj.setSessionObject('logData',logData);
    }
    
}
/**
 * return the sessionObject
 */
function getLog(useSession) {
    if (useSession) {
        return ssj.getSessionObject('logData');
    } else {
        return logData;
    }
}
line 1 is a global declaration of the logData. If I don't use the SessionObject, I use this global object.

line 5- 7 the function clearLog simply clears out the SessionObject variable "logData"

lines 12 - 25 the function setLog does different things depending on the useSession value.  If useSession is false, then only lines 19-21 are utilized.  line 19 set a time variable into the object.  line 20 is a poor mans deep copy of a javascript object.  If the copy is not done, then the log contains all the entries but there all of the last object that was created.

lines 13-16 if useSession is true,  check if there's anything in the SessionObject for variable "logData" and if not, initialize the array.

lines 23 - 25 if useSession is true, set the array logData into the session object.

private function _crud


/**
 * private function
 * log various activities if detail
 * perfrom CRUD
 * always start/commit transaction
 */
function _crud(request, detail, useSession) {
    //log start connection
    if (detail) {
        setLog({ state: 'start connection', request: request}, useSession);
    }
    cObj = ssj.getConnection(request.connection);
    
    if (detail) {
        setLog({ state: 'end connection', request: request}, useSession);
    }
    
    var error = false;
    var rtn = {};
    try {
      
        //log begin transaction
        if (detail) {
            setLog({ state: 'start transaction', request: request}, useSession);
        }
        cObj.beginTransaction();    
   
        if (request.action === 'insert') {
            cObj.insert(request.table, request.data);    
            rtn = cObj.lastInsertId();
        } else if (request.action === 'update') {
            rtn = cObj.update(request.table, 
                request.data, 
                cObj.quoteInto('userId=?', request.data.userId));
        } else if (request.action === 'delete') {
            rtn = cObj.delete(request.table, 
                cObj.quoteInto('userId=?', request.data.userId));
        } else if (request.action === 'select') {
            var selectObj = cObj.select().from(request.table).where('userId=?', request.data.userId); 
            rtn =cObj.exec(selectObj);
        } else if (request.action === 'deleteAll') {
            cObj.delete(request.table);
        }
    }
    catch(e) {
        //log error
        cObj.rollback();
        error = true;
        throw e;
    } finally {
        if (!error) {
            cObj.commit();
        }
        //log end transaction
        if (detail) {
            setLog({ state: 'end transaction',  request: request}, useSession);
        }
        return rtn;
    }
}
First off, according to AC documentation, by starting a function in SSJS with the _ ("underscore"), the function is private. Now I don't know what that's important at this time 'cause I don't know how to access any SSJS functions outside of the AC client. But there is this interesting bit of documentation that gives a flavor of things to come with SSJS functions: Exposing your SSJS DB Handlers to other systems

There are numerous sections of this code that has to do with logging or not the request. As I was working through this and trying to understand why the performance was so bad over time I attempted to whittle away the things that might be affecting it. So this function _crud has numerous if statements to decide if logging should be done.

On line 12 the connection to the database is retrieved.  Notice how I surrounded that with logging to check how fast this was performed.

Note the  try/catch/finally statement starting at line 20.  This is because at line 26 I start a transaction.  For this exercise I wanted all my CRUD functionality to be all in one function an resuse the getConnection, beginTransaction, commit and rollback.  I will probably revisit this design latter and make it more object oriented.

By having the try/catch/finally pattern I am able to catch any exception and either rollback or commit if there is no exception.  The catch clause begins on line 45 and sets a variable if there is an exception.  That variable "error" is check in the finally clause to make sure the commit is not called.  Notice line 58 the return of the variable rtn.

The Read (select), Update and Delete SSJS functions all return a value.  In the case of the select, it's an array of rows of data that have been retrieved.  The update returns the number of updates.  The delete returns the number of deletions.

But the Create (insert) doesn't return a value but when using "auto increment" you have to ask the db what the last id was.  That occurs on line 30.

So between lines 28 and 42 the CRUD operations are performed against the "AppUser" table.  I think there is room for improvement with better javascript design but it was sufficient for me for the purpose of learning about AC persistence.

The "assert" function

/**
 * simple assertion function that logs if fails
 */
function assert(outcome, description, obj, useSession) {
    if (!outcome) {
        debugger;
        setLog({state: 'assert', description: description, data: obj},useSession );
    } 
}
The assert function is simply used to log any failures along with a description and the oject that failed.  The useSession variable is passed in so that the logging function does the right thing.

The "startTesting" function

This function is the SSJS driver code.  It basically clears out the current contents in the table being tested, and then performs 4 different actions, namely the Create, Read, Update and Deletes.  At the completion of each CRUD operation, an assertion is performed to make sure things are running as expected.
/** 
 * Run CRUD for numberOfTest using Connection
 * return logs
 * 
 * obj: {connection: 'connection', state: 'action', table: 'table', data: {stuff}}
 * action: insert/update/delete/select/assert/deleteAll
 */
function startTesting(numberOfTests,connection, detail, useSession) {
    debugger;
    //Clear the session log
    clearLog();
    
    //first clear the table
    var tableCleared = false;
    
    for (var test = 0; test < numberOfTests; test++) {
        var obj = {};
        obj.connection = connection;
        obj.table = 'AppUser';
        
        //Clear the database table
        if (!tableCleared) {
            obj.action = 'deleteAll';
            performCrud(obj, detail, useSession);
            tableCleared = true;
        }
        //Insert an object
        obj.action = 'insert';
        obj.data = {name: 'test' + test, source: 'auto', id: test};
        var userId = performCrud(obj,detail, useSession);
        assert(userId !== null, 'insert failed',obj, useSession);
        
        //Update that same row
        obj.action = 'update';
        obj.data.userId = parseInt(userId);
        obj.data.name = obj.data.name + 'changed';
        var numUpdates = performCrud(obj,detail, useSession);
        assert( 1 === numUpdates,'update failed', obj, useSession);
        
        //Select from that row
        obj.action = 'select';
        var sel = performCrud(obj,detail, useSession);
      
        assert(1 === sel.length, 'select failed - array length not 1', obj, useSession);
        assert(sel[0].userId === obj.data.userId, 'select failed - userId not equal',sel[0], useSession);
        assert(sel[0].name === obj.data.name, 'select failed - name not equal',sel[0], useSession);
        assert(sel[0].source === obj.data.source, 'select failed - source not equal',sel[0], useSession);
        assert(sel[0].id === obj.data.id, 'select failed - id not equal',sel[0], useSession);
        
        //Delete that row
        obj.action = 'delete';
        assert( 1 === performCrud(obj,detail, useSession)); 
    }
    //Get all the logs 
    return getLog(useSession);
}
I think this code is rather straight forward.
The test looping starts on line 16. I use an object called "obj" to contain the control information. On line 18, the connection variable is set. This is the value from the client selection of either "AC Database" or "Xeround". Line 19 sets what the table value is. Line 22 - 26 runs the action "deleteAll" to clear the database. It only runs when the test starts.

 Lines 27 - 31 sets up the insert. Note that the "id" is using the increment value "test". The return value from the "performCrud" method is the userId. Since that's the primary key that the other CRUD operations require, it's set into the obj.data object. Notice how the assert confirms that the userId is not null.

 Lines 33 - 38 sets up for the "update". The name value is modified. After running, the return value is asserted to be 1.

 Lines 41 -49 perform a Read (select). The return array is asserted to match all the fields against the initial object that was updated.

 Lines 50 - 52 simply delete the object and assert only one row was effected.

 Line 55 is outside of the "for loop" and returns the logs from the test.

The "performCrud" function

/**
 * perform the request 
 * and log
 */
function performCrud(request, detail, useSession) {

    //log begin insert
    setLog({ state: 'start', request: request}, useSession);

    var rtn = _crud(request,detail, useSession);
   
    //log end inser
    setLog({state: 'end',request: request}, useSession);
  
    return rtn;
 
}

This function just wraps the call to the private function _crud with logging.  Essentially this was all that was necessary for the charting that shows the results.

Now back to the client!

Now that the test has completed the code handling the  handler_startTestBtn_onClick function passes the returned data to the function fillZingerChart.  This function runs through the data and finds the "start/end" states and calculates the duration and adds to the overall total.

Then for each request.action it pushes the duration into an array depending upon if it's create, read, update or delete.


function fillZingerChart(data) {  
    var createArray = [];
    var readArray =[];
    var updateArray = [];
    var deleteArray = []; 
    
    var start = 0;
    var end = 0;
    var total = 0;
    for (var log = 0; log < data.length; log++) {
        var obj = data[log];
        //Ignore the cleaning
        if (obj.request.action === 'deleteAll') {
            continue;
        }

        if (obj.state === 'start') {
            start = obj.time;
        } else if (obj.state === 'end') {
            end = obj.time;
            var duration = end - start;
            total += duration;
            if (obj.request.action === 'insert') {
                createArray.push(duration);
            } else if (obj.request.action === 'update') {
                updateArray.push(duration);
            } else if (obj.request.action === 'select') {
                readArray.push(duration);
            } else if (obj.request.action === 'delete') {
                deleteArray.push(duration);
            }
        }
        
    }
    
    var db = app.getValue("databaseRadio").label;
    var jsonData = getZinger(db + " " + total,createArray, readArray, updateArray, deleteArray);
    
    console.log(jsonData);
    
    if (db === 'AC Database') {
        app.w('ZingchartAC').fullSettings(jsonData);
    } else {
        app.w('ZingchartXE').fullSettings(jsonData);
    }
}
On line 37 the database label from the selected radio group has the total time appended and the four arrays are passed into a simple function that sets all the options up for the ZingChart.

If you want to see the function "getZinger" get the source code from gitHub and take a look - it's just a lot of JSON that controls the Zing chart.

 You may be asking how was that JSON created? How did I build those two charts? Well the secret is to use the ZingChart Builder.  It's quite impressive and rather intuitive once you start playing around with it.

Notice that once the JSON data is retreived, on lines 42 and 44, the appropriate chart is set using the "fullSettings" API.

Summary

I think using the AC Database option is fine.  I'd like to understand better what the limitations are - like how many Mbs are allowed for the cost of the Pro account.

As to the CRUD persistence framework?  It works quite nice and I really like the way the "where" "from" etc is structured.  AC, as usual, as made this quite easy.

My one concern is that SessionObject.  Hopefully AC will address this.



Application Craft - Easy Peasy Task List Project


Building an Easy Peasy TaskList with ApplicationCraft

Background

I've been working with ApplicationCraft.com for some time now though for the last few months I've not had that luxury.  I've always been impressed how great the product is and the stability and robustness is awesome.  Here's a link to a previous blog where I spelled out what makes AC exciting to me: A Word About ApplicationCraft.

In order to get my AC skill sets up to speed I thought I'd build a Task List application using ApplicationCraft.  Since everything is so easy with AC, I thought I'd call this the Easy Peasy Task List. 

I will write blogs about each of the learning activities I undertake.  I don't know about you, but my blogs provide valuable information for myself (thus the name "Notes to Myself") for future reference.  I frequently look back at my blogs to see how I did a certain thing as I can't remember everything.  Hopefully not only does it benefit me but you also.  If so, drop me a comment and let me know.

Features

I want to build this Easy Peasy Task List with the following features ( in no particular order)
  • Task list management.  The user should be able to 
    • Create and maintain various task lists
    • Use calendar widget for date selection
    • Calendar summary view 
    • Add due dates and notifications (by date or location).  
    • History of all tasks
    • Completed tasks are archived and available for resurrection.
  • Run from everything with single code base AC provides a great way to build both the web application and mobile/smartphone application with a single code base.  They've introduced a new AC Mobile Build that I want to utilize.  I only care about iOS and Android and web app.  I need to refresh my skills in the adaptive layout  
  • App available in store.  I want to have the app available for both iOS and Android and accepted in the appropriate app store.  This means I need to learn about the certificates and such.
  • Use oAuth for authentication.  I don't want to manage user ids and lost passwords and such.  I just care that someone identifies themselves via some Social Network and they use that same authentication each time they use EasyPeasy TaskList.  At this time Facebook, Twitter and DropBox are supported.  I would like to see support for JanRain Engage Social Login where by I would be able to open up the oAuth to a lot more then just three Social Networks.  I submitted this request to the dev portal.
  • Database support - AC has come out with a better persistence story.  I want to use the new SQL features to track who logged in, their tasks, etc.
  • Off line support.  The app should be functional even w/ no internet connectivity.  AC supports SQLite database.  Once the app is on line, the SQLite database should sync up with the back end MySql db.
  • Share list.  Allow user to email someone a request to collaborate.  Permissions supported should be read/write or read only.
  • Reminders and Notifications - When tasks are entered, the EPTL user can keep track by stating due dates or locations and setting notifications
  • Testing - I really want to have unit tests and integration tests.  The integration tests should include the web app and mobile.
  • Advertising - After all this work, I need to consider how to make a buck or two.  AC has a Advertising widget available which I know nothing about.
  • Logging support - AC has built in logging but I find it rather difficult and insufficient for my use.  (See AC: Server Side Javascript and Logging.  I want logging from the client to be available online for monitoring.  If a user says they are having problems, I should be able to turn on a debug flag and watch the activity.
  • Release management - Need to address how users app is updated.  What happens if there is a database migration required for the new release? How are users kept off line while the updates are performed.

List of steps I've written about so far:

ApplicationCraft - Easy oAuth

ApplicationCraft - Easy CRUD

ApplicationCraft - Easy Testing

Status of discovery / learning exercises

The table below has the various learning exercises I need to build EasyPeasy TaskList.  Links to the various blogs I write with my analysis are available from the Task / Blog column.


Task /
Blog
Description Status Date
oAuth Web app and native app should both use oAuth In Progress
AC states here that the AC Mobile Build requires a ChildPlugin to support oAuth in mobile.
Nov 28, 2012
Database
CRUD
AC provides their own hosted MySql database.  The AC framework has new SSJS db features to perform CRUD. Learn how to use SSJS db. Simple solution needs to be refactored to be more OO Dec 1, 2012
Testing Need unit tests and integration tests. Basic requirements met  Dec 7, 2012
Notification  When user wants to be notified of a due date the server can push the notification.  AC offers a plugin using Xtify Push. Xtify works with native mobile apps.  I want to notify browser apps to so will use Pusher for that. Have a working solution for the basics. Dec 12, 2012
Use oAuth with JanRain JanRain Engage would open up the oAuth capabilities to 20+ Social Networks Submitted request to AC support.  Not sure how it's being tracked at this time.   Nov 26, 2012
Learn requirements
for app store
What is required by AC Mobile Build to qualify app to store not started
Preparation for app store Utilizing the AC Mobile Build to create an iOS and Android app that is ready for an app store will have certain requirements. not started
Offline / sync support How does the native app work with no connectivity? How does syncing work once it comes back on line? not started
Collaborate 1). Need ability to email invitation to collaborate.
2) What is impact to DB schema
3) How to resolve conflicts of concurrent task modification
not started
Location Notification Useing GPS, determine if any notifications are dependant upon location proximity not started
Calendar Need to learn how to select due dates from calendar widget and how to display summary calendar views (day, week, month, year) not started
Advertising How can I make some money? not started
Logging support Need ability to turn on/off client logging
Need ability to monitor logging
not started
Release management Ability to block access while maintenance is performed
Ability to require update of new release
Entered Release management of web / native app  Nov 28
2012
Stuff

Wednesday, November 28, 2012

ApplicationCraft - Easy oAuth

Overview

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

ApplicationCraft provides an easy way to authenticate users via 3 social networks: Twitter, Facebook and Dropbox.  The code required to authenticate is quite easy and is documented as shown below.  The oAuth overview docs are located here: http://www.applicationcraft.com/developers/documentation/product-guide/advanced-features/oauth/

Web app only for now

At this time (November 28, 2012) the native app doesn't work.  There is a good thread about oAuth in the forum:  https://getsatisfaction.com/application_craft/topics/oauth_has_arrived and CTO Joe Moss updated it yesterday that a ChildBrowser plugin will be required.  So until that is done, this demo only works as a web app.

Video

I've created a short video that shows how this demo is coded and what properties are setup.


Code

The exported project is available from my github account: https://github.com/bartonhammond/TaskList.  Just clone the repository or download it as a zip.  Then within ApplicationCraft, import the "app_Step%2B1%2B-%2BOauth_r55.acft".  You will have to follow the steps below to obtain the key/secret data values from each of the social networks and enter their values in the application properties (see below).

Web app

You can kick the tires here:  http://acft.ws/nftc.  

Steps to getting the social networks key/secrets.

The first step required is to get the key/secret data from the social network.  The following links should suffice:




Now let's look at the code from this demo

Let's follow what happens when the dropbox image is clicked.  The logic is similar to facebook and twitter.
/**
 * Handle twitter
 */
function handler_twitterImg_onClick(mouseev){
    window.oauth='twitter';
    doOauthSignIn();
}

When the twitter image is clicked we call this function.  The first thing is to set a global variable I've called "window.oauth".  This is made global so that it can be accessed later within the callback function.  I needed a way to determine which social network was been accessed.  All three image handlers set the window.oauth variable appropriately and call doOauthSignIn().
/**
 * Generic oauthSignIn
 */
function doOauthSignIn() {
      app.oAuthSignIn(window.oauth, authCallback); 
}
This doOauthSignIn does only one thing - it calls the app.oAuthSignIn passing the window.oauth variable and the function name to be called when the oAuth process completes. The docs are here: http://www.applicationcraft.com/developers/documentation/product-guide/advanced-features/oauth/authentication2/

The function authCallback is shown below.
/**
 * Callback from SSJ when oAuth is completed
 * 
 * If result is good, make a request to the social network
 * for identifying information for the user
 */
function authCallback(result, userAuthKey) {
    if (result) {        
        app.callSSJ('makeOAuthRequest', function(error,data){
            setFields(data);
        }, [userAuthKey,window.oauth]); 
    }
    else {
        alert("failed oauth");
    }
}
The function authCallback is called when the social network completes the authentication of the user. The parameters provided to this function are defined in the ApplicationCraft.com documents http://www.applicationcraft.com/developers/documentation/product-guide/advanced-features/oauth/authentication2/

Note that the object returned in the function defined on line 9 "data" will be the data returned by the SSJS function "makeOAuthRequest".  If you look below you'll see that the SSJS function builds that data object and handles the discrepancy of the returned data that the three social networks return.  This makes the code on the client side easy as it doesn't have to multiple callbacks, one for each social network.  Instead the SSJS function handles that abstraction by returning a normalized object.

If the result of the social network oAuth process is successful, we make a call to a server side javascript (SSJS) function called "makeOAuthRequest".  Note that we are passing two parameters to this function: userAuthKey and window.oauth.

The SSJS function makeOAuthRequest is shown below:
/**
 * param: userAuthKey - what was provided during the oAuth
 *        source - what social network is being accessed.
 * 
 * Make the oAuthRequest and return the data.
 * 
 * The three sources are facebook, twitter and dropbox.
 * 
 * The fields object below has the request for each source and what the data attribute 
 * name is that is returned.
 */
function makeOAuthRequest (userAuthKey, source) {
  
    var fields = {'facebook' : {request: 'https://graph.facebook.com/me', id: 'id', name: 'name'},
                 'twitter'  : {request: 'https://api.twitter.com/1.1/account/verify_credentials.json', id: 'id', name: 'name'},
                 'dropbox'  : {request: 'https://api.dropbox.com/1/account/info', id:'uid', name:'display_name'}};
       
    var data =  ssj.oAuthRequest(userAuthKey,
            fields[source].request, 
            'GET',{});    
           
    return {id: data[fields[source].id], name: data[fields[source].name], source: source};
   
}
This function has some objects that need explanation.

lines 14-16

This is an associative array indexed by the social network source.  There are three array entries which are indexed by facebook, twitter and dropbox.  Remember that the client is calling this SSJS function passing in the global variable window.oauth.  If you index this array by the source variable you return a object.  The object has three fields: request, id and name:

  • request field is the URL that is used in the oAuthRequest to return information about the currently logged in user
  •  id: this is the value in the returned data (line 18) that contains the value I'd like to track, namely the id within the social network.
  • name: this is the value in the returned data (line 18) that contains the value of the logged in persons name.  For facebook and twitter, the value is "name".  But for dropbox the value is "display_name".

lines 18 - 20

This single call, by utilizing the fields array, supports accessing all three social networks.  On line 19 the request value for the selected source is used.  To read this line 19, index into the fields array using the "source" value and then access the value of the request attribute.  So if the source is "facebook" the request will be 'https://graph.facebook.com/me'

line 22

This line returns an object with three attributes: id, name and source.  The id value is taken from the social networks returned data.  By indexing into the fields array, again, with the source, the value within the data object is retrieved by referencing the field with the value of the id.  For twitter and facebook, that value is "id".  But for dropbox, data.uid is what is retrieved.  Likewise, the name is retrieved by referencing the value of the "name".  For twitter and facebook, it's "name" while dropbox the attribute is "display_name".

Summary

I think ApplicationCraft.com has done a great job in making oAuth easy for these three social networks.  Hopefully the code presented here will help you learn how to incorporate oAuth into your application.

Wednesday, September 12, 2012

Vertx, SockJS, Heroku and AC

Vertx, SockJS, Heroku and Application Craft

There's an exciting application development tool that caught my attention: http://vertx.io/.  It's pretty impressive, as it states on it's home page it's an "Effortless asynchronous application development for the modern web and enterprise".

What I found interesting is that it's a JVM based solution and supports multiple languages, namely Java, Groovy, Javascript, Python, and Ruby.  It uses a different threading model then typical JEE does - your app runs in it's own thread and only one thread.  So you can program as if your single threaded.  Concurrency is not an issue.   Refer to the web site for more information.

For purposes of this demo, I wanted to demonstrate one aspect that Vertx provides which is SockJS.  SockJS is a browser Javascript library that provides a WebSocket-like object.

Vertx provides a SockJS server implementation.  So it's a breeze to intercommunicate between browsers and mobile apps!   I think a demo would help see how powerful this is.  Watch this

The following code is all that my vertx server contains:

load('vertx.js');

var server = vertx.createHttpServer();

// Create a SockJS bridge which lets everything through
vertx.createSockJSServer(server).bridge({prefix: "/eventbus"}, [{}], [{}]);

//Start the server, list to the port defined in the environment and 
//accept requests from any host.
//When running locally, set the PORT to say 8080
//It's purpose has more to with running on Heroku
server.listen(vertx.env['PORT'],'0.0.0.0');

The client code is pretty simple too:
 
 var eb = null;
  var firstMessage = true;
  var firstSubscribe = true;
  
  /**
   * Publish a message to a topic
   */
  function publish(address,  message) {
    if (eb) {
      var json = {text: message};
      eb.publish(address, json);
    }
  }
    /**
     * Subscribe to a topic
     */
  function subscribe(address) {
    if (eb) {
        eb.registerHandler(address, function(msg,replyTo) {
            app.setValue('receivedTxtArea',app.getValue('receivedTxtArea') + (!firstMessage ? '' : '\n') + msg.text);
            firstMessage = false;
        });
        app.setValue('subscribedTxtArea',app.getValue('subscribedTxtArea') + (firstSubscribe ? '' : '\n') + address);
        firstSubscribe = false;
    }
  }
    /**
     * Close connection
     */
  function closeConn() {
    if (eb) {
      eb.close();
    }
  }
  /** 
   * Open a connection
   * Note it's using heroku
   */
  function openConn() {
    if (!eb) {
        eb = new vertx.EventBus("http://fast-mountain-7331.herokuapp.com/eventbus");
        eb.onopen = function() {
        app.setValue('connectStatus',"Connected");
      };

      eb.onclose = function() {
        app.setValue('connectStatus',"Not Connected");
        eb = null;
      };
    }
  }


You can access the AC app and associated code for Vertx from GitHub: https://github.com/bartonhammond/vertx-ac

To get Vertx running on Heroku follow this guide http://fbflex.wordpress.com/2012/05/02/running-vert-x-applications-on-heroku/ but with the following change:

Use this command instead as it supports having a "server.js" rather then a "server.groovy".  Also, you need to have a Procfile as you see within my github.

heroku create --stack cedar --buildpack https://github.com/bartonhammond/heroku-buildpack-vertx-jdk7.git
The best way to see the changes is upload the VertxAC.zip from my GitHub account and import it into AC. One note of interest is shown below - include the SockJS client files: