Chapter 22. Seam and the Google Web Toolkit

For those that prefer to use the Google Web Toolkit (GWT) to develop dynamic AJAX applications, Seam provides an integration layer that allows GWT widgets to interact directly with Seam components.

To use GWT, we assume that you are already familiar with the GWT tools - more information can be found at http://code.google.com/webtoolkit/. This chapter does not attempt to explain how GWT works or how to use it.

22.1. Configuration

There is no special configuration required to use GWT in a Seam application, however the Seam resource servlet must be installed. See Chapter 25, Configuring Seam and packaging Seam applications for details.

22.2. Preparing your component

The first step in preparing a Seam component to be called via GWT, is to create both synchronous and asynchronous service interfaces for the methods you wish to call. Both of these interfaces should extend the GWT interface com.google.gwt.user.client.rpc.RemoteService:

  public interface MyService extends RemoteService
  {
    public String askIt(String question);      
  }

The asynchronous interface should be identical, except that it also contains an additional AsyncCallback parameter for each of the methods it declares:

  public interface MyServiceAsync extends RemoteService 
  {
    public void askIt(String question, AsyncCallback callback);
  }

The asynchronous interface, in this example MyServiceAsync, will be implemented by GWT and should never be implemented directly.

The next step, is to create a Seam component that implements the synchronous interface:

  @Name("org.jboss.seam.example.remoting.gwt.client.MyService")
  public class ServiceImpl implements MyService
  {
    @WebRemote
    public String askIt(String question)
    {
      if (!validate(question)) 
      {
        throw new IllegalStateException("Hey, this shouldn't happen, I checked on the client, " +
               "but its always good to double check.");
      }
      return "42. Its the real question that you seek now.";
    }
   
    public boolean validate(String q) 
    {
      ValidationUtility util = new ValidationUtility();
      return util.isValid(q);
    }
  }

The methods that should be made accessible via GWT need to be annotated with the @WebRemote annotation, which is required for all web-remoteable methods.

22.3. Hooking up a GWT widget to the Seam component

The next step, is to write a method that returns the asynchronous interface to the component. This method can be located inside the widget class, and will be used by the widget to obtain a reference to the asynchronous client stub:

   private MyServiceAsync getService() 
   {       
      String endpointURL = GWT.getModuleBaseURL() + "seam/resource/gwt";      
      
      MyServiceAsync svc = (MyServiceAsync) GWT.create(MyService.class);
      ((ServiceDefTarget) svc).setServiceEntryPoint(endpointURL);
      return svc;     
   }

The final step is to write the widget code that invokes the method on the client stub. The following example creates a simple user interface with a label, text input and a button:

public class AskQuestionWidget extends Composite
{
   private AbsolutePanel panel = new AbsolutePanel();
   
   public AskQuestionWidget() 
   {      
      Label lbl = new Label("OK, what do you want to know?");
      panel.add(lbl);
      final TextBox box = new TextBox();
      box.setText("What is the meaning of life?");
      panel.add(box);
      Button ok = new Button("Ask");
      ok.addClickListener(new ClickListener() 
      {
         public void onClick(Widget w)
         {
            ValidationUtility valid = new ValidationUtility();
            if (!valid.isValid(box.getText())) 
            {
               Window.alert("A question has to end with a '?'");
            } 
            else 
            {
               askServer(box.getText());
            } 
         }
      });
      panel.add(ok);
      
      initWidget(panel);
   }

   private void askServer(String text)
   {
      getService().askIt(text, new AsyncCallback() 
      {
         public void onFailure(Throwable t)
         {
            Window.alert(t.getMessage());
         }

         public void onSuccess(Object data)
         {
            Window.alert((String) data);
         }         
      });      
   }
   
   ...    
    

When clicked, the button invokes the askServer() method passing the contents of the input text (in this example, validation is also performed to ensure that the input is a valid question). The askServer() method acquires a reference to the asynchronous client stub (returned by the getService() method) and invokes the askIt() method. The result (or error message if the call fails) is shown in an alert window.

The complete code for this example can be found in the Seam distribution in the examples/remoting/gwt directory.

22.4. GWT Ant Targets

For deployment of GWT apps, there is a compile-to-Javascript step (which compacts and obfuscates the code). There is an ant utility which can be used instead of the command line or GUI utility that GWT provides. To use this, you will need to have the ant task jar in your ant classpath, as well as GWT downloaded (which you will need for hosted mode anyway).

Then, in your ant file, place (near the top of your ant file):

  <taskdef uri="antlib:de.samaflost.gwttasks"
                resource="de/samaflost/gwttasks/antlib.xml"
                classpath="./lib/gwttasks.jar"/>
   
  <property file="build.properties"/>

Create a build.properties file, which has the contents:

gwt.home=/gwt_home_dir

This of course should point to the directory where GWT is installed. Then to use it, create a target:

  <!-- the following are are handy utilities for doing GWT development.
      To use GWT, you will of course need to download GWT seperately -->
  <target name="gwt-compile">
      <!-- in this case, we are "re homing" the gwt generated stuff, so in this case
      we can only have one GWT module - we are doing this deliberately to keep the URL short -->
      <delete>
          <fileset dir="view"/>
      </delete>
      <gwt:compile outDir="build/gwt"
          gwtHome="${gwt.home}"
          classBase="${gwt.module.name}"
          sourceclasspath="src"/>
      <copy todir="view">
          <fileset dir="build/gwt/${gwt.module.name}"/>
      </copy>
  </target>

This target when called will compile the GWT application, and copy it to the specified directory (which would be in the webapp part of your war - remember GWT generates HTML and Javascript artifacts). You never edit the resulting code that gwt-compile generates - you always edit in the GWT source directory.

Remember that GWT comes with a hosted mode browser - you should be using that if you are developing with GWT. If you aren't using that, and are just compiling it each time, you aren't getting the most out of the toolkit (in fact, if you can't or won't use the hosted mode browser, I would go far as to say you should NOT be using GWT at all - it's that valuable!).