Java Development News:

Integrating the Google Web Toolkit with JSF using G4jsf

By Sergey Smirov

01 Aug 2006 | TheServerSide.com

The Google Web Toolkit (GWT) has attracted a lot of attention lately as a way to make it easier for developers to add AJAX Web 2.0 features to their applications. Like other approaches, the designers of GWT have tried to insulate developers from having to deal with the underlying JavaScript, which implements these features. GWT achieves this goal of simplifying the creation of advanced client-side JavaScript widgets by generating them from Java code.

At the same time, JavaServer Faces (JSF) has gathered more and more momentum as a general server-side framework for Web applications. The key to its success has been its top-to-bottom component-based approach to Web development. Although JSF comes with its own standard out-of-the-box UI components, it is designed for the easy inclusion of other component libraries.

While both of these technologies are incredibly beneficial on their own, they also both complement each other extremely well. GWT is server-side-agnostic, while JSF's component-based architecture can easily accommodate any approach for rendering components. In this article, we will discuss (primarily by using a step-by-step example) a new integration library for facilitating the combination of these two complementary technologies.

The G4jsf subproject has been spun off from the Ajax4jsf open source project (https://ajax4sf.dev.java.net) to produce this integration library.

The mission of the G4jsf subproject is to shift the perception of the relationship between GWT and JSF from viewing them as competitive technologies to viewing them as naturally complementary technologies.

About G4jsf

The library contains two parts:

  • The G4jsf Component Developer Kit containing the project skeleton generator.
  • A run-time library that provides the bridge between Google Web Toolkit widgets and the JavaServer Faces environment.

G4jsf respects the development approaches of both JSF and GWT technologies. For GWT, the widgets are still developed in the hosted mode and debugged using the Google browser. The structure of the GWT project containing /client, /public, and /server packages is also maintained.

The components that the G4jsf Component Development Kit (G4jsf CDK) produces have two parts. One part is the GWT widget where the client-side behavior is concentrated. The other part mediates between the GWT widget and the JSF environment.

G4jsf CDK uses the Facelets approach to writing JSF components. It is much simpler than the classic JSP approach, which involves creating a TLD file and a tag class along with a lot of coding. The Facelets approach is much more straightforward.

Example Application

In this article, we will walk step-by-step through a simple "Hello, World" style example that shows the major features of G4jsf. We will also create a more practical example, a4j-gwtdemo. It available online at http://livedemo.exadel.com/a4j-gwtdemo/.

You can download the source code and deployable war files for a4-gwtdemo in the Ajax4jsf examples downloading section at https://ajax4jsf.dev.java.net/nonav/ajax/ajax-jsf/download.html#examples. The final version of the example described in this article is also available there.

Although we use specific tools (mostly Ant) for building the application in this article, this example does not require the use of any particular development tool. You just need to generate a standard Java project. Just pay attention to a very important detail concerning the GWT compiler. It produces the JavaScript part of the component and this part should be in the META-INF/gwt folder (under WEB-INF/classes). The compile.js target of our build script launches the compiler and puts the result into a JavaSource folder. Be sure your tool copies it to the deployable code.

Download and Setup

1. Visit https://ajax4jsf.dev.java.net/nonav/ajax/gwt/gwt-cdk.html to download the G4jsf CDK and save it on your local disk.

To use the G4jsf CDK, you need the Google Web Toolkit SDK. Google does not allow the distribution of the Google Web Toolkit SDK, so you need to get it from Google at http://code.google.com/webtoolkit/download.html.

2. Download the GWT SDK from here and save it on your local disk.

3. Rename the build.properties.sample from the G4jsf CDK files as build.properties and open it for editing.

In this file, you just need to provide the path to the Google Web Toolkit SDK. The other possible options will be commented out. For example, if you have your copy of Google Web Toolkit SDK saved into:
D:/gwt-windows-1.1.0
You would provide the following in the build.properties file:
gwt.home=D:/gwt-windows-1.1.0

4. Edit and save the file.

The initial required steps are now done. The next step is the generation of the project skeleton.

Generating a Project Skeleton

The build script in this same folder has a "create-component" target. It requires that two parameters be provided:

out

The place where the project will be generated into. The last part of the path will be the name of the project.

m odule

The full qualified name of the GWT module. The generated JSF component type will have the same name.

Let's name our module demo.gwt.HelloWidget and store the project with the name, KickStart, into the D:/workspace/ folder. (Of course, you can change any of these.)

5. Launch the Ant script with the following line:

ant -Dout=D:/workspace/KickStart -Dmodule=demo.gwt.HelloWidget create-component

The Ant script should generate the project.

6. Go to the project folder.

"Hello, world!" with GWT

Let's makes our project live. We'll look at it in the standard GWT Hosted Mode and then run it in the next section in a servlet container.

7. Open the JavaSource/demo/gwt/client/HelloWidgetEntryPoint.java file.

It contains a HelloWidgetEntryPoint class that returns null in the generated method stub.

8. Replace it with the following code:

public class HelloWidgetEntryPoint extends ComponentEntryPoint {

 protected Widget createWidget(final String id) {
  
   Button btn = new Button("Click me", new ClickListener() {
        public void onClick(Widget sender) {
         Window.alert("Hello, World");
        }
  });
  
  return btn;
 }
} 

9. Save the file, and go to the "ant" folder, which is where the build script is located.

Hosted Mode

10. Launch the Ant script with the "shell" target:

ant shell

The Google Toolkit Development shell and the Google browser are launched. The browser shows the button we have just created. If you click the button, an alert window with a greeting message appears.

The Google browser contains the "Compile/Browser" button in the tool bar. If you click on it, you can see the result from inside your own default Internet browser.

There is no JSF involved yet. At this point, we're just dealing with "Hosted Mode." You can develop and debug your GWT widget in this mode. If you already have development experience with GWT, you should be a familiar with this. The G4jsf Component Development Kit keeps this part of the process exactly the same.

Run-Time Mode

The next step is to run this example application in the JSF environment.

11. Launch the Ant script with the "war" target:

ant war

The Ant script generates a file, build/KickStart.war. It's a standard JaveEE Web archive.

12. Deploy it under the servlet container you use.

For example, copy KickStart.war into the webapps folder of Tomcat and Run it on server.

If you run the project you will see the same application, but working inside the JSF environment. We'll call it "Run-Time Mode." We have wrapped the GWT widget with the JSF component. This is a first step in integration.

Using More Dynamic Text

In the first version of our application, we hard-coded the label for the button and the greeting message for the window. In the next version, we will keep this text outside of the Java code.

GWT Parameters in Hosted Mode

One approach is to use GWT parameters to define the label and message.

13. Open demo.gwt.client.HelloWidgetEntryPoint.java class and replace the createWidget method with the following code:

protected Widget createWidget(final String id) {
  Map m = getWidgetParams(id);
  final String buttonLabel = (String) m.get("buttonLabel");
  final String greeting = (String) m.get("greeting");

  Button btn = new Button(buttonLabel, new ClickListener() {
  public void onClick(Widget sender) {
         Window.alert(greeting);
        }
  });
  
  return btn;
 }

14. Save the changes.

15. Open the JavaSourcedemogwtpublicindex.html file.

This file already contains an example of how to define the parameters. We just need to replace them with the actual ones.

16. The resulting page should contain the following text:

<html>
 <head>
  <meta name="gwt:module" content="demo.gwt.HelloWidget">
  <meta name="gwt:property" content="viewid=hello">
  <meta name="gwt:property" content="action=/gwtFacesServlet">
  <title>gwt-jsf integration</title>
 </head>
 <body bgcolor="white"> 
  <script language="javascript" src="gwt.js"></script>
 <iframe id="__gwt_historyFrame" 
 style="width:0;height:0;border:0"></iframe>

 <span id="_id1" class="demo.gwt.HelloWidget">
  <span id="_id1:_data" style="display:none;">
   <span  title="buttonLabel">Say Hello</span>
<span  title="greeting">Hello GWT!</span>
  </span>
  <input type="hidden" id="javax.faces.ViewState" 
  name="javax.faces.ViewState" value="_id0:_id0" />
 </span>

 </body>
</html>

17. Launch the application in Hosted Mode with:

ant shell

You can now see the text defined in the public/index.html file:

However, if you try to create a war file and deploy it right now, you will not see the same result. This is because the public/index.html file only makes sense in the Hosted Mode. In the Run-Time Mode, we need to use a JSF page.

Using JSF Expression Language (EL) in Run-Time Mode

Take a look at WebContentpagesBase.xhtml. It contains:

<widget:component id="main"  />

This is actually a JSF component wrapper for a GWT widget.

18. Define the attributes "buttonLabel" and "greeting" for the component:

<widget:component id="main"  buttonLabel ="Click Me!" 
greeting="Hello, GWT and JSF"/>

19. Run the project in Run-Time Mode.

See the results. However, this was too easy. Let's use JSF EL to make it more dynamic.

20. Close the XHTML file for now.

Adding a Resource File

21. Create bundle demo/gwt/app/bundle/Labels.properties under the JavaSource folder with the following context:

#
buttonLabel=Say Hello!

Adding a Managed Bean

22. Create new class demo.gwt.app.GreetingBean under the JavaSource with the following context:

package demo.gwt.app;

public class GreetingBean {
 String name;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

23. Register this bean into WEB-INF/faces-context.xml file under WebContext folder.

The faces-context.xml should contain:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.
//DTD JavaServer Faces Config 1.1//EN"
                              "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>

 <managed-bean>
  <managed-bean-name>greetingBean</managed-bean-name>
  <managed-bean-class>demo.gwt.app.GreetingBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
  <managed-property>
   <property-name>name</property-name>
   <property-class>java.lang.String</property-class>
   <value>GWT and JSF</value>
  </managed-property>
 </managed-bean>


  <application>
  <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
 </application>
</faces-config>

Finishing Up

To make EL working, we need to add attributes to the attribute map in the component class.

24. Open demo.gwt.jsf.UIHelloWidget, find getWidgetParameters() method and replace.

public Map getWidgetParameters() {
  HashMap params = new HashMap();
  params.put("greeting",getAttributes().get("greeting"));
  params.put("buttonLabel",getAttributes().get("buttonLabel"));
  return params;
 }

25. Also add the following import to use a Hash Map in Java Code below:

import java.util.HashMap;

Now, let's return back to the pages/Base.xhtml file.

26. Put the load declaration for the resource file at the top of the page:

<f:loadBundle basename="demo.gwt.app.bundle.Labels" var="bundle"/>

27. Redo the attributes for widget:component:

<widget:component id="main" buttonLabel ="#{bundle.buttonLabel}"
 greeting="Hello #{greetingBean.name}!" />

Now, both attributes values are generated by JSF EL: one from the bundle resource file, another from the backing bean.

If you create, deploy, and launch the application, you can see that the application now uses the button label:

Well, we parameterized the GWT widget with the data from the JSF environment. So, you are able to initialize the widget with the data the same way as you do in a pure JSF application. However, so far it all looks like a one-way ticket. One very important piece is still missing.

AJAX, AJAX, AJAX

If you have heard about GWT, you have definitely heard that AJAX support is a key feature of it. In GWT, however, the "X" part, for transferring data back and forth from client to server, is provided through an RPC (Remote Procedure Call) mechanism, instead of through the XMLHttpRequest object.

Beyond this, what you want to do on the server side is up to you. Google labels this as being "Server Agnostic".

Let's look at adding AJAX style asynchronous communication to our application.

Asynchronous Communication in Hosted Mode

In this next phase, we will modify the scenario of the application a little bit. We will add a text field where the user can enter a name. Once the button is clicked, the request comes to the server. The server returns the greeting message that is shown in the popup window. In order to trace the request and response, we will add a text label that will show the status of the request, loaded or loading.

In order to transfer data between the client and the server, we will use two Java beans to bring data back and forth – one to bring input data to the server, another to bring the response back to the client.

Client-Side C oding

28. Create a demo.gwt.client.EventData Java class under JavaSource folder with the following content:

package demo.gwt.client;

import org.ajax4jsf.gwt.client.GwtFacesEvent;

public class EventData extends GwtFacesEvent {
 String name;

public String getName() {
 return name;
}

public void setName(String name) {
 this.name = name;
}
 
}

The bean bringing the event from the client to the server should extend GwtFacesEvent from G4jsf.

29. Create another Java class with the name demo.gwt.client.ResultGreeting.

It should have the following content:

package demo.gwt.client;

import org.ajax4jsf.gwt.client.GwtFacesResult;

public class ResultGreeting extends GwtFacesResult {
 String greetingText;

 public String getGreetingText() {
  return this.greetingText;
 }

 public void setGreetingText(String greetingText) {
  this.greetingText = greetingText;
 }
}

The bean bringing response from server to the client should extend GwtFacesResult from G4jsf.

Both, GwtFacesEvent and GwtFacesResult implement the com.google.gwt.user.client.rpc.IsSerializable interface that enables the data marshaling between client and server that GWT uses.

Let's update our widget class.

30. Open the demo.gwt.client.HelloWidgetEntryPoint class and replace its content with the following:

package demo.gwt.client;

import java.util.Map;

import org.ajax4jsf.gwt.client.ComponentEntryPoint;
import org.ajax4jsf.gwt.client.GwtFacesServiceAsync;

import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class HelloWidgetEntryPoint extends ComponentEntryPoint {
Label status;
TextBox input;
 protected Widget createWidget(final String id) {

  Map m = getWidgetParams(id);
  final String buttonLabel = (String) m.get("buttonLabel");
  

  HorizontalPanel panel = new HorizontalPanel();
  

  input = new TextBox();
  status = new Label("Loaded.");

  final GwtFacesServiceAsync service = createFacesService(id);

  final AsyncCallback callback = new AsyncCallback() {
   public void onSuccess(Object result) {
    if (null != result) {
     status.setText("Loaded");
     String greeting =  ((ResultGreeting)result).getGreetingText();
     Window.alert(greeting);
    } else {
     status.setText("Request finished, but the result is empty");
     }
   }
   
   public void onFailure(Throwable caught) {
    status.setText("Error call :" + caught.getMessage());
   }
  };
Button btn = new Button(buttonLabel, new ClickListener() {
  public void onClick(Widget sender) {
   EventData eventData = new EventData();
   eventData.setName(input.getText());
   service.sendEvent(eventData, callback);
   status.setText("Loading...");      
        }
  });
  

  panel.add(input);
  panel.add(btn);
  panel.add(status);
  return panel;
 }
}

There are three important parts here. First is the layout of the component. We created a horizontal panel and added an input box, button, and text label for it.

The second is the declaration of the asynchronous service and event that initiates the AJAX request in asynchronous mode:

...
final GwtFacesServiceAsync service = createFacesService(id);
...
Button btn = new Button(buttonLabel, new ClickListener() {
 public void onClick(Widget sender) {
  EventData eventData = new EventData();
  eventData.setName(input.getText());
  service.sendEvent(eventData, callback);
  status.setText("Loading...");      
       }
});
...

We added ClickListener to the button. When the On Click event occurs, we create and fill the EventData bean with the entered data and send the Event using an asynchronous service. With last line of the code, we set the text of the text label to "Loading…", so the user can see the moment when the AJAX request is started.

Sending the event, we register the callback method included in the third, corresponding, part of our code:

.........
.........
final AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
  if (null != result) {
   status.setText("Loaded");
   String greeting =  ((ResultGreeting)result).getGreetingText();
   Window.alert(greeting);
  } else {
   status.setText("Request finished, but the result is empty");
   }
 }
   
 public void onFailure(Throwable caught) {
  status.setText("Error call :" + caught.getMessage());
 }
};
...........
...........

Callback is an instance of AsyncCallback class of the Google toolkit. We implemented two methods, onSucess and onFailure. In case of onSuccess, we make an additional check on the upcoming result. If we do not receive an instance of the expected class, we note it in the status text.

After this, we are done with the client-side code. If you try to launch the application in Hosted Mode (with the "ant shell" command), you will see "Request finished, but the result is empty", because we haven't done anything on the server side yet.

Server-Side Coding

On the server side in the Hosted Mode, a class in the "server" package plays the necessary role.

31. Open the demo.gwt.server.MockHelloWidget java class under the JavaSource folder.

The sendEvent method is responsible for sending the response back to the client.

32. Replace it with the following text:

public GwtFacesResult sendEvent(GwtFacesEvent event){
 
        ResultGreeting result = new ResultGreeting();
        result.setGreetingText( "Hello " + 
  ((EventData)event).getName() );
               return result;
 }

33. Do not forget to include the necessary imports in this file:

import demo.gwt.client.ResultGreeting;
import demo.gwt.client.EventData;

The parameter of the method points to the event content that came from the client. What we do here is create the Result bean filling it with a greeting message and then returning.

34. Launch the application in the Hosted Mode with:

ant shell

Now, the application shows the greeting message based on a typed name:

Adding JSF Listeners for Run-Time Mode

The handling of events on the JSF side is very similar to what we did in the Hosted Mode. The client-side widget code stays the same. On the server side, G4jsf uses JSF listener mechanics to handle Ajax events.

35. Open the WebContent/pages/Base.xhtml file and add this listener to the component declaration as a child element:

<widget:component id="main" buttonLabel="#{bundle.buttonLabel}"
   greeting="Hello #{greetingBean.name}!" >
   <gwt:gwtListener method="#{greetingBean.takeGreeting}" 
   event ="demo.gwt.client.EventData"/>
</widget:component>

The gwtListener element has two key attributes. The attribute "method" points to handler using the JSF EL. The "event" attribute defines the type of the event. The type is the qualified name of the event class.

The last step is implementing the method itself.

36. Open demo.gwt.app.GreetingBean java file and add the following method there:

public ResultGreeting takeGreeting(EventData event) {
  name = event.getName();
  ResultGreeting result = new ResultGreeting();
  result.setGreetingText("Hello " + name + " from JSF!");
  return result;
}

37. And also add these imports:

import demo.gwt.client.ResultGreeting;
import demo.gwt.client.EventData;

The signature of this method is very easy to memorize. The method gets the type of the event that comes from the client as its only parameter. The return type of the method equals is just the type of the class used to return the result. In this method, we form the response data and return it.

38. Create the war file and deploy it.

If you launch the application, you will see the following:

The Last Word

As you can see, great things can be done by combining these two complementary technologies, GWT and JSF, together through the G4jsf integration library, but there is always more that can be done to improve G4jsf. As an open source project, G4jsf relies heavily on a supportive community to provide feedback and development. If all just use this library for your applications, that's fine, but you can also join the G4jsf community to help make G4jsf even better. Come visit us at:

https://ajax4jsf.dev.java.net

About the Author

Sergey Smirnov is Senior Product Manager at Exadel where he oversees the development of major products including Exadel Visual Components Platform and Exadel Studio. He has more than 15 years of in-depth development experience, primarily in the area of Web applications. His experience with JSF goes back to the very early days. For two years, he has served as a member of the JSF expert group. In addition to this, he manages a site for JSF-related resources, www.jsftutorials.net. Finally, Sergey is the co-lead of the open source project Ajax4jsf (https://ajax4jsf.dev.java.net). He can be reached at ssmirnov@exadel.com.