The Interweb is a wonderful place to host complex web applications as long as you’re tethered to it by Cat5 and an ISP that has your back (Communism works out in this scenario as well). But that’s not the world we live in, and in all other cases using web applications from mobile platforms can be a new circle of Dante’s Inferno below that ice one.

The trouble with web applications on mobile platforms is two fold. First, displaying data for mobile devices is a problem in and of itself, for which I will say nothing more about in this article. Imagine users with laptops and tablet-PCs. The second and more interesting problem revolves around maintaining state and managing client server interactions in an environment where spotty network coverage and ambient radio noise are totally messing with your stuff.

In general developers seem to take for granted the following:

  1. The user is always connected to the Internet
  2. The user never switches between browser windows during a stateful web process

This list is certainly not exhaustive, but it’s enough for me to fulfill my weekly blog post requirement. The second point is the most difficult for new developers, so lets start there.
On processes that span several pages – imagine something like a long form spanning three pages taking billing information, delivery information, then payment information – it is tempting to simply slap in a session entry to keep your data between page loads and decide when the process is finished. Most multi-page ordering systems work this way, and it’s often not a problem for their users, but imagine this contrived counter-example:

Edgar W. Developer goes to popularOnlineRetailer.com to buy speakers for his office. He’s stolen the company credit card, has put the speakers in his web shopping cart and has started the checkout process. Then Edgar decides that good deal on hard drives he saw is too good to pass up and wants to order one to his home with his non-stolen credit card. He opens a new window (in the same browser), adds the hard drive to his cart, checks out, notices the speakers, removes them from his cart, completes the process, then closes the window. Now he finishes his original order of the speakers, but when he clicks “submit” something goes terribly awry. His order doesn’t exist anymore.

Edgar would clearly just start over with his criminal intentions on speakers, and most of us would brush this off as a minor inconvenience, but if your users have to order something on the order of one hundred speakers a day through an interface requiring similarly long information requirements and you don’t address this issue, then you may face an angry mob of data entry operators whose manager sold you out and gave them your address. It happens.

What happens, specifically, is that the session identifier is attached to the user’s browser as a cookie, and cookies are sent by every instance of that browser which accesses the issuing domain. Thus, by using multiple instances, the user has wandered into a use case that was unanticipated. There is no way to keep track of instances of the browser strictly from the header information – not even if you allow an infinite list of pages visited, the arguments submitted to those pages and an authoritative map of page relations. The proof of this is left as exercise.

If developers are aware of this problem from planning or testing they might make the web form store it’s entire state at each step. This is a good solution in most cases, and it’s something I’ve done many times in the past. You can even keep all of the form’s values in the session in an array of data arrays and just pass the key to your data in the web form itself. But sometimes this is not enough – either because your data is large or difficult to validate, or because there is so much a variety of such forms that lazy developers like myself just don’t want to do this each time. In fact, our ability to tie session data to a browser instance may make or break our application as its complexity grows.

Handling Per-Instance State

We require a solution that allows us to tie stateful information to a browser instance. The first thing that comes to most people’s mind is to “use Javascript”, and let the Javascript guy figure it out. But it’s not such a simple issue for Javascript Guy. Guy knows that you only have access to the current browser instance, any frames, and window opened via window.open. Guy has to be tricky.

The solution that comes to mind is to use JavaScript to attach an onclick event to links. When a user right-clicks and opens a link into a new tab or window the anchor element’s onclick event does not fire. Thus we can add behavior to this event that will tell the server that this is a specific pre-existing instance of the browser. We can pass an instance id to the server that links our request to a specific place in the session.

How we pass this id is not trivial. We can append the instance id to the URL as an anchor, such as index.php?arg1=val1&arg2=val2#instance_id=123456, then, on the receiving page, we can change the location of the browser to not include the instance id. Since it is a local anchor the page will not reload and bookmarks and links will not be relative to the local anchor. But there is a slim window of time between page load and script execution when bookmarks could happen. Instead of appending the local anchor to our URL, we could have the onclick event create a form which targets the link location and passes via POST the unique instance id and submits that form. This “solution” will present your users with a warning that they are resubmitting post variables every time they click back or forward. On the back end, if we don’t receive a valid instance id then we would initialize a new instance and make the user aware that they are starting over (punishment for violating your use cases). If the user has JavaScript turned off, as some are apt to do, then you are constantly initializing a new local instance. The best thing to do for them is capture the fact that they’re not using JavaScript early and support alternative logic in your session manager. If you can’t, then you’ll have to settle for something far less elegant.

We can reign-in the multiple page processes causing problems for us into a single page managed via JavaScript. Rather than providing a monolithic form, the developer provides the same small sections but advances the user through the process by having JavaScript modify which parts of the form are visible, all while carefully storing the final version in a form for submission once all information is collected.

For larger web applications this solution grows into a kind of JavaScript application controller approach to browser events. When the user clicks a link, that event is intercepted by JavaScript and an AJAX call occurs to execute a command associated with that event. The developer effectively reigns in all the different events with JavaScript and handles them appropriately (passing a unique instance id). If the user does something which breaks the use cases, like right-clicking and selecting “open in new tab” on a link thereby bypassing the onclick event, then the whole environment is reinitialized in the new instance (with a new instance id). This is a good solution for processes that are tightly bound together. Google’s gmail is a good example of this type of solution.

Similar to our method of passing the entire state of the form from page to page above, we can store the state of our more dynamic pages in the URL of the browser instance so that bookmarking and opening links into new windows will carry their necessary stateful data with them. Take a look at YUI’s Browser History Manager.

The YUI Browser History Manager is a utility designed to facilitate the creation of web applications in which the navigation buttons are fully functional and in which broad aspects of an application’s state — what panels are open, what tabs are active, etc. — can be bookmarked.

You can read more about YUI’s Browser History Manager at YUI’s development site.

Handling Flaky AJAX Connections

The AJAX approaches above are related to my first point – that web developers don’t consider connection failures when they design web applications. In order to gracefully handle these issues you need to structure and organize your pages’ interaction with the server via AJAX, and this is almost always a queue or a stack of your AJAX calls. Without loss of generality, assume it’s a queue. We want the queue to recover from a connection failure by notifying subscribed elements that a connection failure has occurred, retry the connection after some interval, and then notify subscribed elements when the connection has been re-established. Normally, you would pass a function to handle failure of your request, but in this design we do not accept failure – we assume that it’s only a matter of time before we achieve success. If the user leaves the page before our queue resolves then their data will be lost.

In the first iteration of our implementation the queue exists on a menu page in which we’ve tucked away the format of all the subsequent pages. When the user clicks a link a new window is opened and filled in so that they can complete the form, mark it closed, and close the window. Only the one main window needs to stay open so we apply a beforeunload event handler to warn the user when the queue is nonempty that there are still processes that need to complete, urging them to leave the window open. If we are mindfully aware of memory leak-causing decisions (just try to keep the division between the DOM and JavaScript as stark as possible) then this process can run nearly indefinitely (we’ve tested 8 hours of vigorous activity, but it looked like it could keep going). The user simply continues on and as they wander through an area with connectivity their data is automatically sent up to the server.

The second iteration of our implementation had to address the maniacal impulse of our users to close the browser window anyway. In this situation we had to find a place to store data on the user’s machine, and there are three ways of going about this.

First, we can use cookies. Cookies are tied to a domain and can be no more that 4 kilobytes before explorer throws a fit. For this reason you’ll want to make sure that there is no ambiguity in which domain from which the user arrived. For example, users going to popularOnlineRetailer.com should be forwarded to www.popularOnlineRetailer.com. Our implementation included 200 characters of textual notes and a series of selectable options such that we were able to store 10 jobs before the user had to exchange that information with the server.

If cookies are not enough, there are ways of storing state with Adobe Flash and with Google Gears. Both Flash and Gears are proprietary products that must be installed on the user’s system, but 90% of systems have Flash already, and Google Gears provides a richer API with fewer restrictions for storing data than does the Flash solution. Both methods are supported by the Dojo project’s Dojox Offline toolkit. Rather than plagiarizing their documentation, let me simply encourage you to read it.

The Flash and cookie solutions are unable to restore the user’s access while they’re offline. Keeping the window open allows them continual access, and it is a feature of Gears that you can stash your pages in it for offline access – allowing the user to re-open the application while offline. Adobe and Microsoft have products on the shelf to accomplish similar functionality, but they require more installation, and at that point why not just use Java web launch which is free and gives you more power anyway.

Posted in: Design, Development, How To