About Google Wave’s end

You probably know that Google has recently announced that they were stopping development for Google wave, and indeed after the initial buzz and all the hype that surrounded wave when it was launched, interested faded rapidly.

Why did that happen ? I mostly agree with this post: Why Google Wave “failed”.

When users need to say things like “To understand what Wave is, you have to either use it or watch the video; it’s impossible to explain“, you should know that something is wrong with your product. To survive and proliferate, products need a clear purpose that everyone can understand.

It doesn’t mean that Wave’s purpose was not smart or that it didn’t have any usefulness, but it could mean that Wave was too much in advance for its time. There are a lot of examples of inventions that didn’t prove successful when they were first published, but only later when the time was ripe for it. If you tried to explain what Twitter is no more than 20 years back, I bet you’d have the same difficulties in communicating Twitter’s purpose than Google had trying to explain what Wave was.

E-mail is so popular because it’s simple: self contained messages sent back and forth between individuals, forming conversations. But these conversations have limits that Wave tried to lift: not real-time, no central archiving, no plugin, little automation… unfortunately, a wave is both too similar and too different from an e-mail conversation for most people to see a clear benefit.

But this alone can not explain Wave’s lack of success; there were also some bad choices in UI design. Google is (was?) well known for its spartan yet functional UI design, which is exemplified in Google Search for example or GMail. No fancy animations or drag and drop or big icons, just functional buttons, at the exact right position, with the exact right size and font so that you’re not distracted from your goal.

Wave was the exact opposite: unintuitive and confusing interface, with minimizing  frames (why on earth?), trying too hard to unify concepts (my settings is a wave? come on!). I’m sure that after receiving their long-awaited invitation, most users connected once, tried to figure it for a while and then gave up and never came back. I’d be curious to know the stats…

Anyway, I’m sure that Wave will resurface in one form or another, because it is a response to one of the biggest challenges of communication these days: the scattering of information and ways to communicate. The need is there, just waiting for the right product when the time is ripe…

1 Comment

Testatoo: TDD for the GUI

Test-driven development is a good thing. We all agree on that, right?

But TDD assumes you can write tests before actually implementing the functionality. How do you do for the GUI when most GUI test tools are based on a scenario-recording appraoch? This requires that the GUI already exists; in other words, another instance of the “chicken or the egg” dilemma…

My friend David Avenante (and host in Montréal for the confoo.ca 2010 conference), has been working on a solution to this problem called Testatoo.

From the web site: “Testatoo provides on one hand an abstraction of the UI business domain through an expressive API and on the other hand a way to express this domain via a DSL (a button semantically stays a buttons whatever the technology). With Testatoo you can therefore write tests with a seldom achieved level of expressiveness and make these tests INDEPENDENT of the underlying technology.”

Interested? The testatoo web site is now up, with tutorials, demos, samples, FAQ, and a forum ! Give it a try, and post your feedback…

, , , ,

No Comments

Slides from my GWT2 presentation at YaJUG

,

2 Comments

GWT: using JSON as history token?

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:

  1. What must be saved? (just the active window? the selected item? etc.)
  2. 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:

{“key0″:["item00"," item01"], “key1″:{“key10″:”value10″, “key12″:["item120"," item121"]}

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:

MyPage.html#%7B%22active%22:%222d22aa%22,%20%22selected%22:%222dff8%22,%20%22workFolder%22:%5B%222be34%22,%222dff8%22,%22414a4%22%5D%7D

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:

    /**
     * Serialize the specified object as a JSONValue. The object to serialize must be an instance of:
     * - String
     * - Map<String, O>
     * - List<O>
     * <p/>
     * where O has the same constraints.
     */
    public static JSONValue serializeAsJson(Object object) {
        if (object instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) object;
            JSONObject jsonObject = new JSONObject();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                JSONValue convertedValue = serializeAsJson(value);
                jsonObject.put(key, convertedValue);
            }
            return jsonObject;
        }
        else if (object instanceof List) {
            List list = (List) object;
            JSONArray jsonArray = new JSONArray();
            for (int i = 0; i < list.size(); i++) {
                Object entry = list.get(i);
                JSONValue convertedValue = serializeAsJson(entry);
                jsonArray.set(i, convertedValue);
            }
            return jsonArray;
        }
        else if (object instanceof String) {
            return new JSONString((String) object);
        }
        else {
            throw new RuntimeException("Unsupported state type: " + object.getClass());
        }
    }
 
    /**
     * Parse a JSONValue as follows:
     * -if the value is a JSONObject, parse into a Map<String, Object>
     * -if the value is a JSONArray, parse into a List<Object>
     * -if the value is a JSONString, parse into a String
     * -otherwise fail
     */
    public static Object parseObject(JSONValue jsonValue) {
        if (jsonValue.isObject() != null) {
            return parseMap(jsonValue.isObject());
        }
        else if (jsonValue.isArray() != null) {
            return parseList(jsonValue.isArray());
        }
        else if (jsonValue.isString() != null) {
            return jsonValue.isString().stringValue();
        }
        else {
            throw new RuntimeException("Failed to parse JSON: " + jsonValue.toString());
        }
    }
 
    /**
     * Parse the specified JSONObject into a Map<String, Object>. The JSONValue associated to
     * a key is parsed recursively using {@link #parseObject(JSONValue)}
     */
    public static Map<String, Object> parseMap(JSONObject jsonObject) {
        Map<String, Object> result = new HashMap<String, Object>();
        for (String key : jsonObject.keySet()) {
            JSONValue jsonValue = jsonObject.get(key);
            Object convertedValue = parseObject(jsonValue);
            result.put(key, convertedValue);
        }
        return result;
    }
 
    /**
     * Parse the specified JSONArray into a List<Object>. The JSONValues contained in the array are
     * parsed recursively using {@link #parseObject(JSONValue)}
     */
    public static Object parseList(JSONArray array) {
        List<Object> result = new ArrayList<Object>();
        for (int i = 0; i < array.size(); i++) {
            JSONValue jsonValue = array.get(i);
            Object convertedValue = parseObject(jsonValue);
            result.add(convertedValue);
        }
        return result;
    }

,

3 Comments

Google and Palm: the ideal scenario

It was suggested by Phil kearny (ex Apple employee) that Google should buy Palm…

Let’s dream a little bit:

  1. Google buys Palm
  2. A GWT wrapper is developper for Mojo (Palm’s Javascript SDK). GWT becomes the official SDK for webOS
  3. dalvik is ported to webOS. All the apps from the Android marketplace are now available for webOS devices
  4. Android and webOS merge. Android 3.0 runs both dalvik apps and Mojo/JavaScript/GWT apps
  5. Google releases the most amazing smartphone ever, the Nexus Ultimate. The iPhone is now a thing of the past.
  6. Apple open sources the iPhone SDK, allows Flash on their mobile devices, opens up the AppStore for all apps, and goes back to what they do best: desktop and laptop computers, with a reliable UNIX-based OS.

PS: after I wrote this post, I found that some rumors were spreading about Palm moving to Android… I promise I’m not the one who wrote the fake “anonymously sourced, unconfirmed memo” ;)

, , , ,

6 Comments

Tim Bray now a Google employee

For those of you not familiar with the Who’s Who of IT, Tim Bray is best known for the central role he played in the initial specification of XML. I should know because I spent countless hours studying it back in 2000. If you really really have to read it too, I urge you to read instead the annotated XML specification, with invaluable insight from said Tim Bray. Almost entertaining compared to the plain spec…

Anyway, Tim joined Google with focus on the Android platform. I’m glad to see that we share the same feeling about the iPhone and Apple’s AppStore policies:

The iPhone vision of the mobile Internet’s future omits controversy, sex, and freedom, but includes strict limits on who can know what and who can say what. It’s a sterile Disney-fied walled garden surrounded by sharp-toothed lawyers. The people who create the apps serve at the landlord’s pleasure and fear his anger.
I hate it.

While I don’t have much hope that this reality will ever bother the vast majority of iPhone users, I strongly hope that more and more developers move away from the iPhone because of the way Apple treats them.

I’m sure Tim will add to the reasons to move to Android…

, ,

No Comments

Slides from my presentation at confoo.ca

, , ,

No Comments

Is HTML5 Ready Yet?

Just attended Mark Pilgrim’s talk this morning at confoo.ca about HTML5.

I’ll spare you the list of novelties and changes… HTML5 actually does two things:

  • solves a lot of the current problems with HTML and AJAX apps that required ugly Javascript hacks work around (like initial focus, empty text, typed form fields, etc.)
  • brings new functionalities much required for new generation RIA apps (geolocation, storage, offline, video/audio support, etc.)

Apparently it does that very well, allowing compatibility with older markup when possible, complying with standards… and HTML5 is already there in the latest versions of Chrome, Safari, Firefox and Opera.

Google is obviously pushing hard for HTML5, and the not-so-hidden goal is to get rid of the Flash plugin. Mark said several times during his presentation “look, no Flash!”… With Google and Apple openly trying to push Flash out of the picture, Adobe has a lot to do :)

BTW check those links:

PS: liveblog of Mark’s session

No Comments

Smart GWT 2.1 has just been released.

If you don’t know yet, SmartGWT is a cool GWT tookit made by Sanjiv Jivan (the guy who wrote GWT-ext). It is primarily intended to build SmartClient frontends, but it is also available standalone under LGPL license. SmartGWT is fast becoming a strong alternative to GWT-ext (stuck because of license issues) and Ext-GWT (very buggy and full of bad design choices).

SmartGWT 2.1 is out with lots of novelties.

SmartGWT on Google Code

2 Comments

EricDaugherty.com: Does Write Once Run Anwhere Work?

Eric Daugherty:

Realistically, I think tools like GWT are the future. As a Flex developer, I enjoy the ability to quickly and easily create rich applications that will render the same on ever user&apos;s machine. But I would prefer that the Flex applications would compile to HTML and JavaScript, so they could be run native in the browser.

via EricDaugherty.com: Does Write Once Run Anwhere Work?.

, ,

No Comments