background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

1 of 8

2006-06-02 16:36

    

 

Published on 

ONJava.com

 (

http://www.onjava.com/

)

 

http://www.onjava.com/pub/a/onjava/2006/05/31/working-with-google-web-toolkit.html

 

See this

 if you're having trouble printing code examples

Working with the Google Web Toolkit

by 

Robert Cooper

05/31/2006 

The Google Web Toolkit

 (GWT) was rolled out for JavaOne 2006 at an innocuously titled session. Due to

what I assume was a timing miscommunication, the Google Blog scooped the presentation, but the impact
was no less felt.

GWT is, in essence, a JavaScript generator. The interesting thing is what this JavaScript is generated 
from: Java. GWT takes Java code written against a special API and converts it into browser-runnable 
Ajax code. If that weren't enough to make it cool, it also includes a test harness (see Figure 1) that will 
execute the Java code inline with a test browser, allowing you to step-through debug (see Figure 2), 
profile and unit test your Ajax front end in your favorite IDE or at the command line.

Figure 1. GWT includes a console window with hierarchical logging.

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

2 of 8

2006-06-02 16:36

Figure 2. Java execution in the test browser allows for step-through debugging of code.

As they say, "But wait! There's more!" GWT also includes a basic RPC framework for making calls back 
to your server application in the form of a servlet. The 

RemoteServiceServlet

 allows you to implement 

service methods on an implementation and expose these to your generated Ajax application. This is done 
by simply adding new 

public

 methods to the servlet, and the GWT will handle serialization and 

deserialization to and from the client. How much would you pay for a framework like this? Don't answer 
yet!

GWT also includes a set of core DOM mapped classes and layouts that are more familiar to desktop 
application developers, such as the 

dock panel

 and 

horizontal panel

. Prefabbed widgets such as 

PopupPanel

 for context popups, 

DialogBox

 for popup blocker friendly dialogs, and 

StackPanel

 for 

Outlook-bar style displays. There is also a URI and History management system for maintaining a 
bookmarkable and "back buttonable" state through your application.

Getting Started

GWT is really just an executable Java application with some hooks into 

Tomcat

 and either Mozilla or 

MSIE under Linux and Windows, respectively (sorry Mac users, you are out of luck for now). When you 
download the GWT, it comes with several sample projects, including "KitchenSink," the GWT equivalent
of SwingSet. You can run the batch file in the samples/KitchenSink directory to see it run. Two things 
will pop up: the GWT server monitor application and browser window where you can see the application 
run. Executing 

java com.google.gwt.dev.GWTShell --help

 with your platform's *dev.jar and 

gwt-user.jar will show you the options available:

Google Web Toolkit 1.0.20
GWTShell [-port port-number] [-noserver] [-logLevel level] 
    [-gen dir] [-out dir] [-style style] [-notHeadless] [url]

where 
  -port         Runs an embedded Tomcat instance on the specified
                port (defaults to 8888)
  -noserver     Prevents the embedded Tomcat server from running,
                even if a port is specified
  -logLevel     The level of logging detail: ERROR, WARN, INFO, 
                TRACE, DEBUG, SPAM, or ALL
  -gen          The directory into which generated files will be
                written for review
  -out          The directory to write output files into 
                (defaults to current)
  -style        Script output style: OBF[USCATED], PRETTY, 
                or DETAILED (defaults to OBF)
  -notHeadless  Causes the log window and browser windows to be
                displayed.  Useful for debugging.
and 
  url           Automatically launches the specified URL

Google includes an 

applicationCreator

 script that will generate a set of batch files, scaffolding classes, 

and directories for a new GWT project. For our purposes here, however we are going to bypass this and 
start from scratch. First you will want the GWT Maven plugin installed. You can install this by unzipping

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

3 of 8

2006-06-02 16:36

the plugin project and typing 

maven plugin:install-now

. Also, you will want to copy the gwt-user.jar

to ~/.maven/repository/com.google.gwt/jars/ manually. Since this JAR needs to be included in your final 
WAR file, it needs to be registered as a dependency in the Maven POM file.

Related Reading

Head Rush Ajax

By

Brett McLaughlin

Table of Contents
Index
Sample Chapter

Project Basics

GWT uses a basic structure that might seem a little alien at first, so let's take a quick tour. The code we 
will be using is available as a ready-to-run application in the 

Resources

 section. It provides an 

implementation of a simple Table class that will toggle a CSS element on a selected row and an RPC call 
to fetch information for the table from the server.

First is the [Module].gwt.xml file. This is an XML marker in your project that maps to an 

EntryPoint

class for a particular module. Think of a module as being a self contained display, like a 

JFrame

. In 

"Table.gwt.xml," our module is as follows:

<module>

 <!-- Inherit the core Web Toolkit stuff.                  -->
 <inherits name='com.google.gwt.user.User'/>

 <!-- Specify the app entry point class.                   -->
 <entry-point class='com.totsp.gwt.client.TableEntryPoint'/>
  
</module>

Next, to start up our module, we need a root HTML document to run. Here we are using 
src/java/com/totsp/gwt/public/Table.html. To embed your entry point into your HTML document, you 
need to include two things. First, add a 

meta

 tag that indicates what the module you want to include is:

<meta name='gwt:module' 
    content='com.totsp.gwt.Table'>

Second, at the point in the HTML where you want the GWT to start rendering, you include the gwt.js
boilerplate JavaScript file:

<script type="text/javascript" 
    src="gwt.js"></script>

This will then run a default script and inspect the 

gwt:module

 value to decide what it needs to start up. 

Also notice that you place your HTML host page, CSS files, and graphic assets in the 

public

 package of 

your Java source tree, not in the web app. This is a little counter-intuitive, but the 

GWTShell

 and the 

GWTCompile

 steps expect to load them out of here. You can't, however, really put them in the web app 

folder, because GWT will clobber them with defaults if you do.

Pretty simple so far. Now we need to start actually writing our classes. The first one is our 

EntryPoint

implementation. This is what actually starts rendering stuff.

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

4 of 8

2006-06-02 16:36

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class TableEntryPoint implements EntryPoint {
    
  private Table table;

  public void onModuleLoad() {
    table = new Table( null, "myTable" );
    table.setStyleName("myTable");
    RootPanel.get().add(table);
    getData(); //just ignore this for the moment.
  }

Looking over these classes, we have a few things here. First is the 

Window

 class. This maps, roughly, to 

what you would think of as the JavaScript 

window

 object. It exposes 

alert()

 and 

getClientWidth()

among other things. The 

RootPanel

 is a singleton class that returns the root for placing widgets via 

get()

. This singleton methodology is pretty convenient for working with, so we are going to add this to 

our classes. Next is 

Widget

. This is the base class for widgets and is used for event handlers, such as the 

following from the "Hello" example:

 Button b = new Button("Click me", new ClickListener() {
  public void onClick(Widget sender) {
    Window.alert("Hello, Ajax");
  }
});

To start up our 

EntryPoint

 class, we need to override the 

onModuleLoad()

 method with our code to build

our display. For the purposes of this article, we are only going to use our simple 

Table

 class.

The 

Table

 class contains a very simple operation. It extends 

FlexTable

, then adds itself as a listener to 

itself. When a table row is clicked that isn't the header row (if there is one), it adds a CSS style name 
postfixed with 

-selected

 to that row. Everything else is done in the CSS.

public void onCellClicked(SourcesTableEvents sender, 
            int row, int cell) {
    this.getRowFormatter()
        .removeStyleName(selectedRow, selectedStyle);
    if ((source.getHeaderRow() == null) || (row > 0)) {
        this.getRowFormatter()
            .addStyleName(row, selectedStyle);
        selectedRow = row;
    }
}

The table also has a method for populating data from a 

TableDataSource

. Let's look at that next.

Building the Table

Our table class here just does a pretty simple thing: draw a table from a 

TableDataSource

implementation, then toggle a stylesheet class on the selected row so we can highlight it. To do this, we 
are going to extend the GWT's 

FlexTable

 class. This is a class that represents an HTML table, but lets 

the author create table cells on the fly. To populate our table we are going to use a very simple 

TableDataSource

 that we can fetch from our RPC servlet. For now, lets just look at the code.

The first step is to build your service interface. Ours is pretty simple:

public interface DataService extends RemoteService {    
    public Person[] getData();
}

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

5 of 8

2006-06-02 16:36

Next, we need to create an 

Async

 interface for the service. This is done entirely with naming conventions.

You need to create an interface that matches the service interface and all methods. However, instead of 
return types, every method is 

void

 and the last argument is an 

AsyncCallback

 implementation, thusly: 

public void myMethod( Param p..., AsyncCallback callback)

. Finally, the interface class name 

should match the 

RemoteService

 interface name, postfixed with 

Async

.

For our simple service, it looks like this:

public interface DataServiceAsync {
    public void getData(AsyncCallback callback);   
}

Next, you need your service implementation. This needs to extend the GWT's 

RemoteServiceServlet

servlet and implement your service interface.

public class DataServlet 
    extends RemoteServiceServlet implements DataService {
    
    public Person[] getData(){
    //...
    }
}

Whew. Almost there. Now we want to add the servlet to both the web.xml (so it will be there when we 
deploy the war) and we want to add a declaration to our Table.gwt.xml file. This second entry tells the 
GWTShell to load the servlet and also mounts it in the testbed Tomcat and generates client stubs for you.

<servlet path="/DataService" 
    class="com.totsp.gwt.server.DataServlet"/>

Now, we are ready to deal with the call itself. GWT only supports Asynchronous calls. There are a 
number of technical reasons for this, notably that the most obvious path to do a synchronous call would 
be to spin waiting for the response. Unfortunately, many browsers will not actually fire the 

XmlHttpRequest

 event until said spinning is done. But hey, this is Ajax, not Sjax. Right?

Now we add the 

getData()

 method to our 

EntryPoint

 class. To do this, we need to get a 

DataServiceAsync

 implementation from the 

GWT

 class, then create an 

AsyncCallback

 handler. This is a 

simple interface that just has an 

onSuccess()

 and 

onFailure()

 method to handle the return state of the 

callback.

private void getData(){
    DataServiceAsync dataService = 
       (DataServiceAsync) GWT.create( DataService.class );
    ServiceDefTarget endpoint = (ServiceDefTarget) dataService;
    endpoint.setServiceEntryPoint("/DataService");
    dataService.getData(new AsyncCallback(){
        public void onSuccess(Object result) {
            table.setSource( 
                new SimpleDataSource( (Person[]) result ) );
        }

        public void onFailure(Throwable caught) {
            Window.alert("Unable to get data from server: "
                +caught.toString());
        }

    });

}

Notice the unfortunate cast to 

ServiceDefTarget

. Why they couldn't have made a 

GWT.create(Class 

clazz, String url)

 method is a bit beyond me, but you gotta do it.

Now, we simply call the 

setSource()

 method on our table object and voila, the table will render itself 

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

6 of 8

2006-06-02 16:36

with the new data. Also notice that our objects that move from the server to the JavaScript world have to 
implement 

IsSerializable

. This also supports arrays and basic collections, although in my experience, 

collections are sometimes sketchy and may give you cryptic errors. It's best to avoid them where possible 
for the moment.

Revisionist History

As anyone who has used (most) Ajax or RIA apps knows, the back button can be the bane of your 
existence. Fortunately, GWT includes a means of dealing with it. The 

History

 class allows you to store a 

single 

String

 token that you can use to capture the display state and redraw as needed. This is 

accomplished by inserting the token into the anchor part of the URL on the page and writing the 

location.history

 as needed.

Let's modify our simple example to support the back button. First, we want to capture state changes and 
store them in the 

History

 object. To do this, we will add a new 

TableEventListener

 to our table in our 

onModuleLoad()

:

//Add a new listener to record the row history
table.addTableListener( new TableListener(){
    public void onCellClicked(SourcesTableEvents sender, 
        int row, int cell) {
        History.newItem( ""+row );
    }
});

Now, we want to capture changes to the back button and update our selection on our table appropriately. 
So we will make our 

EntryPoint

 a 

HistoryListener

:

public class TableEntryPoint 
    implements EntryPoint, HistoryListener {
//...

    public void onHistoryChanged(String historyToken){
        table.onCellClicked( table, 
            Integer.parseInt( historyToken ), 0);
    }

Now, whenever you click the back button, it will track back to the last selected item. The last step is to 
make sure our initialization works in case the page is bookmarked or gets a hard reload. To do this, we go
back to our 

getData()

 method on the 

EntryPoint

 and modify the 

onSuccess()

 handler of the 

AsyncCallback

. This will check to see if there is a stored token in the History object and reset the state of 

the table to the selected row stored in the history token.

public void onSuccess(Object result) {
    table.setSource( new 
        SimpleDataSource( (Person[]) result ) );
    if(History.getToken().length() != 0){
        table.onCellClicked( table, 
            Integer.parseInt( History.getToken()), 0);
    }
}

The final step is to go back and modify our HTML host page with a hidden iframe that the GWT script 
uses for history manipulation:

<iframe id="__gwt_historyFrame" 
    style="width:0;height:0;border:0"></iframe>

This is a pretty primitive example, but hopefully you understand enough to make use of the History class 
in a more complicated application. Figure 3 shows the sample application's table display.

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

7 of 8

2006-06-02 16:36

Figure 3. Our finished table widget in the GWT browser.

That's Not All!

There is still more coolness in the GWT. There is still more not covered here, included a JavaDoc 
based-annotation system for typing collections (Java SE 5 generics aren't supported for 

IsSerializable

), 

JUnit

 testing frameworks, and lots of cool widgets to use and extend. Also, be sure to check out the GWT 

provided examples. Although I admit I find some of the programming idioms a bit strange -- and it may 
simply be my history as "Not a GUI developer guy" -- they definitely give you a feel for what can be 
done with the framework. In fact the DynaTable example is a much more robust version of what we have 
built here.

The Maven Module

The GWT Maven module provided with this article gives you a few options for working with the Google 
Web Toolkit in a Maven project. You will notice in the sample code provided the following set in the 
project.properties:

google.webtoolkit.home=/home/cooper/local/gwt-linux-1.0.21
google.webtoolkit.runtarget=com.totsp.gwt.Table/Table.html
google.webtoolkit.compiletarget=com.totsp.gwt.Table
google.webtoolkit.logLevel=DEBUG

The first line is the path to your GWT installation. The second is the target you want the GWTShell to 
start with when you call the 

gwt

 goal. This will run the shell and open the test browser. 

compiletarget

 is 

the module declaration you want to use when you call 

gwt:compile

. Finally, 

logLevel

 is the log level to 

show in the GWTShell when you are testing. You can look at the Mevenide properties panel in your 
project properties to see the rest of the settings. They pretty much map one-to-one with the command-line
arguments for GWT.

The final target is 

gwt:debug

. This will start the GWTShell in debug mode allowing you to step trough 

your Java code. Once you call this target, the debugger will come up and wait for a connection before 
anything is rendered. In NetBeans, you can click on "Attach To Debugger," select your local machine, 
and port 8888 to connect. You can then set breakpoints in your code and move through the web app in the
debug browser! This is by far the greatest feature of the GWT. Debugging JavaScript -- even with 

Venkman

 -- always felt a little lacking. GWT lets you keep using the same tools you're used to with no 

changes.

Lastly, you want to make sure you have a 

gwt:compile

 executed before your 

war:war

 Maven goal. You 

background image

ONJava.com: Working with the Google Web Toolkit

http://www.onjava.com/lpt/a/6611

8 of 8

2006-06-02 16:36

can accomplish this by adding in the project 

maven.xml

:

<preGoal name="war:war">
 <attainGoal name="gwt:compile"/>
</preGoal>

License Concerns

How much would you pay for all this? Well, it is free. However, it is not under any OSI license you might
be familiar with. Google has 

terms available

 -- basically you are free to use it, but not redistribute it 

outside your organization. Now, I am no lawyer, but I am seriously troubled by this clause, however:

you may not distribute Google Web Toolkit Development Tools or any services or software 
associated with or derived from them, or modify, copy, license, or create derivative works 
from Google Web Toolkit Development Tools, unless you obtain Google's written 
permission in advance.

One thing that GWT seems built to do is allow people to build new and interesting components. Indeed, 
when I first saw it, it screamed "whole new community" of GWT-based widgets. It would seem to me, 
however, that this is forbidden as a derivative work. It is also worth noting an interesting part of the 
privacy notice on the download page:

Privacy notice: When you use the Google Web Toolkit's hosted web browser, the application 
sends a request back to Google's servers to check to see if you are using the most recent 
version of the product. As a part of this request, Google will log usage data including a 
timestamp of the date and time you downloaded the Google Web Toolkit and the IP address 
for your computer.

Resources

Sample code

 for this article

The Maven plugin

 for use with GWT

The Google-Web-Toolkit

 group on Google Groups

The GWT Team Blog
The GWT Home Page

Robert Cooper

 is an independent Java developer in the Atlanta area, working with J2EE technologies

and web/web service projects.

Return to 

ONJava.com

.

Copyright © 2006 O'Reilly Media, Inc.