17.4. Timeout![]() Logout, Screensaver, Session, Suspend, Timeout Figure 17-11. Timeout![]() 17.4.1. Goal StoryTracy was so keen on leaving the office Friday afternoon that she forgot to shut down the market news web site. Fortunately, the web site monitors mouse movements, meaning that if she doesn't interact with the page for an hour, it will suspend itself, saving a lot of wasted refreshes. On Monday morning, Tracy walks in and sees a screensaver-like animation in the browser, with a message indicating that updates have ceased. She clicks a mouse button and sees the screen update with the latest prices. 17.4.2. ProblemHow can you tell whether the user is still working with the application? 17.4.3. Forces
17.4.4. SolutionHave the browser Timeout the user after a period of inactivity and, optionally, inform the server. After a period of inactivity, the application is either suspended, requiring the user to manually resume it or shut down, requiring the user to restart. This pattern raises the whole question of sessions in Ajax. Conventional applications usually have server-side session objects which represent the user's current session. Typically, this means short-lived information such as shopping cart contents or recently viewed items. In Ajax, server-side sessions are not so useful. In fact, if you rely solely on RESTful Services, you may not need those session objects at all. In browser-centric Ajax Apps, the browser is a full-fledged application with all of the session state held within. So the browser state is the session. The server-side session, if used at all, is only relevant to authentication and has no impact on the response to any particular query. See RESTful Service (Chapter 9) for more details. How is all of this relevant to Timeout? Well, consider this familiar nightmare scenario: The user opens up a form, spends two hours populating it while researching the answers, and clicks submit. The browser then responds with "Your session has expired" and adds insult to injury by presenting a new blank form. Stories like this are all too common because server-side session Timeouts ignore what's happening in the browser. If the user hasn't submitted a form in, say, 30 minutes, the session times out and all the data is lost. That's a problem if the user has been actively working on the browser side. With an Ajax App, the server has a better chance of staying in sync with the browser thanks to XMLHttpRequest Calls (Chapter 6). In most environments, the server-side session will be automatically refreshed when an XMLHttpRequest Call comes in. However, this is not optimal either because the server still doesn't know why browser calls are occurring. For applications using Periodic Refresh (Chapter 10), for example, a server-side session will stay alive indefinitely even if the user has left the building. By relying instead on browser-based Timeout detection, you can be more intelligent about how Timeouts will occur. Browser-based Timeout detection is based on Scheduling (Chapter 7). A timer is established to count down to Timeout state. Each significant activity cancels the timer and starts a new one in its place. What activities are significant enough to restart the timer? Mouse movement is one candidate. You can monitor each mouse movement on the page; one of the following Code Refactoring illustrations below does exactly that. Mouse movements are a very broad indicator, and you might be concerned about "false positives": These may be suggestions that a user is still working with an application when they're in fact not, e.g., they just happened to move the mouse across the browser window while cycling through each open window. You'll also get some "false negatives": A user working only with the keyboard is liable to be timed out. If you'd prefer to keep the session going only if the user is actively changing data, you can instead catch events such as button clicks and keypresses within input fields. Sudden Timeouts can be frustrating for users, so consider these feedback measures:
Once you've detected that the user is inactive, what do you do? Timeout can be used in several ways:
So informing the server of a Timeout is a good thing. But there's still a small problem with this approach: What if the user quits the browser or moves away to another site? Then a Timeout will never occur and the server will blissfully continue assuming that the user is logged in for all eternity. To alleviate this problem, have the server refresh the user's record upon each incoming request that suggests user activity. The rest remains, however, of inadvertently timing out a user who is performing browser-only activity. So what we need is a way for the browser to keep telling the server that the user is still active, explicitly noting that a Timeout hasn't actually occurred. That's exactly what the Heartbeat pattern is aboutsee later in this chapter for more details. 17.4.5. Decisions17.4.5.1. How long will the Timeout period be?The Timeout period will depend on the purpose for having the Timeout period in the first place. For example, if the purpose is primarily to stop Periodic Refresh (Chapter 10), you need to weigh the benefits of reduced bandwidth and server costs against the frustration caused to users who will have to reactivate the page and wait a few seconds for the latest data. In practice, there are probably several reasons to use Timeout, and the needs in each situation must be taken into consideration. 17.4.5.2. How will Timeout affect the user interface?A Timeout can impact the interface in different ways. You may choose to use any of the following:
In addition, if you want to be really sure that users know they've been timed out, you could produce an alert. Use Timeout alerts with caution: it's quite obtrusive, and you could probably make the Timeout equally obvious with a carefully considered page element. A Popup (Chapter 15) is a less obtrusive mechanism. 17.4.6. Real-World ExamplesMany conventional web sites have a session Timeout feature, though it's implemented server side rather than the way I've described in this section. 17.4.6.1. Lace ChatBrett Stimmerman's Lace Chat (http://www.socket7.net/lace/) is an Ajax chat app. As such, it requires a Periodic Refresh in order to keep showing new messages, a serious bandwidth concern if the user is no longer interested in the conversation. So after some idle time, a dialog box appears indicating that Lace has stopped; it includes a button allowing the user to resume (Figure 17-12). Lace also has a Status Area (Chapter 15) that always shows whether the application is runningi.e., whether the server is being polled. In fact, you can pause and resume it manually too. The Timeout mechanism hooks into this state by forcing the application to pause upon Timeout. Figure 17-12. Lace Chat Timeout![]() 17.4.6.2. PandoraPandora uses Flash to stream music into a browser. After a long idle period, it stops playing and produces a dialog to confirm that the user is still listening. 17.4.6.3. Session Warning DemoEric Pascarello has explained a demo system which has a standard server-side session Timeout but which also pops up a warning near Timeout allowing the user to renew the Timeout (http://radio.javaranch.com/pascarello/2005/07/05/1120592884938.html). There are implementations in both VB.net and JSP. 17.4.6.4. Operating system TimeoutsA precursor to Ajaxian Timeout is evident in operating systems, such as Apple OSX, that show a warning when they are about to enter standby mode. A pop-up dialog says something like "You haven't used the mouse or keyboard for 15 minutes. Standby mode will start in 30 seconds." The dialog will count down, and the user can prevent the Timeout with a keyboard or mouse action or by explicitly hitting a Cancel button. A similar precursor is present in remote terminal systems that output an idle warning to the console. 17.4.7. Code Refactoring: AjaxPatterns Timeout Wiki17.4.7.1. Introducing Timeout to the wikiTimeout is a very useful pattern for a wiki on several counts. Firstly, it cuts down on Periodic Refresh (Chapter 10) wastage. Secondly, it helps users understand what others are up to. Thirdly, it improves security if users are logged on. For all of these reasons, we will now add some Timeout functionality to the Basic Wiki Demo (http://ajaxify.com/run/wiki). (The Periodic Refresh Time Demo [http://ajaxify.com/run/time/periodicRefresh] also has a couple of similar Timeout refactorings under it.) 17.4.7.2. Initial refactoring: unconditional TimeoutThis initial version, Timeout Wiki Demo (http://ajaxify.com/run/wiki/timeout), is merciless. Log into the wiki and, no matter what you do, you're timed out a few seconds later (Figure 17-13). The point, of course, is to introduce and prove the basic Timeout functionality, which can be built on later. Figure 17-13. A timed-out wiki![]() A Timeout message that will initially be hidden is added to the initial HTML: <div > Timed out. Please <a href=".">reload the page</a> to continue.<br/> </div> The script begins by hiding the Timeout message and kicking off a Timeout timer: window.onload = function( ) { ... $("timeoutMessage").style.display = "none"; timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME); } When Timeout occurs, the Timeout message is made visible (with some fanfare, courtesy of a Scriptaculous effect [http://script.aculo.us]). Most importantly, the periodic sync stops, so the server is no longer polled. All the messages are removed from the page as well: function onTimeout( ) { new Effect.BlindDown($("timeoutMessage")); stopPeriodicSync( ); removeMessages( ); } ... function removeMessages( ) { while ($("messages").hasChildNodes( )) { $("messages").removeChild($("messages").firstChild); } } 17.4.7.3. Warning that Timeout is pendingIf only for the selfish reason that we want to avoid being flamed by the user, it would be nice to warn them that a Timeout is about to occur. And nicer still to let them prevent it from occurring. So this demo (http://ajaxify.com/run/wiki/timeout/warning) introduces a warning mechanism. To the previous Timeout message we add a warning message. Both are initially invisible. The warning includes a renew button, which will invoke renewSession( ). <div > Near timeout. Please <button >Renew</button> your session now. </div> window.onload = function( ) { ... $("renew").onclick=renewSession; ... } renewSession( ) is not only used on renew but also on startup. It resets both the timeoutTimer and the warningTimer. Note that these timers have been introduced as variables in the script, precisely so that we can cancel them in this method. In addition, renewSession( ) kicks off the standard wiki sync timer and hides the warning and Timeout messages in the case that either is showing: var timeoutTimer = null; var warningTimer = null; ... function renewSession( ) { $("warningMessage").style.display = "none"; $("timeoutMessage").style.display = "none"; clearInterval(warningTimer); clearInterval(timeoutTimer); warningTimer = setTimeout(onWarning, WARNING_TIME); timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME); startPeriodicSync( ); } So as long as the user clicks on the renew button while it's showing, he can force all timers to be restarted. But if he doesn't click on it, Timeout will proceed. We simply have to ensure that the warning message is shown before the Timeout message, i.e., WARNING_TIME must fall short of TIMEOUT_TIME. When Timeout occurs, we can assume that the warning is already being shown, so we replace it with the Timeout message. And as before, we stop synchronization and remove all messages: function onTimeout( ) { new Effect.BlindUp($("warningMessage")); new Effect.Appear($("timeoutMessage")); stopPeriodicSync( ); removeMessages( ); } 17.4.7.4. Monitoring mouse movementsA warning's better than nothing, but it does have the slight odor of techno-centeredness. From the user's perspective, why should she have to explicitly renew the session. What's this Timeout business anyway? For most users, it's better to handle Timeout on their behalf. This demo (http://ajaxify.com/run/wiki/timeout/monitoring/) builds on the initial Timeout demo to suspend activity when the user is idle. Here, we'll make an assumption that the user is working with the mouse. That is, we can assume that the user is idle if (and only if) he hasn't used the mouse for a while. As before, we have only one Timeout message, which we've altered to help the user understand how to bring all of those messages back again: <div > Timed out. Please wiggle your mouse to continue.<br/> </div> The key to this pattern is watching for mouse movements on the page and renewing the session when one occurs. Actually, that's easy enough to achieve: window.onload = function( ) { ... document.getElementsByTagName("body")[0].onmouseover = function(event) { renewSession( ); } ... } Calling a function on each mouse movement could get grossly inefficient. In real life, you might want to be just a little more intelligent about this. For example, use some sort of throttling algorithm to ensure that renewSession( ) isn't called more than once every few seconds. renewSession( ) itself works similarly to the preceding warning demo: it hides the Timeout message and kicks off sync and Timeout timers. function renewSession( ) { if ($("timeoutMessage").style.display!="none") { new Effect.BlindUp($("timeoutMessage")); } clearInterval(timeoutTimer); if (!syncTimer) { startPeriodicSync( ); } timeoutTimer = setTimeout(onTimeout, TIMEOUT_TIME); } 17.4.8. Related Patterns17.4.8.1. HeartbeatHeartbeat (see the next pattern) is a companion pattern that helps the server monitor whether the user is still working in the browser. 17.4.8.2. Periodic RefreshFor Ajax Apps, a big motivation for Timeout functionality is the prospect of reducing Periodic Refreshes (Chapter 10). 17.4.8.3. Progress IndicatorProgress Indicator (Chapter 14) is normally used for feedback during an XMLHttpRequest Call. However, another application of it would be to count down toward the Timeout. The display could be present as a permanent fixture, or, alternatively, you could introduce it near Timeout. 17.4.8.4. Direct LoginIf your Timeout requires reauthentication for users to continue working with the system, you probably want to offer a Direct Login (see earlier). 17.4.8.5. Status AreaYou can show Timeout-related messages and countdowns in a Status Area (Chapter 15). 17.4.8.6. PopupYou can show Timeout-related messages and countdowns in Popups (Chapter 15). 17.4.9. MetaphorIf a car engine remains idle long enough, you're probably going to assume that it's dead and give up trying to start it. To keep going would only be a waste of energy. |