You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Mindbridge <mi...@yahoo.com> on 2003/10/05 03:30:21 UTC

Back button (2) -- suggestions for Tapestry improvements

Hi,

I am sorry, I have had little free time in the past week, so I only managed to get to writing this now.


Let me summarize the conclusions from my previous message:

A web application is immune to the Back button side-effects if the actions it performs are only dependent on 
- data that is a part of the requests
- session data that is immutable with respect to the session lifetime

If any other type of data is used, a desynchronization between the state visible to the user and the actual session state can occur and the application may end up performing an action different than the one the user expects.

Needless to say, there are many situations where these two categories of data are insufficient to implement the desired application. The program must often store 'intermediate data' in the session, for example, and it relies on that data being 'fresh' when it has to execute an operation. (please note that Tapestry's persistent properties typically fall precisely in this category). In such cases it is essential for the application to be able to determine whether desynchronization has occurred so that it can take an appropriate action without a risk of undesired behaviour.



Here are some suggestions for improving Tapestry to allow handling of such situations:


1. Encode 'Engine context' in URLs

At the moment the URLs (links) generated by Tapestry encode the following information:
- service name
- service context
- service parameters

We suggest to add 'engine context' to that as well. The engine context is data (e.g. in a map) prepared by the Engine specifically with the purpose of being encoded in the next request. By default the context will be empty, so the URL will be exactly the same as before. 

This is not directly related to the topic, but is a prerequisite for implementing what is needed. It is a very powerful extension and can be used to achieve a number of different other goals as well.


2. Generate a UID representing the Session State at each request and encode it in the URLs to detect desynchronization

The idea here is that if the current session UID is different from the UID encoded in the request, then the Back button (or Refresh, etc) must have been used, and what the user sees on screen is not necessarily what the server thinks the user sees. 
This mechanism allows the application to 'notice' the discrepancy and issue a warning to the user if the data it needs may be "corrupted", because it is not immune to desynchronization (i.e. it is in the session and is mutable). Without that mechanism the application may perform an action that is different from what the user expects.

The implementation of the mechanism can be simple:
- The session state ID needs to be unique only with respect to the current session. Thus, it could just be a simple counter incremented at each request within this session, and encoded in Base64 (or base 80 or any other large base). Since users do not generate that many requests per session, this ID would be two or three characters long at most. (Unlike the session ID, there are no security issues involved either.)
- That ID will be managed by the Engine, and will be a part of the 'engine context' that will be encoded in the URL (see 1)
- Before handling a request the Engine checks whether the session ID and the ID in the request match. There are a number of ways in which the information whether they match or not can be passed to the application. The simplest one is to have a function in the Engine that would allow the application to enquire whether there is desynchronization.
Interestingly, this approach resolves bug 21174.
- It makes sense for this feature to be optional. If the Engine is refactored to allow extensions, all of this could be implemented using an extension. Alternatively, this could be a part of another standard Engine implementation similar to BaseEngine. Users can then choose to use it in their program if they need that functionality (although in almost all cases they would, if a stable and predictable software is the goal)


3. Add a 'store' parameter to the persistent property definition to define whether to store them on the server (in the session) or on the client (in the request)

As mentioned above, persistent properties are particularly vulnerable to desynchronization. It is relatively easy to make an application that uses them freely to behave strangely by some simple use of the Back button (and believe me, users are quite adept at finding those sequences).

Rather than presenting the user with an error message when that happens, an alternative is to store the data in the request (as long as it is not too big) so that the application would work okay even if the Back button is being used liberally. This will be simple to do if the developer can instruct Tapestry to store the persistent property in the request rather than in the session. I would suggest adding to the persistent property declaration the ability to say store='server' (the default) to store the data in the session and store='client' to store the data in the request. As with (2), suggestion (1) makes the implementation of this trivial.


4. Add a 'scope' parameter to the persistent property definition to define how long the property should be remembered.

Currently, if you want to remove the value of a particular persistent property from the session, you can either set it to null, or use forgetPage(). Neither of these can be done, however, if the page in question contains Page links -- it is just not notified that the user has gone somewhere else and hence cannot perform the cleanup. In many (most?) cases you want these properties to be wiped out if you leave the current page.

This is an even more pressing problem if the properties are stored on the client side. While proliferation of properties on the server side is not that critical, proliferation on the client-side leads to longer and longer URLs, containing lots of irrelevant data.

To automate the 'property cleanup' process (and make it more widely practiced) in the typical cases, we could add a 'scope' parameter to the persistent property definition with possible values of 'session' (default -- current behaviour) and 'page' (keep it as long as you are on the same page).


To summarize:
(1) provides the ability to implement a number of new features
(2) allows applications to notice that they are about to perform an action that is different from what the user intends.
(3) allows applications to be coded in a way that is less dependent on whether the user plays with Back or not.
(4) is mostly necessary because of (3), but also simplifies some current operations. 


Comments?

-mb