Categories

Meta

Java, Agile, the Web and other nice things

As you might know, GWT provides a handy and elegant mechanism to manage back and forward buttons (and bookmarking) through the use of the “anchor” part of the URL, that is the part after the # sign. Basically, whatever you put after the # will not trigger a page reload, but can be intercepted by JavaScript.

Using it in GWT is very straightforward:

register a listener to be notified when the context changes (user pressed back or forward or jumped in history) with History.addHistoryListener(listener)

when you want to create a browser history savepoint, call History.newItem(token)

The only thing is, GWT will not generate the token for you, because it has no way of knowing what must be included in the so-called application state. So when you want to manage history with GWT, you should always ask yourself the following questions:

What must be saved? (just the active window? the selected item? etc.)

How will you encode this in a String?

Depending on how complex is your answer to question 1, the answer to question 2 can be very trivial. For example, if all you want to save is the active tab in a multi-tab app, then the contents of the history token can be just an identifier of this tab. Generating it is easy, and so is parsing.

But what if you have more complex data, such as a list of items you want to restore, plus the currently selected item, plus the active view, etc.? I’ve been looking for a generic way to build a history token in such a case for a while. At first I considered a simple list of key/value pairs, such as:

key0=value0; key1=value1

This will certainly work in simple cases; you could write a parser and serializer that takes or generates a Map<String, String> without too much trouble. But then inevitably will come the need to embed another map (or a list) in the map… then the simple case is not so simple anymore.

Anyway, you could imagine handling recursivity with a format like :

key0=(item00, item01), key1={key10=value10, key12=(item120, item121)}

which would be parsed as:

map

key0 -> list

“item00″

“item01″

key1 -> map

key10 -> “value10″

key12 -> list

“item120″

“item121″

you get the picture.

I was set to write a parser for this type of format, and not very happy to have to do so, when it struck me how close this format was to JSON… what if a map was a JSON object, a list a JSON array ? My previous example would then generate the following JSON string:

Given that GWT comes with a JSON parser/serializer, if I can represent my application state as a Map<String, Object> where objects are instances of String, List<Object> or Map<String, Object>, it would be very easy to build a JSON representation of it. Would that work?

The short answer: yes, but there are caveats.

First, since most of the special characters are URL-encoded, it’s not pretty. Actually it’s really ugly. This is what it looks like on an example:

Scary, isn’t it? But, unless you believe every user pays attention to what’s going on in the address bar, it doesn’t really matter.

Second, it’s pretty verbose. The URL can’t grow in length indefinitely, and this burns a lot of precious characters.

Third, it’s not secure, if you use GWT’s native JSON parser that is, because as stated in the Javadoc: “For efficiency, this method [parse] is implemented using the JavaScript eval() function, which can execute arbitrary script. DO NOT pass an untrusted string into this method.”.

The last one is a showstopper for public web sites, unless you reimplement your own JSON parser in GWT. So yes, it works, but no, don’t use it. And if you do anyway, don’t blame me.

If you want some detail here’s how I have done it. I’m in the context of a HMVC app; each controller has a getState() method which is supposed to return a Map representing its state to be saved, and a restoreState() method that takes a Map of the state to be restored. By default getState() returns an empty map, and restoreMap() does nothing, so subclasses are free to override those methods to provide something to save/restore (which might recursively include the state of subcontrollers).

The topmost controller is responsible for handling save state requests; it uses getState() to obtain the global application state, then converts it to a JSON representation, and calls History.newItem().

Conversely, when a history change is detected, the token is parsed as JSON and then converted to a Map. The result is passed to restoreState(), which takes what it needs from the map, does what is needed to restore the state, and recursively calls restoreState() on subcontrollers. How neat is that?

Here’s the code I use to convert my object graph that represents the app state to and from JSON:

Cool, I didn’t know that… but it means my effort of encoding the application state in JSON might not be in vain after all
The problem is, until we can afford to not support non-HTML5 browsers, we’ll have to deal with the old way…

seriously i hope that browser of the future will have very very little back and forward button, it’s really a need? i’m an ajax developer but i impose to my clients to DON’T use the back button: it’s an ajax application not a ’95s website!