Google Web Toolkit
September 1st, 2007 by SamWe recently discovered the wonderful world of the Google Web Toolkit. In a nutshell, GWT lets you write Javascript/AJAX applications entirely in Java. You write client code in a subset of Java 1.4, making use of a really big library of existing widgets, which is then compiled into Javascript. Server side uses the standard Servlet architecture in your preferred version of Java, if you even want a server side piece. (The next version will support Java 5 on the client side)
In this article, I’ll show the code for a little auto-suggest widget that speaks to a Servlet which I wrote in 30 minutes. It is designed to be reused.
You never need to write any Javascript, with layout being defined in pure XHTML/CSS (unlike JCP). AJAX (i.e. communication with a server) is handled almost transparently… in the client side, create an interface extending
and implement it on the server side, whilst extending the much cleaner wrapper to javax.servlet.Servlet
An asynchronous version of the RPC is created according to the RPC Documentation. Serialisation to/from Java/Javascript objects is handled transparently. It’s amazing.
During development, GWT comes with its own mini-tomcat meaning that you don’t need to worry about configuration… you just click Run and it compiles the client code and starts servlets on a local port. Debugging is fully supported through your usual debugger (e.g. Eclipse) to allow for stepping. It’s almost worthwhile setting up a dummy client just to test Servlet code!
GWT comes with an auto-suggest widget, but it is entirely client side. I simply wrote an alternative that speaks to the server. I posted the code to the GWT Group. It would be a very useful widget to use for search term completion (like the Google toolbar).
Client-side Walkthrough
I won’t go into the details about how the ServicedSuggestOracle class works… but I’ll show how it is used. GWT comes with 2 scripts… one for creating an Eclipse project and another for adding applications within a project. The details are on the developer docs so I won’t repeat them here.
We begin by creating HTML elements for the history you see on the right and a debugging area, which hopefully you didn’t experience!
final HTML history = new HTML();
final HTML debugging = new HTML();
We then load and setup the suggester
SuggestOracle oracle = new ServicedSuggestOracle("complete", 3);
final SuggestBox completer = new SuggestBox(oracle);
completer.setLimit(6);
final String HELP = "Type here";
completer.setText(HELP);
The parameters to ServicedSuggestOracle are the Servlet name and minimum number of characters before completing. In order for the Hosted Mode to work during development, you will need to add the Servlet into the project’s .gwt.xml file
<servlet path='/complete' class='javablog.completer.server.CompleterServiceImpl'/>
We want to add some code that will capture the choice a user makes, this does it…
completer.addEventHandler(new SuggestionHandler() {
public void onSuggestionSelected(SuggestionEvent event) {
String text = history.getHTML();
history.setHTML(text + "<br />" + completer.getText());
completer.setText("");
completer.setFocus(true);
}
});
and to always fill the box with the help text when focus is lost, this is what is needed…
completer.addFocusListener(new FocusListener() {
public void onFocus(Widget sender) {
completer.setText("");
}
public void onLostFocus(Widget sender) {
completer.setText(HELP);
}
});
There are many ways to attach GWT widgets to HTML, but I prefer to give some div elements unique IDs and then explicitly attach the widgets using
RootPanel.get("completer").add(completer);
RootPanel.get("history").add(history);
and that’s it!
Server-side
On the server, I implemented ServicedSuggestOracleService which I defined in my ServicedSuggestOracle source, the class extends RemoteServiceServlet. In this case we take a String and return a List<String>. In the more general case, I find it useful to define an IsSerializable container, using the appropriate annotations (@gwt.typeArgs <type>) for elements with generic type.
In this particular case, I discovered a really nice method called SortedSet.subSet. Keeping all movie names in a case-insensitive TreeSet means I can use subSet from the request string and the request string + "ZZZ". This then uses binary search to give very fast performance at searching through the millions of strings. I then rank the movies based on their popularity.
The bad news: Tomcat
Unfortunately, final deployment of server-side means you need to get acquainted with Tomcat (or another Servlet server)… which can be nasty. You basically need to add a WEB-INF/lib directory to your public folder, and a web.xml file in the WEB-INF directory. Chad Bourque has been writing a tutorial on creating a WAR file for deployment to Tomcat, but there is a lot of scope for automation here. Don’t forget that Eclipse allows you to save the settings for a jar file, so you don’t need to choose the correct classes every time.
A little bit of an inconvenience
Unfortunately, GWT comes as a platform-dependent download. But it is possible to setup it up for cross-platform version control. We have a folder where we place third party jars. In this folder we create a folder named gwt which is added to the svn:ignore list. Everybody puts their own version of GWT here and then creates a symlink from gwt-dev.jar -> gwt-dev-{windows, mac, linux}.jar. This means we can link to the platform independent location gwt-dev.jar in Eclipse. The Javadocs are also missing the Servlet classes, which you can grab on the Java Community Process and extract in the documentation directory for the full IDE experience. If you’re running 64 bit Linux, you must download a 32bit JRE and run GWT in that.