Like Coldfusion only Faster!
   
 

9 Advanced State Management

There are a number of important factors to consider when evaluating what sort of state management to use for your application. Will the application need to be distributed, or does it require bookmarkable URLs ? Will there be multi-page interview processes ? Can you require your users to have client side cookies turned on ? Moto provides built in support for four state management systems :

Stateless : A stateless application requires no information to be maintained in a user's session from page view to page view. This would be the case for static web sites or web sites whose content does not change based upon who is viewing it.

Client-Side : An application that requires knowledge only of who is logged in in order to display information is a good candidate for client side state management. When a user connects to the application a client side cookie is deposited on the user's browser identifying the Session. Subsequent page executions may retrieve information from the Session identified by the cookie. Client side state management is preferable for bookmarkable applications but may suffer from time travel problems and may not be used by browsers that have disabled cookies.

Server-Side : Instead of setting a cookie on the user's browser to identify the Session a state id is included in every link or form submission by using a special state variable sid. The value of this variable changes from page view to page view and identifies not only the Session but the previous State. Through this mechanism state variables are automatically persisted across page views without sufferring from time travel problems. However, since the sid variable is included in every link bookmarking pages of the application is not recommended as the Session may have expired by the time the bookmark is used.

Hybrid Server-Side - Client-Side : In the hybrid system both a client-side cookie and the sid variable are used. A correctly written application can default to Server-Side state management if client-side cookies are turned off. Conversly, only application areas that require the power of parent state identification such as multi-page interview processes need to use the sid. Other bookmarkable pages will be able to identify the correct Session via the cookie.

The type of State management your application uses is determined through a combination of httpd.conf configuration options and coding practices used in writing the application. The following diagram provides a graphical overview of Moto's state management subsystem.

9.1 Stateless Applications

To identify your application as Stateless include the following configuration option in your httpd.conf:

<Application Name>Option Session.Disable TRUE  

Or when running unter the interpreter:

<Location /~dhakim/docs>
   MotoOption ExamplesPath /Users/dhakim/Workarea/content/moto/examples
   MotoOption Session.Disable TRUE
</Location>
 

What this will do is turn off session creation and tracking within the application (or application area) where the directive applies.

Stateless is somewhat of misnomer because States are still created with every pageview. What Stateless applications really lack are Sessions. Calling getSession() on the current state will always return null

9.2 Server-Side State Management

Server-Side state management is available to an application whenever Sessions are enabled. Using it involves coding in such a way that a special State variable sid is passed in every hypertext link, location redirect, and form submission.

9.2.1 The State Variable sid

Every time a user visits a page when Sessions are enabled Moto attempts to discover whether this user already has a Session. A Session in moto doesn't mean that the user is necessarily logged in, just that the user has been interacting with the application on previous page views and has browsed to this page by following links on those pages. When Moto cannot determine the 'current Session' a new Session is created.

States are created on every page view. If Sessions are enabled then when a State is created it is bound to the current Session. The way moto discovers the current Session under Server-Side state management is by looking at a State variable named 'sid'. If it is present in the current State then Moto can derive the current Session from it and bind the newly created State to that Session.

The 'sid' variable's value identifies a State. It is used to identify the parent State of the currently executing page. If it is not present then Moto assumes there was no 'parent State' and spawns a new Session. Otherwise the parent State is retrieved and the Session it was bound to identified. The newly created State is then bound to that same Session. The process perpetuates itself when, on the currently executing page, the new State's own id (returned by the method getSID()) is included in dynamically generated links.

<a href="http://www.webcodex.com/index.moto?sid=$(getState().getSID())>  

location redirects

$(sendRedirect("index.moto?sid="+getState().getSID()+))  

and form submission by passing the sid as a hidden variable

<form action="index.moto" method="post">
  <input type="hidden" name="sid" value="$(getState().getSID())>
  <input type="submit" value="click me">
</form>
 

When the link is followed, the form submitted, or the browser redirected, Moto recognizes the sid, discovers the State it refers to, and from that state discovers the Session.

This mechanism is used to provide continuity to the user experience not only through Session identification but by persisting the values of State variable between page views. State variables in the parent state that do not begin with an underscore '_' are copied into the current state. So if the state variable 'foo' was present in the parent state than it is present in the current state with the same value. It's value changes only when a new value is passed for it. Thus in the login page example from the previous section

<form name="login form" method="post" action="do_login.moto">
  User: <input type="input" name="user">
  Password: <input type="password" name="password">
  <input type="submit" name="_action" value="login">
</form>

 

$switch(getValue('_action',"login"))
  $case(login)
     $if(getValue("user","") eq "bob" && getValue("password","") eq "bobpw")
        $do(redirect("index.moto?sid="+getState().getSID()))
     $else
        $do(redirect("login.moto?sid="+getState().getSID()+"&_err=Password+rejected"))
     $endif
$endswitch
 

The value of the State variable "user" could be retrieved on all subsequent page views. This is handy because it does not require the extra work of inserting it into the Session or promoting its value. There are further benefits which are discussed in detail in subsequent sections.

9.2.2 HTML Forms With Server-Side State Management

Continuing from the previous example of login and do_login pages, a user has just entered a username (bob) and password on the login page, pressed submit taking him to the do_login page where his account was validated and his browser redirected to an index page.

If this were an application that made use of Server-Side state management then on the index page we could write

Welcome $(getState("user"))  

And it will print

Welcome bob  

We could not however print

You clicked $(getState("_action"))  

which would result in

You clicked null  

Since '_action' began with an underscore it's value is not copied to subsequent states and thus available to subsequent page views.

Form submissions account for the way Moto handles State variables with names that begin with '_'. In general:

  • Most values submitted in forms are only needed immediately after the form submission
  • Values from form submissions may be quite large and as such expensive to copy from page view to page view.

Consider a web application that is used for maintaining an employee directory. One could imagine a page containing a form that solicits data for a new hire which, upon submission, is inserted into a database. Let's suppose this form prompts for the employee name, address, and a photo that can be uploaded. All this data will be submitted to moto as state variables. The page that handles this submission will insert it into the database and then return to the index where upon the user can choose to add another employee or browse through other application pages. Either way this data is unnecessary, and, with regard to the employee photo, potentially quite large. We would definitely not want it copied with every page view.

When the Session.Disable option is present, all state variables are free'd after page execution. In Stateless applications the variable sid is meaningless and not prefixing state variables with an underscore has no effect. They all go away at the end of the page view to which they were submitted.

But why copy state variables, why not just keep one value around for every state variable name used in a user's session? The answer to this question requires some background...

9.2.3 Time Travel

When building web applications it is useful to diagram them out in Application Flow Diagrams. These diagrams map out the pages of a web application with arrows between them illustrating the links that a user could follow to transition from page to page. A simple application where users may view or modify a 'user profile' might be diagrammed as follows:

A user's activities in a web application can be viewed as a traversal of the transition diagram.

State 1 2 3 4 5 6 7 8
Parent State none 1 2 3 4 5 6 7
Page 1 2 1 2 3 4 5 4

For the traversal above the user forgot his password the first time and was sent back to the login page to start again. This is clear by comparing the pages the user visited to the transitions in the app flow diagram. The user then went on to the index page, viewed his profile, and successfully edited it. But what could we conclude happened if we then saw

State 9 10 11 12
Parent State 1 9 10 8
Page 1 2 3 5

The user backed up to the login page, openned up a new browser window and logged in as (potentially) a different user in it. Then went back to the original window and followed the edit profile link! If the only mechanism being used to determine what user profile to edit was based upon an identifier tied to the Session then the user whose profile will be edited in the first window will be the user who is logged in in the second. Another way to look at this is that the actions the user took by opening up a second window and logging in as a different user affected the behavior of a link that was generated previously. This is known as a time travel problem.

Implications of this sort of problem are

  1. Using the back button on applications that make extensive use of Session objects or (or Client-Side State management) may be unsafe.
  2. Following links by openning them up in new windows may effect the ability to follow links in other open windows.
  3. Session objects are tricky things that must be used with care.

Use of Session objects is the most common cause of unexpected time travel problems. If the 'current user' in the application diagrammed above was stored as a persistent State variable there would be no confusion as to which 'user' was being edited in which window. If you believe that the above example is somewhat contrived consider the role of an HR administrator who may be responsible for updating the profiles of multiple employees ... this sort of thing happens all the time.

One area where this comes up a lot is in relation to multi page interview processes. In these sorts of cases users are asked to enter information through a series of interactions with a web based application. In general the information is persisted in memory until it is ready to be stored. An example of this might be web page building application where fields of a web page template get filled in over successive page views

State 1 2 3 4 5 6 7
Parent State none 1 2 3 4 5 6
Page 1 2 3 2 3 2 3

In the above traversal the user has selected a page to edit and is entering data into each of it's fields

State 8 9 10
Parent State 1 7 9
Page 2 2 1

It seems as though the user has gone back to the directory screen by using the back button on his browser and selected a new page in a new browser window (perhaps to copy some information out of that page into the page he's working on). Back in his working window he then alters the field and then saves the page. But which page did he alter? If the data for a page is stored in a Session variable named 'page' for instance then the very act of opening up a second page in another window has undone all the users work.

9.3 Client-Side State Management

So now that we are aware of the hazards of Session objects because of time travel problems, one might ask why we should ever consider a State Management methodology where one is forced to use Sessions to persist data between page views. The biggest reason may be bookmarkability. Including the state variable sid in every URL means that bookmarking generated URLs may be useless or potentially dangerous if the link is distributed to others.

With pure Client-Side state management a Cookie named ssid is deposited on a users browser after his first page view against an application. This Cookie does nothing more than identifies the Session. If on subsequent page views Moto retrieves this Cookie then Moto can discover the current Session. If the cookie is not included within the HTTP GET or POST then Moto assumes there is no current Session and spawns a new one.

You can get this behavior by including the option

<Application Name>Option Session.SetSSIDCookie TRUE  

Or if using the interpreter use the following moto option in the location you want it applied:

MotoOption Session.SetSSIDCookie TRUE  

The lifetime of the ssid cookie is the same as the lifetime of the Session which is specified in seconds via the Moto option

MotoOption Session.Timeout 60  

The default value for this option is 1800.

The downsides of this system apart from the time travel phenomina we've already looked at are :

  • Some users may have client side cookies disabled
  • It becomes impossible to maintain multiple Sessions at once against the same application under one browser (sometimes this makes testing difficult)
  • State variables are not persisted meaning that all data for a users session must be promoted and stored in the Session

9.4 Hybrid State Management

The best State management systems for web applications are hybrid systems. They are also the most complex to implement. The way a hybrid system is implemented in Moto is by including the Session.SetSSIDCookie option in your httpd.conf file AND including the sid variable on all pages that could benefit from it and do not need to be bookmarked OR in every generated URL in cases where client side cookies are turned off.

You can discover whether cookies are working or not by trying to getCookie("ssid") on a page that expects it. If it is not found, include the State variable sid on all generated URLs.

Simply by enabling the SetSSIDCookie option on applications that are basically Server-Side State managed adds an extra level of security. If the Session identified by the sid and the Session identified by the ssid do not match then a new session is spawned.