What works:

I have an application running on a non-interactive kiosk that I need to keep updated with information that will be provided by a remote server. To do this, I've created an XMLHttpRequest/Microsoft.XMLHTTP object that checks the modified date in the header of a watched file that resides on a remote server.

This function runs on an interval, and if it detects a change in the modified date since the last check, it retrieves new information from the remote server.


The problem:

When the kiosk is unable to connect to the remote server, I want it to fall back to displaying a file stored locally. To do this, I've added a contingency to the header request so that if the response from the server was something other than 200, it would trigger the display of the fallback page.

In Safari and Firefox for Mac, and Safari and Internet Explorer for Windows, this seems to work. But I need it to work on Firefox for PC, and a 1.5 version of Firefox running on a Linux network appliance.

Where it doesn't work, nothing seems to happen. I've done tests with DIVs displaying the response on each request, and some of the browsers just seem to keep trying to connect, and failing to, but never display 0, or some other incomplete response.

In case the problem was that the OnStateChange event wasn't tripping appropriately when the connection was lost, I've added a secondary function set to call the OnStateChange method, which runs on another interval. But, this hasn't helped.

/* STATIC SETTINGS */
"use strict";
var mod_header_a = '';
var mod_header_b = '';
var mod_bool = false;
var network_err = false;
var fallback = "file:///tmp/ftproot/usb_1/fallback/alt/screen1/layout.html";
var dummy_path = "../../../dummy.txt";
var layout_path;
var rnum = '';
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var string_length = 8;
var query_string = '';

function random_string()
{
	query_string = '';
	for (var i=0; i<string_length; i++)
	{
		rnum = Math.floor(Math.random() * chars.length);
		query_string += chars.substring(rnum,rnum+1);
	}
	return query_string;
}

function reload_frame()
{
	//layout_path set by inline code within index.html
	document.getElementById("ajax_frame").src = layout_path+'?'+random_string();
}

function create_request_object()
{
	var request_obj;
	var browser = navigator.appName;
	if(browser === "Microsoft Internet Explorer")
	{
		request_obj = new ActiveXObject("Microsoft.XMLHTTP");
	}
	else
	{
		request_obj = new XMLHttpRequest();
	}
	return request_obj;
}
var dum_head = create_request_object();

function determine_state()
{
	if(dum_head.readyState === 4)
	{
		if(dum_head.status === 200)
		{
			if(network_err === true || mod_bool === true)
			{
				document.getElementById("err_div").style.display = "none";
				reload_frame();
				network_err = false;
			}
			mod_header_b = mod_header_a;
			mod_header_a = dum_head.getResponseHeader("Last-Modified");
		}
		else
		{
			document.getElementById("err_div").style.display = "block";
			if(network_err === false)
			{
				document.getElementById("ajax_frame").src = fallback+'?'+random_string();
			}
			network_err = true;
		}
	}	
}

function get_header()
{
	if (mod_header_a !== mod_header_b && mod_header_b !== '')
	{
		mod_bool = true;
	}
	else 
	{
		mod_bool = false;
	}
/	dum_head.open("HEAD", dummy_path+"?"+random_string(), true);
	dum_head.onreadystatechange = determine_state;
	dum_head.send(null);
}

window.setInterval("manual_update()", 20000);
function manual_update()
{
	dum_head.onreadystatechange();
	determine_state();
}

Thoughts :

  • You would probably benefit from a formulation of create_request_object that uses try/catch rather than testing navigator.appName . There are many examples on the web.
  • Only kick off the update cycle after the page has loaded, otherwise a quick ajax response could generate a Javascript error and the whole thing will crash.
  • It would be better to coordinate the update cycle from within the response/error handlers rather than using an interval. This will immunise you from the possibility of having two (or more) simultaneous unserviced requests, as might be the case with slow/failed server.
  • You also need to handle server failure (no response), in the same way you handle a non-200 status.
  • It is more typical (and more reliable?) not to reuse an xmlHttpRequest.
  • There appears to be some unnecessary global variables and flag waving.

Putting all this together, you could do something like this:

var mod_header_a = '';
var mod_header_b = '';
var fallback = "file:///tmp/ftproot/usb_1/fallback/alt/screen1/layout.html";
var dummy_path = "../../../dummy.txt";
var layout_path;

var maximum_waiting_time = 5 * 1000;//milliseconds
var cycle_time = 20 * 1000;//milliseconds

function create_request_object() {
  var r = false;
  try { r = new XMLHttpRequest(); }
  catch (e) {
    try { r = new ActiveXObject("Msxml2.XMLHTTP"); }
    catch (e) {
      try { r = new ActiveXObject("Microsoft.XMLHTTP"); }
      catch (e) { alert("Browser doesn't support XMLHTTP (AJAX)"); return false; }
    }
  }
  return r;
}

function errorHandler() {
	document.getElementById("err_div").style.display = "block";
	document.getElementById("ajax_frame").src = fallback;
};

function successHandler() {
	if(mod_header_a !== mod_header_b && mod_header_b !== '') {
		document.getElementById("err_div").style.display = "none";
		document.getElementById("ajax_frame").src = layout_path + '?' + Math.random();
	}
	mod_header_b = mod_header_a;
	mod_header_a = httpRequest.getResponseHeader("Last-Modified");
};

function get_header() {
	var httpRequest = create_request_object();
	httpRequest.open("HEAD", dummy_path + "?" + Math.random(), true);
	var requestTimer = setTimeout(function() {
		httpRequest.abort();
		errorHandler();
		get_header();
	}, maximum_waiting_time);
	httpRequest.onreadystatechange = function() {
		if(httpRequest.readyState === 4) {
			clearTimeout(requestTimer);
			if(httpRequest.status === 200) { successHandler(); }
			else { errorHandler(); }
			setTimeout(function(){ get_header(); }, cycle_time);
		}
	};
	httpRequest.send(null);
};

onload = function(){
	get_header();
};

As you will see, get_header is called when the page loads, and thereafter it is called either after the server has responded or after a request timeout (each with an independently defined delay).

The above code runs though it's not really tested (I would need to create several other files). It may therefore need debugging, but once debugged it will certainly handle more failure cases and stands a better chance of working cross-browser.

Airshow

Thanks, Airshow. I'll have to study this and get back with the results.

[Nice signature; I couldn't agree more.]

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.