You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/01 18:52:13 UTC

[29/50] [abbrv] brooklyn-ui git commit: fetches and gets are now refactored to use common viewutils code which handles errors, backing off the requests (so we'll see fewer messages in our debug logs) and more importantly disabling the relevant portions o

fetches and gets are now refactored to use common viewutils code which handles errors,
backing off the requests (so we'll see fewer messages in our debug logs) and more importantly
disabling the relevant portions of the screen so user knows the data is stale / unavailable


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/d13dc996
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/d13dc996
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/d13dc996

Branch: refs/heads/0.6.0
Commit: d13dc996dab6979d9590c5d35b1a8e29b73697e9
Parents: 869239d
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Sep 18 03:26:53 2013 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed Sep 18 09:30:07 2013 +0100

----------------------------------------------------------------------
 .../webapp/assets/js/view/activity-details.js   |  52 +++---
 .../assets/js/view/application-explorer.js      |  24 +--
 .../webapp/assets/js/view/application-tree.js   |   8 +-
 .../webapp/assets/js/view/entity-activities.js  |   8 +-
 .../main/webapp/assets/js/view/entity-config.js |  58 +++----
 .../webapp/assets/js/view/entity-details.js     |   9 +-
 .../webapp/assets/js/view/entity-effectors.js   |   3 +
 .../webapp/assets/js/view/entity-policies.js    |  26 ++-
 .../webapp/assets/js/view/entity-sensors.js     |  64 ++++---
 .../webapp/assets/js/view/entity-summary.js     |  51 ++++--
 .../src/main/webapp/assets/js/view/home.js      |  29 ++--
 .../src/main/webapp/assets/js/view/viewutils.js | 172 +++++++++++++++++++
 12 files changed, 342 insertions(+), 162 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js b/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js
index 1466bca..051aa74 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/activity-details.js
@@ -57,6 +57,7 @@ define([
         },
         // requires taskLink or task; breadcrumbs is optional
         initialize:function () {
+            var that = this
             this.taskLink = this.options.taskLink
             if (this.options.task) {
                 this.task = this.options.task
@@ -73,32 +74,31 @@ define([
             ViewUtils.attachToggler(this.$el)
         
             if (this.task) {
-                this.renderTask(true)
+                this.renderTask()
+                this.setUpPolling()
             } else {                
                 this.$el.css('cursor', 'wait')
-                this.refreshNow(true)
+                $.get(this.taskLink, function(data) {
+                    that.task = new TaskSummary.Model(data)
+                    that.renderTask()
+                    that.setUpPolling();
+                })
             }
-            this.renderSubtasks()
-        
-            this.callPeriodically("refresh-activities-now", function () {
-                this.refreshNow()
-            }, 1000);
 
-            if (this.collection) {
-                this.collection.on("reset", this.renderSubtasks, this);
-                // could lean on parent's poll for the task itself, but instead we poll (and more often)
-//                this.collection.on("reset", this.renderTask, this);
-            }
+            // initial subtasks may be available from parent, so try to render those
+            // (reliable polling for subtasks, and for children, is set up in setUpPolling ) 
+            this.renderSubtasks()
         },
         
         refreshNow: function(initial) {
             var that = this
             $.get(this.taskLink, function(data) {
                 that.task = new TaskSummary.Model(data)
-                that.renderTask(initial)
+                that.renderTask()
+                if (initial) that.setUpPolling();
             })
         },
-        renderTask: function(initial) {
+        renderTask: function() {
             // update task fields
             var that = this
             
@@ -149,19 +149,23 @@ define([
 
             if (this.task.get("children").length==0)
                 $('.toggler-region.tasks-children', this.$el).hide();
-            
-            if (initial) {
+        },
+        setUpPolling: function() {
+                var that = this
+                
                 // on first load, clear any funny cursor
                 this.$el.css('cursor', 'auto')
                 
+                this.task.url = this.taskLink;
+                this.task.on("all", this.renderTask, this)
+                ViewUtils.fetchRepeatedlyWithDelay(this, this.task, { doitnow: true });
+                
                 // and set up to load children (now that the task is guaranteed to be loaded)
                 this.children = new TaskSummary.Collection()
                 this.children.url = this.task.get("links").children
                 this.children.on("reset", this.renderChildren, this)
-                this.callPeriodically("refresh-activity-children", function () {
-                    that.children.fetch({reset: true});
-                }, 3000);
-                that.children.fetch({reset: true});
+                ViewUtils.fetchRepeatedlyWithDelay(this, this.children, { 
+                    fetchOptions: { reset: true }, doitnow: true, fadeTarget: $('.tasks-children') });
                 
                 $.get(this.task.get("links").entity, function(entity) {
                     if (that.collection==null || entity.links.activities != that.collection.url) {
@@ -169,14 +173,12 @@ define([
                         that.collection = new TaskSummary.Collection()
                         that.collection.url = entity.links.activities
                         that.collection.on("reset", this.renderSubtasks, this)
-                        that.callPeriodically("refresh-activity-bgtasks", function () {
-                            that.collection.fetch({reset: true});
-                        }, 3000);
-                        that.collection.fetch({reset: true});
+                        ViewUtils.fetchRepeatedlyWithDelay(that, that.collection, { 
+                            fetchOptions: { reset: true }, doitnow: true, fadeTarget: $('.tasks-submitted') });
                     }
                 });
-            }
         },
+        
         renderChildren: function() {
             var that = this
             var children = this.children

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
index 4cd2008..0ac69fa 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-explorer.js
@@ -4,10 +4,10 @@
  * @type {*}
  */
 define([
-    "underscore", "jquery", "backbone", 
+    "underscore", "jquery", "backbone", "view/viewutils", 
     "./application-add-wizard", "model/app-tree", "./application-tree", 
     "text!tpl/apps/page.html"
-], function (_, $, Backbone, AppAddWizard, AppTree, ApplicationTreeView, PageHtml) {
+], function (_, $, Backbone, ViewUtils, AppAddWizard, AppTree, ApplicationTreeView, PageHtml) {
 
     var ApplicationExplorerView = Backbone.View.extend({
         tagName:"div",
@@ -25,31 +25,17 @@ define([
             $(".nav1").removeClass("active");
             $(".nav1_apps").addClass("active");
 
-            this.collection.on('reset', this.render, this)
             this.treeView = new ApplicationTreeView({
                 collection:this.collection
             })
             this.$('div#app-tree').html(this.treeView.renderFull().el)
-            this.refreshApplications();
-            that.callPeriodically("entity-tree-apps", 
-                    function() { that.refreshApplicationsInPlace() }, 3000)
+            this.collection.fetch({reset: true})
+            ViewUtils.fetchRepeatedlyWithDelay(this, this.collection)
         },
         beforeClose:function () {
             this.collection.off("reset", this.render)
             this.treeView.close()
         },
-        render:function () {
-            return this
-        },
-        
-        refreshApplications:function () {
-            this.collection.fetch({reset: true})
-            return false
-        },
-        refreshApplicationsInPlace:function () {
-            this.collection.fetch()
-            return false
-        },
         show: function(entityId) {
             this.treeView.displayEntityId(entityId)
         },
@@ -64,7 +50,7 @@ define([
             }
             var wizard = new AppAddWizard({
             	appRouter:that.options.appRouter,
-            	callback:function() { that.refreshApplicationsInPlace() }
+            	callback:function() { that.collection.fetch() }
         	})
             this._modal = wizard
             this.$(".add-app #modal-container").html(wizard.render().el)

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
index db1c679..57999f4 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js
@@ -47,12 +47,18 @@ define([
             this.removeNode(child.id)
         },
         modelEvent: function (eventName, event, x) {
-            if (eventName == "change" || eventName == "remove" || eventName == "add" ||
+            if (/^change/i.test(eventName) || eventName == "remove" || eventName == "add" ||
                     eventName == "reset" ||
                     // above are handled; below is no-op
                     eventName == "sync" || eventName == "request")
                 return;
 
+            if (eventName == "error") {
+                log("model error in application-tree - has the internet vanished?")
+                // ignore; app-explorer should clear the view
+                return;
+            }
+            
             // don't think we get other events, but just in case:
             log("unhandled model event")
             log(eventName)

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-activities.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-activities.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-activities.js
index ccb9e52..7106b3c 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-activities.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-activities.js
@@ -53,11 +53,9 @@ define([
             
             ViewUtils.fadeToIndicateInitialLoad($table);
             that.collection.on("reset", that.renderOnLoad, that);
-            that.callPeriodically("entity-activities", function () {
-                if (that.refreshActive)
-                    that.collection.fetch({reset: true});
-            }, 3000);
-            that.collection.fetch({reset: true});
+            ViewUtils.fetchRepeatedlyWithDelay(this, this.collection, 
+                    { fetchOptions: { reset: true }, doitnow: true, 
+                    enablement: function() { return that.refreshActive }  });
         },
         refreshNow: function() {
             this.collection.fetch({reset: true});

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
index 1cadaa1..7ec7399 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
@@ -93,12 +93,31 @@ define([
         refreshNow:function () {
             this.updateConfigNow(this);  
         },
+        updateConfigNow:function (that) {
+            ViewUtils.get(that, that.model.getConfigUpdateUrl(), function(data) { that.updateWithData(that, data) },
+                    { enablement: function() { return that.refreshActive } });
+        },
         updateConfigPeriodically:function (that) {
-            var self = this;
-            that.callPeriodically("entity-config", function() {
-                if (self.refreshActive)
-                    self.updateConfigNow(that);
-            }, 3000);
+            ViewUtils.getRepeatedlyWithDelay(that, that.model.getConfigUpdateUrl(), function(data) { that.updateWithData(that, data) },
+                    { enablement: function() { return that.refreshActive } });
+        },
+        updateWithData: function (that, data) {
+            $table = that.$('#config-table');
+            ViewUtils.updateMyDataTable($table, data, function(value, name) {
+                var metadata = that.configMetadata[name]
+                if (metadata==null) {                        
+                    // TODO should reload metadata when this happens (new sensor for which no metadata known)
+                    // (currently if we have dynamic sensors, their metadata won't appear
+                    // until the page is refreshed; don't think that's a big problem -- mainly tooltips
+                    // for now, we just return the partial value
+                    return [name, {'name':name}, "", value]
+                } 
+                return [name, metadata,
+                    metadata["actionGetData"],
+                    value
+                ];
+            });
+            ViewUtils.processTooltips($table)
         },
         loadConfigMetadata: function(that) {
             var url =  that.model.getLinkByName('config');
@@ -114,32 +133,11 @@ define([
                 }
                 that.updateConfigNow(that);
                 that.table.find('*[rel="tooltip"]').tooltip();
-            });
+            }).fail(that.onConfigMetadataFailure);
         },
-        updateConfigNow:function (that) {
-            var url = that.model.getConfigUpdateUrl(),
-                $table = that.$('#config-table');
-            if (that.viewIsClosed) {
-                return
-            }
-            $.get(url, function (data) {
-                if (that.viewIsClosed) return
-                ViewUtils.updateMyDataTable($table, data, function(value, name) {
-                    var metadata = that.configMetadata[name]
-                    if (metadata==null) {                        
-                        // TODO should reload metadata when this happens (new sensor for which no metadata known)
-                        // (currently if we have dynamic sensors, their metadata won't appear
-                        // until the page is refreshed; don't think that's a bit problem -- mainly tooltips
-                        // for now, we just return the partial value
-                        return [name, {'name':name}, "", value]
-                    } 
-                    return [name, metadata,
-                        metadata["actionGetData"],
-                        value
-                    ];
-                });
-                ViewUtils.processTooltips($table)
-            });
+        onConfigMetadataFailure: function() {
+            log("unable to load config metadata")
+            ViewUtils.fadeToIndicateInitialLoad()
         }
     });
     return EntityConfigView;

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js
index 0028b40..2e139a8 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-details.js
@@ -15,10 +15,6 @@ define([
         },
         initialize:function () {
             this.$el.html(this.template({}))
-            this.summaryView = new SummaryView({
-                model:this.model,
-                application:this.options.application
-            })
             this.configView = new ConfigView({
                 model:this.model
             })
@@ -35,6 +31,11 @@ define([
                 model:this.model,
                 collection:new TaskSummary.Collection
             })
+            this.summaryView = new SummaryView({
+                model:this.model,
+                application:this.options.application,
+                sensors:this.sensorsView.model
+            })
             this.$("#summary").html(this.summaryView.render().el)
             this.$("#config").html(this.configView.render().el)
             this.$("#sensors").html(this.sensorsView.render().el)

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-effectors.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-effectors.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-effectors.js
index b5b8569..8a68c29 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-effectors.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-effectors.js
@@ -30,6 +30,9 @@ define([
                 that.render()
                 ViewUtils.cancelFadeOnceLoaded(that.$('#effectors-table'));
             }})
+            // attach a fetch simply to fade this tab when not available
+            // (the table is statically rendered)
+            ViewUtils.fetchRepeatedlyWithDelay(this, this._effectors, { period: 10*1000 })
         },
         render:function () {
             if (this.viewIsClosed)

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-policies.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-policies.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-policies.js
index 2e64075..c09c395 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-policies.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-policies.js
@@ -25,26 +25,19 @@ define([
         initialize:function () {
             this.$el.html(this.template({ }));
             var that = this;
-            // fetch the list of policies and create a view for each one
+            // fetch the list of policies and create a row for each one
             that._policies = new PolicySummary.Collection();
             that._policies.url = that.model.getLinkByName("policies");
             
             this.loadedData = false;
             ViewUtils.fadeToIndicateInitialLoad(this.$('#policies-table'));
-            that.callPeriodically("entity-policies", function() {
-                that.refresh();
-            }, 3000);
-            that.refresh();
-        },
-
-        refresh:function() {
-            var that = this;
             that.render();
-            that._policies.fetch({ success:function () {
-                that.loadedData = true;
-                that.render();
-                ViewUtils.cancelFadeOnceLoaded(that.$('#policies-table'));
-            }});
+            this._policies.on("all", this.render, this)
+            ViewUtils.fetchRepeatedlyWithDelay(this, this._policies,
+                    { doitnow: true, success: function() {
+                        that.loadedData = true;
+                        ViewUtils.cancelFadeOnceLoaded(that.$('#policies-table'));
+                    }})
         },
 
         render:function () {
@@ -107,8 +100,11 @@ define([
                 // fetch the list of policy config entries
                 that._config = new PolicyConfigSummary.Collection();
                 that._config.url = policy.getLinkByName("config");
+                ViewUtils.fadeToIndicateInitialLoad($('#policy-config-table'))
+                that.showPolicyConfig(id);
                 that._config.fetch({ success:function () {
                     that.showPolicyConfig(id);
+                    ViewUtils.cancelFadeOnceLoaded($('#policy-config-table'))
                 }});
             }
         },
@@ -195,7 +191,7 @@ define([
                 type:"POST",
                 url:url,
                 success:function() {
-                    that.refresh();
+                    that._policies.fetch();
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-sensors.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-sensors.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-sensors.js
index ea24745..c564dff 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-sensors.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-sensors.js
@@ -71,8 +71,8 @@ define([
             ViewUtils.addAutoRefreshButton(this.table);
             ViewUtils.addRefreshButton(this.table);
             this.loadSensorMetadata()
-                .updateSensorsPeriodically()
-                .toggleFilterEmpty();
+            this.updateSensorsPeriodically()
+            this.toggleFilterEmpty();
             return this;
         },
 
@@ -104,13 +104,33 @@ define([
             this.refreshActive = isEnabled
             return this;
         },
-
-        updateSensorsPeriodically: function() {
-            this.callPeriodically("entity-sensors", function() {
-                if (this.refreshActive)
-                    this.updateSensorsNow();
-            }, 3000);
-            return this;
+        
+        /**
+         * Loads current values for all sensors on an entity and updates sensors table.
+         */
+        updateSensorsNow:function () {
+            var that = this
+            ViewUtils.get(that, that.model.getSensorUpdateUrl(), function(data) { that.updateWithData(that, data) },
+                    { enablement: function() { return that.refreshActive } });
+        },
+        updateSensorsPeriodically:function () {
+            var that = this
+            ViewUtils.getRepeatedlyWithDelay(that, that.model.getSensorUpdateUrl(), function(data) { that.updateWithData(that, data) },
+                    { enablement: function() { return that.refreshActive } });
+        },
+        updateWithData: function (that, data) {
+            $table = that.$('#sensors-table');
+            ViewUtils.updateMyDataTable($table, data, function(value, name) {
+                var metadata = that.sensorMetadata[name]
+                if (metadata==null) {                        
+                    // TODO should reload metadata when this happens (new sensor for which no metadata known)
+                    // (currently if we have dynamic sensors, their metadata won't appear
+                    // until the page is refreshed; don't think that's a big problem -- mainly tooltips
+                    // for now, we just return the partial value
+                    return [name, {'name':name}, value]
+                } 
+                return [name, metadata, value];
+            });
         },
 
         /**
@@ -139,32 +159,6 @@ define([
                 that.table.find('*[rel="tooltip"]').tooltip();
             });
             return this;
-        },
-
-        /**
-         * Loads current values for all sensors on an entity and updates sensors table.
-         */
-        updateSensorsNow: function() {
-            var url = this.model.getSensorUpdateUrl(),
-                $table = this.$('#sensors-table'),
-                that = this;
-            $.get(url, function (data) {
-                if (that.viewIsClosed) {
-                    return
-                }
-                ViewUtils.updateMyDataTable($table, data, function(value, name) {
-                    var metadata = that.sensorMetadata[name]
-                    if (metadata==null) {
-                        // TODO should reload metadata when this happens (new sensor for which no metadata known)
-                        // (currently if we have dynamic sensors, their metadata won't appear
-                        // until the page is refreshed; don't think that's a bit problem -- mainly tooltips
-                        // for now, we just return the partial value
-                        return [name, {'name':name}, value]
-                    }
-                    return [name, metadata, value];
-                });
-            });
-            return this;
         }
     });
 

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
index 6476fb7..612ead4 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
@@ -20,17 +20,35 @@ define([
             }))
             ViewUtils.updateTextareaWithData($(".for-textarea", this.$el), ej, true, false, 150, 400)
             ViewUtils.attachToggler(this.$el)
-            that.callPeriodically("entity-summary-sensors", 
-                    function() { that.updateSensorsNow(that) }, 3000)
-            that.updateSensorsNow(that)
+
+            // TODO we should have a backbone object exported from the sensors view which we can listen to here
+            // (currently we just take the URL from that view) - and do the same for active tasks;
+            ViewUtils.getRepeatedlyWithDelay(this, this.options.sensors.getSensorUpdateUrl(), 
+                    function(data) { that.updateWithData(that, data) });
+
+            // however if we only use external objects we must either subscribe to their errors also
+            // or do our own polling against the server, so we know when to disable ourselves
+//            ViewUtils.fetchRepeatedlyWithDelay(this, this.model, { period: 10*1000 })
         },
         render:function () {
             return this
         },
-        revealIfHasValue: function(that, sensor, $div, renderer) {
+        revealIfHasValue: function(sensor, $div, renderer, values) {
             var that = this;
             if (!renderer) renderer = function(data) { return _.escape(data); }
-            $.ajax({
+            
+            if (values) {
+                var data = values[sensor]
+                if (data || data===false) {
+                    $(".value", $div).html(renderer(data))
+                    $div.show()
+                } else {
+                    $div.hide();
+                }
+                that.updateStatusIcon();
+            } else {
+              // direct ajax call not used anymore - but left just in case
+              $.ajax({
                 url: that.model.getLinkByName("sensors")+"/"+sensor,
                 contentType:"application/json",
                 success:function (data) {
@@ -41,22 +59,27 @@ define([
                         $div.hide();
                     }
                     that.updateStatusIcon();
-                }})            
+                }})
+            }
+        },
+        updateWithData: function (that, data) {
+            that.revealIfHasValue("service.isUp", that.$(".serviceUp"), null, data)
+            that.revealIfHasValue("service.state", that.$(".status"), null, data)
+            
+            that.revealIfHasValue("webapp.url", that.$(".url"),
+                    function(data) { return "<a href='"+_.escape(data)+"'>"+_.escape(data)+"</img>" }, data)
         },
-        updateSensorsNow: function(that) {
-            // hard-coded values for most commonly used sensors
+        updateSensorsNow: function() {
+            // hard-coded display of the most commonly used sensors
             
-            // this is redundant with values now returned from REST ApplicationResource.applicationTree
-            // but leaving them here until we more cleanly model that in javascript (e.g. lazy loading)
-            that.revealIfHasValue(that, "service.isUp", that.$(".serviceUp"))
-            that.revealIfHasValue(that, "service.state", that.$(".status"))
+            this.revealIfHasValue("service.isUp", this.$(".serviceUp"))
+            this.revealIfHasValue("service.state", this.$(".status"))
             
-            that.revealIfHasValue(that, "webapp.url", that.$(".url"),
+            this.revealIfHasValue("webapp.url", this.$(".url"),
                     function(data) { return "<a href='"+_.escape(data)+"'>"+_.escape(data)+"</img>" })
         },
         
         updateStatusIcon: function() {
-            // currently we use the string values from the page; messy, but it works
             var statusIconUrl = ViewUtils.computeStatusIcon(this.$(".serviceUp .value:visible").html(), this.$(".status .value:visible").html());
             if (statusIconUrl) {
                 this.$('#status-icon').html('<img src="'+statusIconUrl+'" '+

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/home.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/home.js b/usage/jsgui/src/main/webapp/assets/js/view/home.js
index 32f0399..828e623 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/home.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/home.js
@@ -3,12 +3,15 @@
  */
 
 define([
-    "underscore", "jquery", "backbone", "./application-add-wizard", "model/location",
+    "underscore", "jquery", "backbone", "view/viewutils", 
+    "./application-add-wizard", "model/location",
     "text!tpl/home/applications.html",
     "text!tpl/home/summaries.html",
     "text!tpl/home/app-entry.html",
     "bootstrap"
-], function (_, $, Backbone, AppAddWizard, Location, ApplicationsHtml, HomeSummariesHtml, AppEntryHtml) {
+], function (_, $, Backbone, ViewUtils,
+        AppAddWizard, Location, 
+        ApplicationsHtml, HomeSummariesHtml, AppEntryHtml) {
 
     var HomeView = Backbone.View.extend({
         tagName:"div",
@@ -33,16 +36,23 @@ define([
             	locations:this.options.locations
         	})
             this.renderSummaries()
+            
             this.collection.on('reset', this.render, this)
             this.options.locations.on('reset', this.renderSummaries, this)
 
+            ViewUtils.fetchRepeatedlyWithDelay(this, this.collection, 
+                    { fetchOptions: { reset: true }, doitnow: true, 
+                    /* max is short here so the console becomes usable quickly */
+                    backoffMaxPeriod: 10*1000 });
+            ViewUtils.fetchRepeatedlyWithDelay(this, this.options.locations, { fetchOptions: { reset: true }, doitnow: true });
+
             id = $(this.$el).find("#circles-map");
             if (this.options.offline) {
             	id.find("#circles-map-message").html("(map off in offline mode)");
             } else {
             	requirejs(["googlemaps"], function (GoogleMaps) {
             	    _.defer( function() {
-            	        console.debug("loading google maps")
+            	        log("loading google maps")
             			var map = GoogleMaps.addMapToCanvas(id[0],
             			        // brooklyn bridge
 //            			        40.7063, -73.9971, 14
@@ -60,18 +70,9 @@ define([
             	}, function (error) {
             			id.find("#circles-map-message").html("(map not available)"); 
             	});
-            }
-            
-            this.callPeriodically("home", function() {
-            	that.refresh(that);	            	
-            }, 5000)
-            this.refresh(this)
+            }            
         },
         
-        refresh:function (that) {
-        	that.collection.fetch({reset: true})
-        	that.options.locations.fetch({reset: true})
-        },
         updateCircles: function(that, locatedLocations, GoogleMaps, map) {
             locatedLocations.fetch({success:function() {
                 GoogleMaps.drawCircles(map, locatedLocations.attributes)
@@ -128,7 +129,7 @@ define([
                 this.$(".add-app #modal-container .modal")
                     .on("hidden",function () {
                         wizard.close()
-                        that.refresh(that)
+                        that.collection.fetch({reset:true});
                     }).modal('show')
             }
         },

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/d13dc996/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js b/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js
index a30635c..7867fbb 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/viewutils.js
@@ -242,6 +242,178 @@ define([
                 // ignore - normal during tests
             }
         },
+        /* variant of $.get with automatic failure handling and recovery;
+         * options should be omitted except by getRepeatedlyWithDelay */
+        get: function(view, url, success, options) {
+            if (view.viewIsClosed) return ;
+            if (options['enablement'] && !options['enablement']()) {
+                // not enabled, just requeue
+                if (options['period']) 
+                    setTimeout(function() { ViewUtils.get(view, url, success, options)}, options['period'])
+                return;
+            }
+            
+            /* inspects the status object returned from an ajax call in a view;
+             * if not valid, it fades the view and increases backoff delays and resubmits;
+             * if it is valid, it returns true so the caller can continue
+             * (restoring things such as the view, timer, etc, if they were disabled);
+             * 
+             * takes some of the options as per fetchRepeatedlyWithDelay
+             * (though they are less well tested here)
+             * 
+             * note that the status text object is rarely useful; normally the fail(handler) is invoked,
+             * as above (#get)
+             */
+            var checkAjaxStatusObject = function(status, view, options) {
+                if (view.viewIsClosed) return false;
+                if (status == "success" || status == "notmodified") {
+                    // unfade and restore
+                    if (view._loadingProblem) {
+                        log("getting view data is back to normal - "+url)
+                        log(view)
+                        view._loadingProblem = false;
+                        
+                        var fadeTarget = view.$el;
+                        if ("fadeTarget" in options) {
+                            fadeTarget = options["fadeTarget"]
+                        }
+                        if (fadeTarget) ViewUtils.cancelFadeOnceLoaded(fadeTarget)
+                        
+                        if (options['originalPeriod']) 
+                            options.period = options['originalPeriod']; 
+                    }
+                    
+                    return true;
+                }
+                if (status == "error" || status == "timeout" || status == "parsererror") {
+                    // fade and log problem
+                    if (!view._loadingProblem) {
+                        log("error getting view data from "+url+" - is the server reachable?")
+                        view._loadingProblem = true;
+                    }
+                    // fade the view, on error
+                    var fadeTarget = view.$el;
+                    if ("fadeTarget" in options) {
+                        fadeTarget = options["fadeTarget"]
+                    }
+                    if (fadeTarget) ViewUtils.fadeToIndicateInitialLoad(fadeTarget)
+
+                    if (options['period']) {
+                        if (!options['originalPeriod']) options.originalPeriod = options['period'];
+                        var period = options['period'];
+                        
+                        // attempt exponential backoff up to every 15m
+                        period *= 2;
+                        var max = (options['backoffMaxPeriod'] || 15*60*1000);
+                        if (period > max) period = max;
+                        options.period = period
+                        setTimeout(function() { ViewUtils.get(view, url, success, options)}, period)
+                    } 
+                    
+                    return false;
+                }
+                return true;
+            }
+            
+            return $.get(url, function(data, status) {
+                if (!checkAjaxStatusObject(status, view, options)) {
+                    return;
+                }
+                if (success) success(data);
+                if (options['period']) 
+                    setTimeout(function() { ViewUtils.get(view, url, success, options)}, options['period'])
+            }).fail(function() {
+                checkAjaxStatusObject("error", view, options)
+            })
+        },
+        /** invokes a get against the given url repeatedly, with fading and backoff on failures,
+         * cf fetchRepeatedlyWithDelay, but here the user's callback function is invoked on success
+         */
+        getRepeatedlyWithDelay: function(view, url, success, options) {
+            if (!options) options = {}
+            if (!options['period']) options.period = 3000
+            ViewUtils.get(view, url, success, options)
+        },
+        /* invokes fetch on the model, associated with the view.
+         * automatically closes when view closes, 
+         * and fades display and exponentially-backs off on problems.
+         * options include:
+         * 
+         *   enablement (function returning t/f whether the invocation is enabled)
+         *   period (millis, currently 3000 = 3s default);
+         *   originalPeriod (millis, becomes the period if successful; primarily for internal use);
+         *   backoffMaxPeriod (millis, max time to wait between retries, currently 15*60*1000 = 10m default);
+         *    
+         *   doitnow (if true, kicks off a run immediately, else only after the timer)
+         *   
+         *   fadeTarget (jquery element to fade; defaults to view.$el; null can be set to prevent fade);
+         *   
+         *   fetchOptions (additional options to pass to fetch; however success and error should not be present);
+         *   success (function to invoke on success, before re-queueing);
+         *   error (optional function to invoke on error, before requeueing);
+         */
+        fetchRepeatedlyWithDelay: function(view, model, options) {
+            if (!options) options = {}
+            var period = options['period'] || 3000
+            var originalPeriod = options['originalPeriod'] || period
+//            log("fetching "+model.url+" with delay "+period)
+            var fetcher = function() {
+                if (view.viewIsClosed) return;
+                if (options['enablement'] && !options['enablement']()) {
+                    // not enabled, just requeue
+                    ViewUtils.fetchRepeatedlyWithDelay(view, model, options);
+                    return;
+                }
+                var fetchOptions = options['fetchOptions'] ? _.clone(options['fetchOptions']) : {}
+                fetchOptions.success = function(modelR,response,optionsR) {
+                        var fn = options['success']
+                        if (fn) fn(modelR,response,optionsR);
+                        if (view._loadingProblem) {
+                            log("fetching view data is back to normal - "+model.url)
+                            view._loadingProblem = false;
+                            
+                            var fadeTarget = view.$el;
+                            if ("fadeTarget" in options) {
+                                fadeTarget = options["fadeTarget"]
+                            }
+                            if (fadeTarget) ViewUtils.cancelFadeOnceLoaded(fadeTarget)
+                        }
+                        options.period = originalPeriod;
+                        ViewUtils.fetchRepeatedlyWithDelay(view, model, options);
+                }
+                fetchOptions.error = function(modelR,response,optionsR) {
+                        var fn = options['error']
+                        if (fn) fn(modelR,response,optionsR);
+                        if (!view._loadingProblem) {
+                            log("error fetching view data from "+model.url+" - is the server reachable?")
+                            log(response)
+                            view._loadingProblem = true;
+                        }
+                        // fade the view, on error
+                        var fadeTarget = view.$el;
+                        if ("fadeTarget" in options) {
+                            fadeTarget = options["fadeTarget"]
+                        }
+                        if (fadeTarget) ViewUtils.fadeToIndicateInitialLoad(fadeTarget)
+                        
+                        // attempt exponential backoff up to every 15m
+                        period *= 2;
+                        var max = (options['backoffMaxPeriod'] || 15*60*1000);
+                        if (period > max) period = max;
+                        options = _.clone(options)
+                        options.originalPeriod = originalPeriod;
+                        options.period = period;
+                        ViewUtils.fetchRepeatedlyWithDelay(view, model, options);
+                };
+                model.fetch(fetchOptions)
+            };
+            if (options['doitnow']) {
+                options.doitnow = false;
+                fetcher();
+            } else {
+                setTimeout(fetcher, period);
+            }
+        },
         computeStatusIcon: function(serviceUp, lifecycleState) {
             if (serviceUp==false || serviceUp=="false") serviceUp=false;
             else if (serviceUp===true || serviceUp=="true") serviceUp=true;