Feb 03

Checking the network to handle offline applications

Gears, Tech with tags: 1 Comment »

One of the common questions we here in Gears land is, how to handle checking if an application is online or offline. This is a trickier problem that you may think at first. What does it mean to be offline?

The Autodesk guys talked about this a little in their interview. They have the notion of online and offline, which is just a flag. They then have the notion of connected or disconnected, which they test at regular intervals.

Mathew Foster recently posted the code that he uses to handle this situation. He basically ported the Dojo Offline work to Prototype, and you end up with a library that you can use like this:

var netCheck = new NetworkDetection("blank.php");
 
netCheck.addEventListener("online", function(eAja) {
  $("display").innerHTML = "online";
});
netCheck.addEventListener("offline", function(eAja){                                          
  $("display").innerHTML = "offline";                                      
});

Some other folks also put their thoughts out there.

Firstly, Dimitri Glazkov (who is on fire recently btw) posted his monitor script:

    // provides connection monitoring
    // controller
    function Monitor() {
 
        var me = this;
 
        //  triggered when connection changes
        //  sends as parameter:
        //      online : Boolean, true if connection became available,
        //          false if connection is broken
        this.onconnectionchange = nil;
 
        // starts the monitoring
        this.start = function() {
            try {
                wp = google.gears.factory.create('beta.workerpool', '1.0');
            }
            catch(e) {
                return false;
            }
            wp.onmessage = function(a, b, message) {
                if (message.sender == id) {
                    // only two messages: 
                    // first [f]ailure to connect 
                    // or back [o]nline
                    var text = message.text;
                    if (text == 'f') {
                        me.onconnectionchange(false);
                    }
                    else if (text == 'o') {
                        me.onconnectionchange(true);
                    }
                }
            }
            id = wp.createWorker(String(worker) + ';worker()');
            // send a message to the worker, identifying owner's id
            wp.sendMessage(window.location + '?poll', id);
            return true;
        }
 
        function worker() {
            var POLLING_INTERVAL = 2000;
 
            var wp = google.gears.workerPool;
            var url;
            var parentId;
 
            var first = true;
            var online;
 
            var timer = google.gears.factory.create('beta.timer', '1.0');
 
            wp.onmessage = function(a, b, message) {
                url = message.text;
                parentId = message.sender;
                poll();
            }
 
            function poll() {
                var request = google.gears.factory.create('beta.httprequest', '1.0');
                request.open('HEAD', url);
                request.onreadystatechange = function() {
                    if (request.readyState == 4) {
                        try {
                            if (request.status == 200) {
                                if (!online) {
                                    online = true;
                                    wp.sendMessage('o', parentId);
                                }
                            }
                        }
                        catch(e) {
                            if (online || first) {
                                online = false;
                                first = false;
                                wp.sendMessage('f', parentId);
                            }
                        }
                        wp.sendMessage('d', parentId);
                        timer.setTimeout(poll, POLLING_INTERVAL);
                    }
                }
                try {
                    request.send();
                }
                catch(e) {
                    if (online) {
                        online = false;
                        wp.sendMessage('f', parentId);
                    }
                }
            }
 
        }
 
        function nil() {}
    }

Gears co-lead Aaron Boodman also put his hat in the ring with a solution that uses the Gears HttpRequest object, and a clean use of onerror:

function monitorOnlineness(url, onlinenessChanged) {
 var interval = 1000; // check network once a second
 var onlineness = null;
 var timer = google.gears.factory.create('beta.timer');
 var timerId = null;
 
 function check() {
   timerId = timer.setTimeout(function() {
     var req = google.gears.factory.create('beta.httprequest');
     req.onload = function() { onlinenessChanged(true); }
     req.onerror = function() { onlinenessChanged(false); }
     req.open("HEAD", url);
     req.send(null);
   }, interval);
 }
 
 function updateOnlineness(val) {
   if (onlineness !== val) {
     onlineness = val;
     onlinenessChanged(val);
   }
   check();
 }
 
 function cancel() {
   if (timerId) {
     timer.clearTimeout(timerId);
   }
 }
 
 return cancel;
}

You quickly find that there are many solutions, and that they depend on what you really need to do for your application use case. I am still personally hopeful that an 80% solution is placed into Gears itself as a starting point….