Introducing the Google Web Toolkit

Introducing the Google Web Toolkit

By Brad Busch, OCI Software Engineer

October 2006


Overview of GWT 1.1.0

The Google Web Toolkit (GWT) is a unique entry among AJAX toolkits in that it is built for Java developers who may not know the latest web coding standards, but want to harness the power of AJAX.  Until now, AJAX toolkits or frameworks have consisted of Javascript widget libraries and implementing them involved detailed knowledge of Javascript, HTML and CSS.  With GWT's Java-centric approach, much of the pain has been removed with Swing-like creation of the front-end code, real debugging from within your favorite IDE and guaranteed cross-browser support.

Getting started with GWT

The GWT download comes with two scripts: projectCreator and applicationCreator.  

The projectCreator script can be thought of as  an Eclipse project creator as it is used to create an application directory, Eclipse classpath and project files.  All output is highlighted.

  1. ${path_to_gwt}/projectCreator -eclipse SimpleHelloWorld -out SimpleHelloWorld
  2. Created directory SimpleHelloWorld/src
  3. Created directory SimpleHelloWorld/test
  4. Created file SimpleHelloWorld/.project
  5. Created file SimpleHelloWorld/.classpath

The applicationCreator script generates a src/ directory, a moduleName-compile script and an moduleName-shell script.  I use the convention of 'moduleName' here to denote that GWT refers to an  application as a module.  The src/ directory is comprised of the package name given during creation, some stubbed out classes and an XML file in the form of moduleName.gwt.xml.  This XML file marks the entry point to the application.  The compile script sends all class files and generated JavaScript to the www/ directory.  The shell script is used for debugging purposes and launches a GWT embedded browser that is actually executing Java bytecode — this is generally known as Hosted mode.  Support for hosted mode is currently only available for Windows and Linux users; however, there are rumors of an OS X version on the horizon.

The output from using the applicationCreator script looks like this:

  1. ${path_to_gwt}/applicationCreator -out SimpleHelloWorld
  2. com.ociweb.client.SimpleHelloWorld
  3. Created directory SimpleHelloWorld/src
  4. Created directory SimpleHelloWorld/src/com/ociweb
  5. Created directory SimpleHelloWorld/src/com/ociweb/client
  6. Created directory SimpleHelloWorld/src/com/ociweb/public
  7. Created file SimpleHelloWorld/src/com/ociweb/SimpleHelloWorld.gwt.xml
  8. Created file
  9. SimpleHelloWorld/src/com/ociweb/public/SimpleHelloWorld.html
  10. Created file
  11. SimpleHelloWorld/src/com/ociweb/client/SimpleHelloWorld.java
  12. Created file SimpleHelloWorld/SimpleHelloWorld-shell
  13. Created file SimpleHelloWorld/SimpleHelloWorld-compile

Modules

Modules are the configuration files within the GWT framework that define the entry point to the application.  A single GWT module consists of an XML file that binds the necessary files for a given GWT function.  Module definitions are flexible; they allow for inheritance,  Javascript and CSS injection, package filtering, resource path configuration and multiple modules - as long as they are compiled separately.

SimpleHelloWorld

Below is the generated SimpleHelloWorld.gwt.xml file.  By default, modules denote one entry point and inherit from the User module.  

  1. <module>
  2. <!-- Inherit the core Web Toolkit stuff. -->
  3. <inherits name='com.google.gwt.user.User'></inherits>
  4.  
  5. <!-- Specify the app entry point class. -->
  6. <entry-point class='com.ociweb.client.SimpleHelloWorld'></entry>
  7. </module>

Here is the client java class (SimpleHelloWorld.java) that will be converted into Javascript.   While this code is a bit trivial, it demonstrates some basic concepts of GWT GUI building.  See the comments inline with the source.

  1. package com.ociweb.client;
  2.  
  3.  
  4. import com.google.gwt.core.client.EntryPoint;
  5. import com.google.gwt.user.client.ui.Button;
  6. import com.google.gwt.user.client.ui.ClickListener;
  7. import com.google.gwt.user.client.ui.Label;
  8. import com.google.gwt.user.client.ui.RootPanel;
  9. import com.google.gwt.user.client.ui.Widget;
  10.  
  11.  
  12. /**
  13.  * Entry point classes define <code>onModuleLoad()</code>.
  14.  */
  15. public class SimpleHelloWorld implements EntryPoint {
  16.  
  17.  
  18. /**
  19.  * This is the entry point method and MUST be implemented.
  20.  */
  21. public void onModuleLoad() {
  22.  
  23. final Button button = new Button("Click me");
  24. final Label label = new Label();
  25.  
  26.  
  27. button.addClickListener(new ClickListener() {
  28.  
  29. public void onClick(Widget sender) {
  30. if (label.getText().equals(""))
  31. label.setText("Hello World!");
  32. else
  33. label.setText("");
  34. }
  35. });
  36.  
  37.  
  38. /*
  39.  Assume that the host HTML has elements defined whose
  40.  IDs are "slot1", "slot2". In a real app, you probably would not
  41.  want to hard-code IDs. Instead, you could, for example, search for
  42.  all elements with a particular CSS class and replace them with widgets.
  43.  */
  44. RootPanel.get("slot1").add(button);
  45. RootPanel.get("slot2").add(label);
  46. }
  47. }

One of the key benefits of the GWT is debugging a user interface from within your IDE.  To launch a debugging session in eclipse follow these  steps: 

  1. Open Debug... Add a Java Application configuration. 
  2. Select the Main tab, set your Main class to: com.google.gwt.dev.GWTShell.
  3. Select the Classpath tab, click Advanced... Add Folders... select your source directory (this ensures the module is on the classpath)

GWT's embedded browser:

GWT's Embedded Browser

Debugging client side code from my favorite IDE:

Debugging Client Side from IDE

Creating Services

Loosely speaking, the GWT considers a service to be any server-side code that is invoked from a client.  The GWT provides an RPC mechanism for asynchronous calls removing the need for direct access to the XMLHttpRequest Object, although access to this object is obtainable if desired.  GWT accomplishes this through the use of a configuration file (moduleName.gwt.xml),  two interfaces per service (synchronous and asynchronous) and a server-side implementation of the synchronous interface.  

A simple example using a service

The user types in the top text box and the output from the service call is displayed in the textbox using blue characters - all without refreshing the page.

CopyText


The directory structure for the service components looks like this:

  1. com.ociweb.CopyText.gwt.xml
  2. com.ociweb.client.CopyText.java
  3. com.ociweb.client.CopyTextAsync.java
  4. com.ociweb.server.CopyTextImpl.java

CopyText.gwt.xml:

  1. <module>
  2. <!-- Inherit the core Web Toolkit stuff -->
  3. <inherits name='com.google.gwt.user.User'></inherits>
  4.  
  5. <!-- Specify the application entry point class. -->
  6. <entry-point class='com.ociweb.client.CopyText'></entry>
  7.  
  8. <!-- The servlet implementation to map our service to. -->
  9. <servlet path='/copyTextService'
  10. class='com.ociweb.server.CopyTextServiceImpl'></servlet>
  11. </module>

For the RPC call to work, a few GWT constructs must be satisfied.

Synchronous interface — implemented by our service

  1. package com.ociweb.client;
  2.  
  3. import com.google.gwt.user.client.rpc.RemoteService;
  4.  
  5. public interface CopyTextService extends RemoteService {
  6. public String copyText(String s);
  7. }

Asynchronous interface — method signature similar to synchronous with additional 
    method parameter of type AsyncCallback and a return type of void

  1. package com.ociweb.client;
  2.  
  3. import com.google.gwt.user.client.rpc.AsyncCallback;
  4.  
  5. public interface CopyTextServiceAsync {
  6. public void copyText(String s, AsyncCallback callback);
  7. }

CopyTextServiceImpl is the service implementation.  The RemoteServiceServlet is the base class for RPC service implementations and controls serialization of requests and responses.  This implementation class returns the input string typed by the user.

  1. package com.ociweb.server;
  2.  
  3. import com.google.gwt.user.client.rpc.IsSerializable;
  4. import com.google.gwt.user.server.rpc.RemoteServiceServlet;
  5. import com.ociweb.client.CopyTextService;
  6.  
  7. public class CopyTextServiceImpl extends RemoteServiceServlet
  8. implements CopyTextService, IsSerializable {
  9.  
  10. private static final long serialVersionUID = 1L;
  11.  
  12. public String copyText(String s) {
  13. System.out.println("Text received:"+s);
  14. return s;
  15. }
  16. }

CopyText - This is the client code that gets converted into Javascript and hosts the call to the services as well as creating the user interface

package com.ociweb.client;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
 
    /*
     * Entry point classes define <code>onModuleLoad()</code>.
     */
 
public class CopyText implements EntryPoint {
    VerticalPanel vPanel;
    TextBox textResult;
 
    /*
     * This is the entry point method.
     */
    public void onModuleLoad() {
        final TextBox text = new TextBox();
        final Label label = new Label("Type text here:");
        textResult = new TextBox();
        //creating Async object using GWT.create() for callback
        final CopyTextServiceAsync copyTextService = (CopyTextServiceAsync)
                GWT.create(CopyTextService.class);
 
        //service entry entry point, which is defined in moduleName.gwt.xml
        ServiceDefTarget serviceTarget = (ServiceDefTarget) copyTextService;
        String myUrl = GWT.getModuleBaseURL()+"/copyTextService";
        serviceTarget.setServiceEntryPoint(myUrl);
 
        vPanel = new VerticalPanel();
        vPanel.add(label);
        vPanel.add(text);
 
        text.addKeyboardListener(new KeyboardListener() {
            public void onKeyDown(Widget sender, char keyCode, int modifiers) {
            }
 
            public void onKeyPress(Widget sender, char keyCode, int modifiers) {
            }
 
            public void onKeyUp(Widget sender, char keyCode, int modifiers) {
                copyTextService.copyText(((TextBox)sender).getText(),
                        getCopyTextServiceCallback());
            }
        });
 
        RootPanel.get("slot1").add(vPanel);
    }
 
 
    /* AsyncCallback is the primary interface that a user must
    * implement to receive a response from the remote procedure call.
    */
    public AsyncCallback getCopyTextServiceCallback() {
        return new AsyncCallback() {
            public void onFailure(Throwable ex) {
                RootPanel.get().add(new HTML(ex.toString()));
                Window.alert("The call failed.");
            }
 
            public void onSuccess(Object result) {
                //Added to support Internet Explorer and Mozilla -
                //Safari Worked fine without removing result first
                vPanel.remove(textResult);
 
                //call to external CSS file
                textResult.addStyleName("gwt-TextBox-blue");
                textResult.setText(result.toString());
                vPanel.add(textResult);
            }
        };
 
    }
}

Summary

The GWT has many benefits other than those previously mentioned, such as: JUnit integration, browser history management, the ability to plugin in custom Javascript (using the Javascript Native Interface), and of course a robust widget library to choose from when building a composite user interface.  For these reasons, as well as, the RPC mechanism and code generation, GWT is worth a serious look when developing page-centric web applications such as Google does with its varied web applications.   

There are however some problematic areas that any serious implementers should be aware of.  The main issue is the non-open source licensing agreement.  As a result, any bugs or featured enhancements will have to be fixed by an official GWT release.  It's not hard to imagine how this could be a problem.  A related issue is backward compatibility.  As I've discovered over the course of writing this article, there are no guarantees that future releases will not break current code, nor is there much in the way of documentation letting you know what will break.  This brings me to one final issue - the lack of official documentation. While there is some documentation available, it is largely scattered and lacking examples.  To remedy this, I found the GWT user group very useful and to that end, the GWT developers are responsive to most if not all issues.

References