18.11.2010

GWT: Controlling async processes

In this article, I present a simple class which makes it easy to start async processes, show its state to the user and handle the results.

Perhaps one of the most appealing features of GWT is the handling of async remote calls. It is really easy. Recently I realized the following pattern often used in my apps: Upon a click, an async call is triggered and after that one finished, the user interface is updated. So I have to add a ClickHandler, start the RPC call and specify an AsyncCallback. Simple enough.

On the other hand, I have lots of these interactions. And I want to display a nice little spinning wheel beside the Anchor starting the request as long as it runs. Ah... and I want a server error, if happened, to be displayed beneath that wheel.

Here the game becomes less nice and code less clean. This is why I started organizing it around the following little component:

public abstract class ProgressFlag<T> extends Image implements
HasClickHandlers, ClickHandler, AsyncCallback<T> {

public ProgressFlag() {
super("/progress.gif");
setVisible(false);
}

@Override
public void onFailure(Throwable caught) {
finish();
Window.alert(caught.getMessage());
}

@Override
public void onSuccess(T result) {
onReady(result);
finish();
}

private void finish() {
setVisible(false);
}

public abstract void onReady(T result);

public abstract void onStart();

@Override
public void onClick(ClickEvent event) {
setVisible(true);
onStart();
}
}

As you can see, this little component acts as a ClickHandler as well as an AsyncCallback and redirects to new abstract methods. Now, you can simply implement and use it:

private ProgressFlag<Person> savePerson =
new ProgressFlag<Person>() {






public void onStart() {
          srv.savePerson(person, this);
        }
public void onReady(Person result) {
          Window.alert("Person saved: "+person.getName());
        }
}
...
button.addClickHandler(savePerson);
add(button);
add(savePerson);
So now I only have to state how the call is started and what should happen when it succeeds. Errors are always handled the way I want to and I have my spinning wheel which is an important feature of a UI with asynchroneous RPC.




10.11.2010

GWT Internationalisation Checklist

Follow these steps to internationalize you GWT app and show the language corresponding to the locale the browser sent:


Turn your welcome page to a dynamic jsp by changing your web.xml

  <welcome-file-list>
    <welcome-file>
      your-welcome-file.jsp
    </welcome-file>
  </welcome-file-list>

So rename your welcome page, presumably .html, to .jsp. In the welcome page header, add:

    <meta name="gwt:property" 
      content="locale=<%= request.getLocale() %>">

Now add I18N to your app by adding the following line to your *.gwt.xml:

  <inherits name='com.google.gwt.i18n.I18N'/>

For each locale you want to provide, add another line like these:

  <extend-property name="locale" values="en_US"/>
  <extend-property name="locale" values="de_DE"/>

Now the GWT-App delivered to the client is configured to use the locale sent by her browser.
Next, create an interface extending Messages, e.g.:

public interface Text extends Messages {
public static final Text LANG = 
          GWT.create(Text.class);

String welcome(String name);
}

Now you can externalize your static String. For example, substitute
new Label("Hello "+name);

with
new Label(Text.LANG.welcome(name));

The GWT-magic, on GWT.create provides you with an implementation of your interface that is bound to i18n-property files. So create a File Text.properties in the package of the interface:

  welcome = Hello {0}

You are free to add more resources by providing more methods in your interface and the corresponding line in the properties file. If you want a, say, german translation, create Text_de_DE.properties:

  welcome = Guten Tag {0}

Voilà!

Of course, there are a lot more options in GWT I18N, like setting locales explicitely or use constant string and the like. Therefore do not miss the original documentation.

08.11.2010

What you always wanted to know about: Executors and Thread Pools

Java Concurrency – Part 7 : Executors and Thread Pools: "Let’s start with a new post in the Java concurrency series. This time we’ll learn how to start new threads cleanly and to manage thread pools. In Java, if you have a Runnable like this :


James Sugrue"

03.11.2010

GWT Serialisation of baseclass

Ups - did you know?
Base classes of Serialisable classes have to be serialisable, too!
Consider:

public abstract class Base {
  private long id;
  public long getId() { return id; }
  public void setName(long id) { this.id=id; }
}
public class Person extends Base 
  implements Serializable {
  private String name;
  public String getName() { return name; }
  public void setName(String name) 
    { this.name=name; }
}
Now transferring Person as a parameter to or result of a standard GWT RPC call would result in id==0 at the receiving side no matter what value it had on the sending side.

This was rather surprising to me but simple to repair:
public abstract class Base 
  implements Serializable {
  ...
}

01.11.2010

Google Datastore native API

Recently I ran into the requirement of storing data from a Google App Engine application.

After having studied the docs, wondering a little why the older JDO obviously was preferred over JPA, I was happy seeing my good old APIs turning in. So quickly I turned the datamodel into a JDO-based one, only to run into several heavy problems, e.g. in model unowned 1:n-Relations and such.

I ended up storing only ids in the associations and dereferencing them myself later, and...
Believe me, it was not a good feeling.

So.
After coming to this point again now, I decided to give the native API a try.
And phew - that was a relief.
Only now I learned the the datastore more or less handles persistent hashmaps with keys.
That I was able to understand and handle properly.
Of course I had to introduce a mapping layer here, too. But that one was much more explicit and it is much easier to understand what is going on.
Again I have the feeling that frameworks are my enemies - no, just kiddin'.

But really - it is so simple to store and retrieve data using the low-level API...
Give it a try!