Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Monday, April 11, 2005

Property Persistence on the Client

Over the weekend, in between NoFluffJustStuff sessions, I was busy working on adding the 'client' strategy for persistent page properties. In Tapestry 3.0 and earlier, there was a single strategy: persistent page properties are stored into the HttpSession.

Starting in Picasso (4.0), you can specify that a persistent property is stored on the client instead of in the session (on the server). It looks like this:

<property name="foo" persist="client"/>

Once this is in place, and once a change occurs to the foo property, all links and forms in the application will generate additional query parameters (or hidden fields) to track the value of foo. Actually, it uses a single query parameter per page to store all client persisted properties on that page.

It may still need some tweaking; already I can see that the URLs can get very long, very quickly. For example, after setting a couple of properties on a couple of pages of the Workbench:

http://localhost:8080/Palette.direct?component=%24Border.pageLink&session=T&sp=SChart&
state:Fields=H4sIAAAAAAAAAFvzloG1XI6BgYGRgUE8OSczNa8kLDEnMyWxJDM%2FzzUvMSknNaW4iEEwK7E
sUS8nMS9dzyk%2FPyc1Me%2BsQlHD1Tm%2F3jExMEYxsJYl5pSmVhQwAADTbn6TTwAAAA%3D%3D&state:Pale
tte=H4sIAAAAAAAAAFvzloG1nJuBgYGRgYGlOL%2BopLiIQSe%2FKF0vsSAxOSNVrySxILW4pKhSLzk%2Fr6Qo
M0mvIDEntaQkVS8YqNY3PyW1qyv4%2BsQ1f%2FczMTBUFDEoImlNzs%2FNzc8r1stJzEvXS80rzdVzBRI%2FbV
d5bjYMq2JiYPRhYM30S8xNLWEQ8slKLEvUB6nUDwZak5duXVFQwsDq4%2Bjk6gMAGprRD6MAAAA%3D

Given the practical limit of 4000 characters in a URL (don't ask me for references on that), this could be a problem.

The strategies are defined by a configuration point which is, of course, extensible. I expect to add more strategies. For example, a strategy that stores data on the client only until the there is a server-side session, then stores data on the server instead. Or, with some minor tweaks to the APIs, a strategy that store persistent property data, but only inside forms, not inside link URLs.

Drew Davidson has kludged into Tapestry 3.0 some other lifecycle options; he has pages that keep their state as long as the direct service is used, but when the page service is used (to leave a group of related pages), the persistent state for those pages is discarded.

I've been thinking of another option, where different versions of a page's state are stored on the server, and a token comes up in the request to indicate which state is used. This could go a long way towards automatically handling the browser-back button problem ... browser back to an earlier state, and continue working from that state. Actually, properly handling the browser-back button (and perhaps that duplicate form submit scenario) may take a mix of techniques.

9 comments:

Mind Bridge said...

Very nice! Now the fun stuff begins :)

About long URLs: Please see the FormLinkRenderer in the "Useful Classes" section on t-deli -- this resolves the URL limit in the typical case, I believe. Perhaps it should in the framework as well.

Ideally one should be able to select the default link renderer -- Hivemind makes that easy, I think.

Ido said...

Internet Explorer 5 - 5.5 have a max URL length of 2083 characters.
microsoft knowledge base

Great framework Howard. I am eagerly awaiting the first beta release.

Ido

Jonathan Millett said...

Bravo! I've been thinking about this for six months, but couldn't do it myself :-)

Any thoughts on persisting client state in cookies? I can see some benefits: a) cleaner url b) separation of global vs page data c) shared state between all client frames/windows. It might be possible even to implement shared state between a group of pages that share common extra path info in the url.

+1 on the server token. It is the most powerful of all. Even though it requires a session, it would surely be useful in many cases.

Howard said...

Cookies are another possibility. That wouldn't even require code changes; there's already notifications to the page recorders and from there to persistent property strategy services to do this kind of thing. The same technique (same code even) could be used. It would be wedded to the Servlet Tapestry, since Portlet API provides no access to read or update cookies.

That's the fun part of HiveMind; the configuration is open and we'll probably start seeing small plugin libraries to support this kind of thing.

Bill said...

Howard, I am not sure whether you had a chance to look at 'post back' ASP.NET model, but their concept of '_VIEWSTATE' hidden form field, sounds like what you are trying to accomplish here. Maybe worthwhile taking a look and not repeating same mistakes.

Brendan said...

Cheers Howard, that's just the feature I was after. Having this as standard in the framework would be excellent.

I was looking for something similar, when I wished to use an external link to a page with a table component.
Persisting an ID parameter on the URL would allow multiple copies of the page to be opened with different data. Unfortunately using session persistence could cause problem with multiple windows (eg with sorting the wrong data in a windows).

I'd certainly be interested in how your persistent plugin worked, since I had just started looking at persistent strategies in v3.1, after Mind Bridge mentioned them.

Pedro said...

I have thought about implementing the versioned session data but the big problem is data invalidation. This can be a problem with unversioned data, but in this case it's at least possible to reduce garbage.

If you must keep the data "just in case" I don't get how you can avoid a data explosion in your session. I'm really impatient to see that problem solved, from a purely intellectual point of view. And practical, too. If that worked it would be great.

Sardine said...

This can be solved by "client:page" strategy in T4, but what's the equivalent implementation in T5?

Howard said...

As of Tapestry 5.0.5, you can use "client" strategy (equivalent to T4's "client:app" strategy). Over time, we'll reintroduce the other variants.